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.


  • 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!

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

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-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-2xl is our h1.

Display Headings

Font weight

Letter Spacing

letter-spacing demo

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

Line height

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.



Light source

Foreground color

Dark mode

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.


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',


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.


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

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

Flexbox and Grid

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


// 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]


  • 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


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.