Skip to main content

Tailwind Config

Sets the base config classes for Chroma that can be easily extended per project

caution

These docs are using the latest minor release of Tailwind (v3.4.1 at the time of writing). If you are experiencing any issues try setting the Tailwind version to 3.3.x in your package.json file. Alternatively check our updated examples as they may have minor differences, for example the Button Group Stacked (mobile only) example has changed from flex-wrap sm:flex to flex-wrap sm:flex-nowrap sm:flex due to a change in class specificity in Tailwind v3.4.1.

Installation

Install

yarn add @iag/chroma-react-ui.components

Require

This package wont be imported, it will instead be required within your tailwind config

presets: [require('@iag/chroma-react-ui-utils.tailwind-config/tailwind-config')],

This will now import the base Chroma Tailwind config file and allow you to extend via the theme.extend object.

For more information on extending Tailwind head over to their docs.

Dependencies

info

In order to use the Chroma Tailwind config you must have the Chroma brand tokens loaded, in most cases this will be via the <BrandProvider> component, for more information read the BrandProvider documentation.

Setup

In order to use the Chroma Tailwind config you need to create a tailwind.config.js file in the root of your application and then require the Chroma Tailwind config as a preset as per below:

module.exports = {
presets: [
require("@iag/chroma-react-ui-utils.tailwind-config/tailwind-config"),
],
// Customizations specific to this project go below, update content path and glob for your project
content: ["./src/**/*.{jsx,ts,tsx,mdx,js}"],
theme: {
extend: {
// add objects to extend here
},
},
plugins: [],
};

Complete Chroma Tailwind config object

Colours are replaced with the Chroma colours, everything else is extended. Important is set to true to ensure that Tailwind classes have the required specificity.

module.exports = {
important: true,
corePlugins: {
preflight: false,
},
theme: {
colors: {
black: "var(--color-text-black)",
white: "var(--color-text-white)",
icon: "var(--color-foundation-icon-default)",
"icon-light": "var(--color-foundation-icon-light)",
neutral: {
black: "var(--color-foundation-neutral-black)",
white: "var(--color-foundation-neutral-white)",
lighter: "var(--color-foundation-neutral-lighter)",
light: "var(--color-foundation-neutral-light)",
DEFAULT: "var(--color-foundation-neutral-default)",
grey: "var(--color-foundation-neutral-grey)",
dark: "var(--color-foundation-neutral-dark)",
darker: "var(--color-foundation-neutral-darker)",
transparent: "var(--color-foundation-neutral-transparent)",
},
primary: {
lighter: "var(--color-foundation-primary-lighter)",
light: "var(--color-foundation-primary-light)",
DEFAULT: "var(--color-foundation-primary-default)",
dark: "var(--color-foundation-primary-dark)",
darker: "var(--color-foundation-primary-darker)",
block: "var(--color-foundation-primary-block)",
contrast: "var(--color-foundation-primary-contrast)",
neutral: "var(--color-foundation-primary-neutral)",
beside: "var(--color-foundation-primary-beside)",
"neutral-solid": "var(--color-foundation-primary-neutral-solid)",
},
secondary: {
lighter: "var(--color-foundation-secondary-lighter)",
light: "var(--color-foundation-secondary-light)",
DEFAULT: "var(--color-foundation-secondary-default)",
dark: "var(--color-foundation-secondary-dark)",
darker: "var(--color-foundation-secondary-darker)",
block: "var(--color-foundation-secondary-block)",
contrast: "var(--color-foundation-secondary-contrast)",
neutral: "var(--color-foundation-secondary-neutral)",
beside: "var(--color-foundation-secondary-beside)",
"neutral-solid": "var(--color-foundation-secondary-neutral-solid)",
},
accent: {
lighter: "var(--color-foundation-accent-lighter)",
light: "var(--color-foundation-accent-light)",
DEFAULT: "var(--color-foundation-accent-default)",
dark: "var(--color-foundation-accent-dark)",
darker: "var(--color-foundation-accent-darker)",
block: "var(--color-foundation-accent-block)",
contrast: "var(--color-foundation-accent-contrast)",
neutral: "var(--color-foundation-accent-neutral)",
beside: "var(--color-foundation-accent-beside)",
"neutral-solid": "var(--color-foundation-accent-neutral-solid)",
},
link: {
lighter: "var(--color-foundation-link-lighter)",
light: "var(--color-foundation-link-light)",
DEFAULT: "var(--color-foundation-link-default)",
dark: "var(--color-foundation-link-dark)",
darker: "var(--color-foundation-link-darker)",
},
error: {
lighter: "var(--color-foundation-error-lighter)",
light: "var(--color-foundation-error-light)",
DEFAULT: "var(--color-foundation-error-default)",
dark: "var(--color-foundation-error-dark)",
darker: "var(--color-foundation-error-darker)",
block: "var(--color-foundation-error-block)",
contrast: "var(--color-foundation-error-contrast)",
neutral: "var(--color-foundation-error-neutral)",
beside: "var(--color-foundation-error-beside)",
"neutral-solid": "var(--color-foundation-error-neutral-solid)",
},
warning: {
lighter: "var(--color-foundation-warning-lighter)",
light: "var(--color-foundation-warning-light)",
DEFAULT: "var(--color-foundation-warning-default)",
dark: "var(--color-foundation-warning-dark)",
darker: "var(--color-foundation-warning-darker)",
block: "var(--color-foundation-warning-block)",
contrast: "var(--color-foundation-warning-contrast)",
neutral: "var(--color-foundation-warning-neutral)",
beside: "var(--color-foundation-warning-beside)",
"neutral-solid": "var(--color-foundation-warning-neutral-solid)",
},
success: {
lighter: "var(--color-foundation-success-lighter)",
light: "var(--color-foundation-success-light)",
DEFAULT: "var(--color-foundation-success-default)",
dark: "var(--color-foundation-success-dark)",
darker: "var(--color-foundation-success-darker)",
block: "var(--color-foundation-success-block)",
contrast: "var(--color-foundation-success-contrast)",
neutral: "var(--color-foundation-success-neutral)",
beside: "var(--color-foundation-success-beside)",
"neutral-solid": "var(--color-foundation-success-neutral-solid)",
},
information: {
lighter: "var(--color-foundation-information-lighter)",
light: "var(--color-foundation-information-light)",
DEFAULT: "var(--color-foundation-information-default)",
dark: "var(--color-foundation-information-dark)",
darker: "var(--color-foundation-information-darker)",
block: "var(--color-foundation-information-block)",
contrast: "var(--color-foundation-information-contrast)",
neutral: "var(--color-foundation-information-neutral)",
beside: "var(--color-foundation-information-beside)",
"neutral-solid": "var(--color-foundation-information-neutral-solid)",
},
interactive: {
lighter: "var(--color-foundation-interactive-lighter)",
light: "var(--color-foundation-interactive-light)",
DEFAULT: "var(--color-foundation-interactive-default)",
dark: "var(--color-foundation-interactive-dark)",
darker: "var(--color-foundation-interactive-darker)",
block: "var(--color-foundation-interactive-block)",
contrast: "var(--color-foundation-interactive-contrast)",
neutral: "var(--color-foundation-interactive-neutral)",
beside: "var(--color-foundation-interactive-beside)",
"neutral-solid": "var(--color-foundation-interactive-neutral-solid)",
},
grey: {
50: "var(--color-global-system-grey-50)",
100: "var(--color-global-system-grey-100)",
200: "var(--color-global-system-grey-200)",
300: "var(--color-global-system-grey-300)",
400: "var(--color-global-system-grey-400)",
500: "var(--color-global-system-grey-500)",
525: "var(--color-global-system-grey-525)",
550: "var(--color-global-system-grey-550)",
575: "var(--color-global-system-grey-575)",
600: "var(--color-global-system-grey-600)",
700: "var(--color-global-system-grey-700)",
800: "var(--color-global-system-grey-800)",
900: "var(--color-global-system-grey-900)",
950: "var(--color-global-system-grey-950)",
},
greyscale: {
DEFAULT: "var(--color-foundation-greyscale-default)",
block: "var(--color-foundation-greyscale-block)",
contrast: "var(--color-foundation-greyscale-contrast)",
neutral: "var(--color-foundation-greyscale-neutral)",
beside: "var(--color-foundation-greyscale-beside)",
"neutral-solid": "var(--color-foundation-interactive-neutral-solid)",
},
},
extend: {
borderRadius: {
100: "var(--border-radius-100)",
200: "var(--border-radius-200)",
300: "var(--border-radius-300)",
400: "var(--border-radius-400)",
500: "var(--border-radius-500)",
600: "var(--border-radius-600)",
700: "var(--border-radius-700)",
800: "var(--border-radius-800)",
900: "var(--border-radius-900)",
},
borderWidth: {
100: "var(--border-width-100)",
200: "var(--border-width-200)",
400: "var(--border-width-400)",
800: "var(--border-width-800)",
},
boxShadow: {
100: "var(--box-shadow-100)",
200: "var(--box-shadow-200)",
300: "var(--box-shadow-300)",
400: "var(--box-shadow-400)",
},
fontSize: {
100: "var(--font-size-100)",
200: "var(--font-size-200)",
300: "var(--font-size-300)",
400: "var(--font-size-400)",
500: "var(--font-size-500)",
600: "var(--font-size-600)",
700: "var(--font-size-700)",
800: "var(--font-size-800)",
900: "var(--font-size-900)",
1000: "var(--font-size-1000)",
},
fontWeight: {
100: "var(--font-weight-100)",
200: "var(--font-weight-200)",
300: "var(--font-weight-300)",
400: "var(--font-weight-400)",
500: "var(--font-weight-500)",
600: "var(--font-weight-600)",
700: "var(--font-weight-700)",
800: "var(--font-weight-800)",
900: "var(--font-weight-900)",
},
lineHeight: {
100: "var(--line-height-100)",
200: "var(--line-height-200)",
300: "var(--line-height-300)",
auto: "var(--line-height-auto)",
none: "var(--line-height-none)",
},
spacing: {
100: "var(--spacing-100)",
200: "var(--spacing-200)",
300: "var(--spacing-300)",
400: "var(--spacing-400)",
500: "var(--spacing-500)",
600: "var(--spacing-600)",
700: "var(--spacing-700)",
800: "var(--spacing-800)",
900: "var(--spacing-900)",
1000: "var(--spacing-1000)",
1100: "var(--spacing-1100)",
1200: "var(--spacing-1200)",
},
textColor: {
heading: "var(--typography-heading-color)",
headline: "var(--typography-headline-color)",
body: "var(--typography-body-color)",
subheading: "var(--typography-subheading-color)",
default: "var(--color-text-default)",
disabled: "var(--color-text-disabled)",
placeholder: "var(--color-text-placeholder)",
light: "var(--color-text-light)",
},
},
},
plugins: [],
};

Tailwind + MFE's

Tailwind is a CSS utility framework that gives us a lot of prebuilt class names. Unfortunately many of them are generic like 'block' or 'flex' and may conflict with existing uses, or in the case of MFEs where there's zero style boundary protection then different versions of Tailwind between the Host and its MFE children (or customisations to Tailwind classes) could cause problems.

Solution - Tailwind prefix

The proposed fix for this is having each MFE use its own set of prefixed Tailwind classnames that won't cause a conflict. It is best to keep this in mind at the start of a project as the fix for converting an existing application to use this approach is more involved and outline in the section below.

As an example, a prefix of cui- (see code below) would update the margin utility class mb-3 to cui-mb-3.

Caveat

The prefix option lets you configure a prefix you want added to the beginning of every Tailwind class to avoid conflicts, however if you enable it, you need to make sure you actually use that prefix in your HTML markup. Tailwind will not change your source files and automagically convert the css class names such as container to cui-container.

To instruct Tailwind to use a prefix for the generated classes in the output css:

tailwind.config.js

module.exports = {
prefix: 'cui-',
...
}

In order for this method to work and have the css all self contained within the MFE the Tailwind CLI method of installation will be necessary so that the css is compiled and can be imported by the app to then bundle with the js.

How to update an existing project to use the prefix in the Tailwind config

Tailwind optimizes CSS generation by only generating the necessary classes based on usage in the source code. However, if we modify Tailwind's configuration to include a prefix, we need to update all instances where Tailwind classes are used in our project.

  1. In the project dir, run npx tailwindcss -o blah.css to have Tailwind scan your files and generate a CSS file matching your project. This file contains some reset CSS, some generic tag-based selectors, and Tailwind's generated classes. We only want the latter.

  2. Open this file in an editor and search for the regex (^| |, )\..* to find the classname selectors. We want to get them all in a list.

  • In VSCode you can do the search, then click into the CSS file text to give it focus, then hit the menu item Selection -> Select All Occurrences, then copy and paste into a new file.
  1. To remove all the leading whitespace and periods, and the trailing braces and commas, run this regex:
  • search: ^[ \.](.*?)[ {]
  • replacement: $1
  1. To turn this into a (very long) regex to look for:
  • search: \n
  • replacement: |
  • Then wrap the resulting long string in parenthesis.
  1. To remove invalid escaping, run:
  • search: \\([:#])
  • replacement: $1
  1. To reduce the matches to those with a leading quote or space:
  • add (["' ])` to the beginning and end of the regex
  1. We'll use this long regex to update your project's classnames. Change the prefix string below in the replacement to match your project.
  • ⚠️ Be aware that you will get false positives so scan the list and remove any obvious false matches first before replacement (typically in comments, style props where generic terms like block will match, and in )
  • ⚠️ Be aware that you'll need to run this at least twice to catch classnames in the middle of others.
  • ⚠️ You may want to limit your first searches just to your React component files and HTML templates to make it easier to handle unique cases afterward, e.g., src/**/*.tsx, *.html
  • search: the long string you've just created (for reference, the string we used for Consent UI can be found at the bottom of this page)
  • replacement: $1cui-$2$3
  1. There are some final unique checks:
  • for classnames containing variant modifiers (like sm:flex and hover:mb-2), this replacement will have put the prefix before the variant modifier, but it needs to come after it (see https://tailwindcss.com/docs/configuration#prefix). You can find these cases by searching for your prefix followed by ([a-z]:) (e.g., for a prefix of cui- use cui-([a-z]:)) and replacing with $1cui-. Be aware that Tailwind allows multiple variant modifiers (e.g., dark:md:hover:text-green) so check each instance before you replace it. You can easily find whether your project contains multiple variant modifiers with a search for [a-z]+:[a-z]+:.
  • for classnames that use a dash prefix (like -mar-2 for a negative 2 margin), the prefix also needs to go after the leading dash (for example -cui-mar-2). You may be able to do this with a simple search through code for your prefix

Additional notes

As at 2023-03-13 Tailwind with micro-frontends recommends using a prefix of tw-3-2-4- (originally 3-2-4- but classnames can't start with a number). This versioning nomenclature is not recommended by this page because:

  • Individual projects may contain overrides for Tailwind generated classes, reintroducing style collisions by making the last-loaded MFE the "winner"
  • Using the same prefix across multiple projects doesn't reduce the amount of CSS generated because that's done at build time
  • instead it would make the dev tools CSS list extra confusing with duplicate entries when inspecting an element, whereas using a project-specific prefix like cui- is less likely to have collisions as long as the name is unique enough.

For reference, the final big regex we used in Consent UI for step 7 above is:

(["'` ])(container|sr-only|visible|collapse|static|fixed|absolute|relative|left-0|top-0|col-span-11|col-span-12|col-span-3|col-span-9|col-start-3|clear-left|clear-right|clear-both|m-0|mx-auto|my-6|mb-0|mb-1|mb-10|mb-14|mb-2|mb-3|mb-4|mb-5|mb-6|mb-8|ml-1|ml-14|ml-4|ml-8|mr-2|mt-0|mt-1|mt-10|mt-12|mt-2|mt-3|mt-4|mt-6|mt-8|block|flex|table|grid|hidden|h-auto|h-full|w-2\/3|w-full|max-w-\[250px\]|flex-shrink|flex-grow|grow|grow-0|transform|resize|list-disc|grid-cols-1|grid-cols-10|grid-cols-11|grid-cols-12|grid-cols-2|grid-cols-3|grid-cols-4|grid-cols-5|grid-cols-6|grid-cols-7|grid-cols-8|grid-cols-9|grid-rows-1|grid-rows-2|grid-rows-3|grid-rows-4|grid-rows-5|grid-rows-6|flex-row|flex-col|items-start|items-center|items-baseline|justify-end|justify-center|justify-between|border|border-0|bg-error-light|bg-neutral|bg-primary|bg-white|p-0|p-2|p-3|p-4|p-6|px-16|pb-0|pb-1|pb-2|pb-6|pl-2|pl-3|pl-4|pl-6|pr-2|pr-3|pr-4|pr-6|pt-0|pt-1|pt-10|pt-2|text-left|text-center|text-right|text-100|text-1000|text-200|text-300|text-400|text-500|text-600|text-700|text-800|text-900|text-\[12px\]|text-\[14px\]|text-\[16px\]|text-\[18px\]|text-\[20px\]|text-\[22px\]|text-\[60px\]|text-sm|font-400|font-700|uppercase|capitalize|text-\[#ffc\]|text-\[14\]|text-black|text-error|text-information|text-neutral|text-neutral-dark|text-neutral-darker|text-primary|text-primary-dark|text-primary-darker|text-secondary|text-secondary-dark|text-secondary-darker|text-success|text-white|underline|outline|blur|filter|transition|sm:col-span-4|sm:col-span-8|sm:col-start-3|sm:col-start-5|sm:flex|sm:w-auto|sm:flex-1|sm:flex-row|sm:p-6|sm:pr-4|sm:pt-10|md:col-span-11|md:col-start-2|md:p-10|md:pl-10|md:pr-10|md:pt-12|md:text-\[34px\]|dark:md:hover:text-\[#aaaaaa\]:hover|lg:flex|lg:flex-row|lg:p-14|lg:pl-14|lg:pr-14|lg:text-\[40px\])(["'` ])