Class merging
Components that accept a class prop need to merge it with their own classes so the caller wins: p-4 passed in should replace the component’s p-2, not sit next to it. tailwind-merge does that for Tailwind’s built-in utilities. @static-bloom/tailwind/merge extends it with the Static Bloom utilities.
import { cn } from '@static-bloom/tailwind/merge'
cn('rounded-md bg-panel p-2', props.class)cn('glow glow-ion', isAlert && 'glow-danger')cn('bg-canvas', { 'bg-panel': raised }, [false, 'p-4'])cn(...inputs) accepts strings, numbers, null, undefined, booleans, nested arrays and objects whose truthy keys are added. Falsy values are dropped, and it needs no clsx. When two classes set the same thing, the later one wins. Every result below was computed by the shipped cn when this page was built:
| Call | Result |
|---|---|
cn('glow glow-ion', 'glow-lg') | 'glow-ion glow-lg' |
cn('shadow-md', 'glow-sm') | 'glow-sm' |
cn('text-glow text-glow-plasma', 'text-glow-acid') | 'text-glow text-glow-acid' |
cn('text-sm text-fg', 'text-fg-muted') | 'text-sm text-fg-muted' |
cn('bg-canvas px-4', 'bg-panel') | 'px-4 bg-panel' |
cn('bg-panel backdrop-blur-sm', 'glass') | 'glass' |
cn('bg-bloom', 'scanlines') | 'scanlines' |
cn('static', 'relative') | 'static relative' |
cn('rounded-md shadow-sm', 'rounded-xl shadow-overlay') | 'rounded-xl shadow-overlay' |
cn('font-sans', 'font-display') | 'font-display' |
cn('animate-fade-in', 'animate-pulse-glow') | 'animate-pulse-glow' |
What the configuration teaches tailwind-merge:
- Glow sizes (
glow,glow-sm,glow-lg) share the box-shadow group, so they replace each other and anyshadow-*. - Glow colors have a group of their own, shared by
glow-*andtext-glow-*: the last color wins, and a size never removes a color. text-glowbelongs to the text-shadow group.static,scanlines,bracketsandbg-bloomall setbackground-image, so they replace each other.glassreplaces an earlierbg-*color orbackdrop-blur-*. A color written after it still applies.text-bloomsets several properties at once, so it only replaces anothertext-bloom.staticis left out of the position group, socn('static', 'relative')keeps the grain.- Fonts, radius, shadows and animations extend Tailwind’s scales:
font-display,rounded-xs,shadow-overlayandanimate-pulse-glowmerge like their built-in neighbors.
twMergeConfig
Section titled “twMergeConfig”To use the configuration with your own tailwind-merge instance, pass it to extendTailwindMerge:
import { extendTailwindMerge } from 'tailwind-merge'import { twMergeConfig } from '@static-bloom/tailwind/merge'
export const merge = extendTailwindMerge(twMergeConfig)twMergeConfig is typed as TwMergeConfig. The module also exports the ClassValue, ClassDictionary and StaticBloomClassGroupIds types.
Recipes with tailwind-variants
Section titled “Recipes with tailwind-variants”tailwind-variants builds class lists from variants. Create its tv with createTV from the tailwind-variants/config entry point, so every recipe merges with the Static Bloom configuration:
import { createTV } from 'tailwind-variants/config'import { twMergeConfig } from '@static-bloom/tailwind/merge'
export const tv = createTV({ twMergeConfig })import { tv } from './tv'
export const chip = tv({ base: 'inline-flex items-center gap-1 rounded-md px-2 py-0.5 font-mono text-xs', variants: { intent: { primary: 'bg-primary/15 text-primary', danger: 'bg-danger/15 text-danger', }, glowing: { true: 'glow-sm' }, }, compoundVariants: [{ intent: 'danger', glowing: true, class: 'glow-danger' }], defaultVariants: { intent: 'primary' },})The same recipe, rendered when this page was built. In the last row the caller’s rounded-full replaces the base rounded-md:
| Call | Rendered | Classes |
|---|---|---|
chip() | wanted | inline-flex items-center gap-1 rounded-md px-2 py-0.5 font-mono text-xs bg-primary/15 text-primary |
chip({ glowing: true }) | wanted | inline-flex items-center gap-1 rounded-md px-2 py-0.5 font-mono text-xs bg-primary/15 text-primary glow-sm |
chip({ intent: 'danger', glowing: true }) | wanted | inline-flex items-center gap-1 rounded-md px-2 py-0.5 font-mono text-xs bg-danger/15 text-danger glow-sm glow-danger |
chip({ intent: 'danger', class: 'rounded-full' }) | wanted | inline-flex items-center gap-1 px-2 py-0.5 font-mono text-xs bg-danger/15 text-danger rounded-full |
In Vue
Section titled “In Vue”@static-bloom/vue exports the same two helpers, already configured: cn is the function above and tv is created with createTV({ twMergeConfig }). Its components merge their class prop with them, so overrides behave the same everywhere.
import { cn, tv } from '@static-bloom/vue'