Unlocking Brand Consistency: Design Tokens & Tailwind CSS

Meiyappan Kannappa
6 min readNov 16, 2024

--

Design tokens are the smallest, repeatable elements of design system, which stores visual properties like colors, typography, font, sizes, etc. These are the backbones of the design system. Tokens are like nicknames for design elements, instead of hard coding the color hex codes and sizes everywhere, names are assigned to use across. This can be used in all styles within the web, mobile applications.

Why do we need Design Tokens?

Design and UX are key elements of brand consistency, as these are the components which consumers see and engage with the brand products or service.
From logo usage to graphics and icons, UI/UX design combines critical elements throughout the interface to maintain brand consistency.

Design tokens are fundamentally linked to brand consistency. They act as the bridge between design and development, ensuring a unified and consistent brand experience across all platforms and touchpoints by

  1. Providing centralized definition of brand elements
  2. Maintaining brand identity across all platforms
  3. Streamlining design and development workflow
  4. Scalability and Reusability

How to use design tokens in your web development

Consider you are building a frontend platform for a premium organization selling various financial services like credit cards, personal loans, micro finance, etc. To enable trust one of the key factor is Brand Consistency, by enabling the consistent visual appeal to the consumers.

Assume NextJs and ReactJs are the primary tech stack to build this Frontend Web Platform, along with Tailwind as CSS framework. Given the wide range of service offerings, the platform is built as composable Microfrontends. Now how do we ensure design consistency?.

Business & marketing creates UI/UX design based on design elements to make the experience consistent across. The generated design tokens has to be imported in your web application as styles to the UI elements.

Most of the CSS frameworks support importing and using these design tokens, in this blog we have chosen Tailwind as the CSS framework for our virtual use case. Simple flow of how to get and use the design token in our web application is as below. Most of the UX designing tools including sketch and figma allows to create design elements and generate design tokens to share to the development teams.

A sample design token file in json format

{
"themes": {
"default": {
"colors": {
"exbgcolor": "#2d3748",
"exformbgcolor": "#e2e8f0",
"exheadercolor":"#8B4600",
"textColor": "#112233",
"blue500": "#3b82f6",
"blue700": "#2563eb",
"ivory": "#f0f0f0",
"exformcolor":"#000000",
"exfootercolor":"#000000",
"formheading":"#8B0000"
},
"fontFamily": {
"sans": "Helvetica, Arial, sans-serif"
},
"fontSize": {
"5xl": "96px",
"vl": "1.25rem"
},
"spacing": {
"4": "1px",
"8": "2px"
},
"shadows": {
"md": "0 4px 8px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)"
}
},
"greenTheme": {
"colors": {
"exbgcolor": "#B5D0FC",
"exformbgcolor": "#90EE90",
"exheadercolor":"#468B00",
"exfootercolor":"#000000",
"textColor": "#ffeebb",
"blue500": "#000000",
"blue700": "#000000",
"ivory": "#ffffff",
"exformcolor":"#00008b",
"formheading":"#8B008B"
},
"fontFamily": {
"sans": "math"
},
"fontSize": {
"5xl": "48px",
"vl": "3.25rem"
},
"spacing": {
"4": "40px",
"8": "80px",
"customspacing":"20px"
},
"padding":{
"8":"20px"
},
"shadows": {
"md": "0 4px 6px -1px rgba(f, f, f, 0.5), 0 2px 4px -1px rgba(0, 0, 0, 0.06)"
}
}
}
}

Based on the structure of design token json file, we need to write a custom JSON parser, which can parse to extract the various themes and design styles from the Json.

const fs = require('fs');

const designTokens = JSON.parse(fs.readFileSync('./designTokens.json', 'utf8'));

const tailwindConfig = {

plugins: [
require('tailwindcss-themer')({
defaultTheme: {
name: 'style-1',
extend: {
colors: designTokens.themes.default.colors,
fontFamily: designTokens.themes.default.fontFamily,
fontSize: designTokens.themes.default.fontSize,
spacing: designTokens.themes.default.spacing,
boxShadow: designTokens.themes.default.shadows
},
},
}),
],
};

fs.writeFileSync('./tailwind.config.ts', `module.exports = ${JSON.stringify(tailwindConfig, null, 2)}`);
console.log('tailwind.config.js generated successfully!',JSON.stringify(tailwindConfig));

Since we have a very simple design system in this example for demonstration, we can skip the custom token parser and have the logic in tailwind config.js to get values from the designtokens.json file.

import fs from 'fs';
import { join } from 'path';

const designTokens = JSON.parse(fs.readFileSync('./designTokens.json', 'utf8'));
const config = {
content: [
join(__dirname, '{src,pages,components,app}/**/*!(*.stories|*.spec).{ts,tsx,html}'),
],
plugins: [
require('tailwindcss-themer')({
defaultTheme: {
name: 'org-style1',
extend: {
colors: designTokens.themes.default.colors,
fontFamily: designTokens.themes.default.fontFamily,
fontSize: designTokens.themes.default.fontSize,
spacing: designTokens.themes.default.spacing,
boxShadow: designTokens.themes.default.shadows
},
},
themes: [
{
name:'org-style2',
extend: {
colors: designTokens.themes.greenTheme.colors,
fontFamily: designTokens.themes.greenTheme.fontFamily,
fontSize: designTokens.themes.greenTheme.fontSize,
spacing: designTokens.themes.greenTheme.spacing,
boxShadow: designTokens.themes.greenTheme.shadows
}
}
],
}),
],
};
console.log(JSON.stringify(designTokens.themes.greenTheme.fontSize))
module.exports = config;

The above config will inject the values from the design system, and have created two themes based on it. Each theme has colors, fontfamily, fontsize, spacing and boxshadow attributes to be used across the application. This can even be overidden based on product needs. Internally this uses tailwindcss-themer plugin, which supports to configure and switch between multiple themes or you can create custom plugin to do the same.

Once the themes and design are configured , to apply it to the nextjs web app modify the layout.js file to have a className element to pass the theme name as in below code. You can customize it further to switch the themes, based on the URL or query param in the layout.js file.

//layout.js
import "./globals.css";

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" className="org-style1">
<head>
</head>
<body>
{children}
</body>
</html>
);
}
//page.js
import Header from '@/components/header';
import Footer from '@/components/footer';
import Button from '@/components/button';

export default function Home() {

return (
<div className="font-sans min-h-screen flex flex-col">
<Header />

<main className="font-sans bg-exformbgcolor flex-grow container mx-auto p-8">
<div className="font-sans bg-ivory p-8 rounded-lg shadow-md max-w-md mx-auto">
<p className='font-sans text-vl text-formheading mb-4 '>Contact Us</p>
<form>
<div className="mb-4 space-customspacing">
<label htmlFor="name" className="block text-exformcolor font-bold mb-2">Name</label>
<input type="text" id="name" name="name" className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500" required />
</div>
<div className="mb-4 space-customspacing">
<label htmlFor="email" className="block text-exformcolor font-bold mb-2">Email</label>
<input type="email" id="email" name="email" className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500" required />
</div>
<div className="mb-4 space-4">
<label htmlFor="phone" className="block text-exformcolor font-bold mb-2">Phone</label>
<input type="tel" id="phone" name="phone" className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500" required />
</div>
<div className="mb-4 space-4">
<Button labelText="Submit"/>
</div>
</form>
</div>
</main>

<Footer />
</div>
);
}
//button.js
export default function Button(props) {
return (
<button type="submit" className="bg-blue500 hover:bg-blue700 text-white font-bold py-2 px-4 rounded-md focus:outline-none focus:shadow-outline">
{props.labelText}
</button>
);
}
//footer.js
export default function Footer() {
return (
<footer className="bg-exfootercolor text-white p-4 mt-auto">
<div className="container mx-auto text-center">
&copy; {new Date().getFullYear()} My Website. All rights reserved.
</div>
</footer>
);
}
//header.js
import Link from 'next/link';

export default function Header() {
return (
<header className="bg-exheadercolor p-4 shadow-md h-50">
<div className="bg-exheadercolor container mx-auto flex justify-between items-center p-5 m-5 h-50">
<Link href="/" className="text-blue500 text-2xl font-bold m-5">My Website</Link>
<nav>
<div>
<ul className="flex space-x-6">
<li><Link href="/about" className="hover:underline ml-10">About</Link></li>
<li><Link href="/contact" className="hover:underlinevml-10">Contact</Link></li>
</ul>
</div>
</nav>
</div>
</header>
);
}

Video of Working example

Atomic Design and Design tokens

Atomic design is atoms, molecules, organisms, templates, and pages concurrently working together to create effective interface design systems while build software applications, Where in Design tokens are a set of variables that define design properties such as colors, typography, spacing, etc., in a design system which can be used while developing ATOMS.

Atom designs is a way to develop your website as a composable components which can be shared across multiple applications.

Component sharing is an interesting concept in the recent days of Microfrontend buzz. Be it the way of sharing components through Package Manager like npm or with Monorepos like nx, bazel the above explained approach of creating a design system, generation of design tokens and applying via tailwind config is more efficient way. Which can handle multiple themes from various design tokens, and apply to the unstyled components.

Final Thoughts

Design tokens, Tailwind CSS, and atomic design: a powerful combination for building consistent, scalable, and maintainable design systems. By centralizing your brand’s visual language, you empower your team to create exceptional user experiences while streamlining the development process. The result? A stronger brand and a more efficient workflow

--

--

Meiyappan Kannappa
Meiyappan Kannappa

Written by Meiyappan Kannappa

Technical enthusiast, daydreamer. Design of involute software architecture for digital transformation, sustainability and cost optimization.

Responses (2)