Design System Foundations Using Tailwind

The foundation part of the design system comprises all basic styling necessities for a project.

We will go through them one by one and create them using tailwind css.


We can either extend or completely reset the colors. Let's reset the colors. The brand Color of is #007dfc . HSL is easy to understand. The brand color translates to ‘hsl(210, 100%, 49%)’ in HSL.

  • Hue: 210
  • Saturation: 100%
  • Lightness: 49%

HSL is great for creating shades for a particular hue.

We can have the hue 210 and change the lightness to create the shades.

theme: {
colors: {
primary: {
50: 'hsl(210, 100%, 97%)',
100: 'hsl(210, 100%, 91%)',
200: 'hsl(210, 100%, 80%)',
300: 'hsl(210, 100%, 70%)',
400: 'hsl(210, 100%, 60%)',
500: 'hsl(210, 100%, 49%)',
600: 'hsl(210, 100%, 39%)',
700: 'hsl(210, 100%, 28%)',
800: 'hsl(210, 100%, 16%)',
900: 'hsl(210, 100%, 10%)',
extend: {},

The range of lightness I came up with is purely using the eyes. We can simply divide them uniformly but I wanted the primary-50 to be almost white and primary-900 to be almost black.

It is gray. But “primarily” tinted!

The primary color is for cueing the user about the most important thing to notice on a page. But gray is the most used color in any application.

A plain gray won’t break the user experience but if we tint it with the primary color, it gives a good mood.

Using HSL format helps us easily come up with related colors with great precision.

Gray can be achieved by reducing the saturation to 0% at any hue. But we generate our gray like below to match our primary color.

  • Retain Hue as 210.
  • Reduce saturation from 100% to around 12%.
gray: {
50: 'hsl(210, 12%, 97%)',
100: 'hsl(210, 12%, 91%)',
200: 'hsl(210, 12%, 80%)',
300: 'hsl(210, 12%, 70%)',
400: 'hsl(210, 12%, 60%)',
500: 'hsl(210, 12%, 49%)',
600: 'hsl(210, 12%, 39%)',
700: 'hsl(210, 12%, 28%)',
800: 'hsl(210, 12%, 16%)',
900: 'hsl(210, 12%, 10%)',

How does our tinted GRAY (12% saturated) look compared to the GRAY-PLAIN (0% saturated)?

You can increase or decrease the 12% which I found to be a sweet spot to tint.

Imagine doing this in HEX code? HSL makes it simple to masterfully come up with colors.

Let's use the same technique to create two more colors.

  • Red: #f73131 or hsl(0, 93%, 58%)
  • Green: #00CC66 or hsl(150, 100%, 40%)

That's it for the colors. We can revisit later if we want more colors or modify the shades as needed. This tailwind configuration we create here will be propagated throughout the project instantly.


  • We have completely replaced the colors that come with tailwind with these four colors.


Font size

We reset the tailwind’s type scale with the below.

fontSize: {
// Body copy
'xs': ['.75rem', '1rem'],
'sm': ['.875rem', '1rem'],
'base': ['1rem', '1.5rem'],
'lg': ['1.125rem', '1.5rem'],
// Headings
'xl': ['1.5rem', '2rem'],
'2xl': ['2rem', '2.5rem'],
// Display
'3xl': ['5rem', '6rem'],
'4xl': ['10rem', '10rem'],

We can not discuss font size without line-height. The second item in each array is line-height.

A thing to notice is, we try to achieve an 8px grid as much as possible. All the line heights above are divisible by 8 and hence they fit in 8px grids nicely.

This is how our type scale looks.

Body copy

  • text-xs is mostly used for badges. If we go with full uppercase with relaxed letter spacing, an xslooks good compared to sm or base.
  • text-sm is helpful for the text in product cards.
  • text-base is the base size that applies by default. We picked 16px as the base for this e-commerce product. But remember the body text you are currently reading is 21px (in desktop mode).
  • text-lg can be used for callouts, descriptions, etc. This can be used as a heading too with enough font-weight using h3.


  • text-xl is our h2.
  • text-2xl is our h1.

Display Headings

  • text-3xl and text-4xl are used in rare situations where we need obnoxiously huge texts.

Font weight

The text size does not have to dictate hierarchy. Our limited headings text-xl and text-2xl can be emphasized by providing a thicker font-weight.

Letter Spacing

letter-spacing demo

The bigger the text is, the tighter the letter-spacing should be.

Line height

Line height can be fixed or relative. We have already given suitable line heights for each text size.

Relative line heights work like percentages. I reset the tailwind config to include only the relative line-heights.

lineHeight: {
'no-gap': '.8',
'extra-tight': '.9',
'none': ' 1',
'tight': ' 1.25',
'snug': ' 1.375',
'normal': ' 1.5',
'relaxed': ' 1.625',
'loose': ' 2',
line heights

The smaller the text is the larger the line height should be.


Tailwind CSS comes with a good range of shadows with the classes shadow-sm -> shadow-2xl. Let’s use them as it is.


Light source

The light source is assumed to be behind the top of the user’s head making the shadows fall below the object.

Foreground color

Making the default background color a shade darker will get de-emphasized automatically.

Dark mode

In dark mode, the shadows almost won't work. The difference in lightness between the foreground vs background gives a sense that the lighter items are closer to the user.

The lighter items look closer.

Also, notice how the darker card shadow-inner looks like a punched-out hole even though the inner shadow is almost nonexistent.


In the text size section, we did a deliberate effort to make the line-heights divisible by 8px.

Spacing is another thing that we have to focus on making them adaptable to the 8px grid. But it is not very practical to have the smallest size as 8px. So, we include 0.125 (2px) and .25 (4px) for a little nudge here and there. After that, they are all 8px divisible.

spacing: {
0: '0rem',
0.5: '0.125rem',
1: '.25rem',
2: '.5rem',
3: '1rem',
4: '1.5rem',
5: '2rem',
6: '3rem',
7: '4rem',
8: '5rem',
9: '6rem',
10: '8rem',
11: '10rem',
12: '12rem',
13: '16rem',
14: '20rem',
15: '24rem',
16: '32rem',


I copied the necessary SVGs from hero icons. I exported them from an index.

Keeping a separate library instead of relying on a third-party library is debatable. But I believe any software product can do fine with a handful of icons.


  • This way is slow when the developers need additional icons.
  • Icons can become inconsistent if we start copying SVGs from multiple sources.


Tailwind comes with zIndex classes for values 0,10,20,30,40,and 50. I prefer having one value behind 0.

So, let's extend zIndex to have -10.

extend: {
zIndex: {
'-10': -10,

Flexbox and Grid

The utility classes tailwind provides for flexbox and grid are going to be responsible for our layout design throughout our application.

It is important to have small how-to-approach demos for layout designs. But currently, I leave this in the flexbox and grid page in our design system. We can develop this later if we want.

flexbox and grid


Let's add line clamp and aspect ratio as add-ons.

// Install
npm i @tailwindcss/aspect-ratio @tailwindcss/line-clamp
// Import in tailwind.config.js
const lineClamp = require('@tailwindcss/line-clamp')
const aspectRatio = require('@tailwindcss/aspect-ratio')
// Add this in module.exports.
plugins: [lineClamp, aspectRatio]


TailwindCSS JIT brings immense benefits to the developer experience. Have look at the official documentation for details.

  • Add mode: ‘jit’ in the tailwind.config.js.
  • Add TAILWIND_MODE=watch in the start scripts.
"start": "TAILWIND_MODE=watch craco start",
"storybook": "TAILWIND_MODE=watch start-storybook -p 6006 -s public",

The config file

After all the work, taliwind.config.js will become like below.


We have created the foundation for our design system. Next up we will be creating base components like accordions, avatars, buttons etc.

That’s it.

Thank you. See you next time.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Karthick Ragavendran

Fullstack engineer @ | React, Typescript, Redux, JavaScript, UI, Storybook, CSS, UX, Cypress, CI/CD.