Customize components

TL;DR

  • Components are customizable with the same TailwindCSS-classes
    that they are build on (through the tw-prop)
  • Refer to the Prop-table for the particular component to see what is customizable
  • Base components typically allow more customization
  • Know the impact; customizing a property foregoes ALL styles for that particular property, including handling of any states or variants that may be associated with that property (e.g. :hover, :active, dark:, ...)

One features of the component library is that you can customize components using Tailwind-classes (p-2, text-blue, ...). For example, if we wanted to change the background and the text color of the Badge-component, we could do it like this:

<Badge tw={{ color: ['bg-blue', 'text-white']}}>My tag</Badge>

This would replace the original utils-classes (defined inside the Badge-component) associated with the color property. Note the phrasing her; you don't extend, you REPLACE the util-classes define in component completely for that property.

This is perhaps be easier to understand when you se how the Badge is implemented in this example:

function Badge({ tw, ...restProps }) {
return (
<span
className={twToClassNames({
colors: 'bg-cloud text-haiti', // 👈 base color properties
}, tw)} // 👈 incomming overrides
{...restProps}
/>
)
}

The helper-function twToClassNames(componentStyles, overrides) is where the customization-logic is happening. This function combines the style-properties defined in the component-props (tw) and the base style of the components itself; preferring the ones that comes from the props.

As mention in the Introduction to Tailwind, the Tailwind-utils are considered design tokens of the design system. The components them selfs are just structured group of the same util-classes/tokens. This is why this method makes sense, because we customize with the same tokens that component them self are built on.

Example: Creating a Yezz! Button

Let's say that we need a special kind of branded button to use on the front-page. Normally, buttons are not branded1 because we want to convey intention, and not brand when we use colors on Button (e.g. the "primary action," or the "payment/purchase action," and so on). On the front page, however, we consider this OK since we show a group of branded buttons; denoting that they convey branding.

Ok, back to the Yezz!-button. In the following example we are customizing the base Button by adding bg-lipstick text-white to the tw-prop:

That was easy! Too easy, actually; try to hover the two buttons above. See the problem?

That's right, when you override a property you forego the associated styles related to that property. In this case, the twColors is removing the styling for :hover-state (since the button changes color on hover).

Hover state

Let's fix the missing hover-effect by adding hover:bg-lipstick-lighter to our customization:

Disabled

Nice, almost there! The last thing we need to handle is the disabled condition. Again, try to hover the following disabled button:

See? We don't want the hover-effect when the button is disabled; it sends mixed signals to the user. Is the button interactive or not? 🤔

Unlike the :hover-state, which can be differentiated in Tailwind (e.g. hover:bg-lipstick-lighter), the disabled-state is toggled via the disabled-prop on the Button-component. So we need to remove the hover:-property when the button is disabled:

Putting it all together

If you need to use your customize button multiple times, it probably a good idea to extract it into a thin wrapper.

import React from 'react';
import { Button } from '@ntds/button';
export function YezzButton(props) {
return (
<Button
{...props}
tw={{
color: [
'bg-lipstick', 'text-white',
// 👇 remove the the hover-state when deactivated
!props.disabled && ['hover:bg-lipstick-lighter']
],
}}
/>
);
}
export default YezzButton;

☝️🤓 This is actually how the Primary, Secondary, Payment, and Square buttons are implemented.

Before you customize

  • Component should in most cases be ready to use as-is; that's after all, a key part of a design system
  • If you find yourself overriding many properties to accommodate sketches; make sure there is a rigid rationale behind it
  • Don't hesitate to involve the design team early in the process!

  1. All components are unbranded, except the Logo-component (which uses the brand to resolve the logo-asset). See fundamentals for mor information on this.