This library has a development dependency.
~npm i -D tailwindcss
And the following regular dependencies.
~npm i clsx tailwind-merge class-variance-authority usehooks-ts tailwindcss-animate tailwindcss-radix
Eventually, you'll need all of the Radix UI dependencies. For now, while the library is being developed, this is the list of dependencies you'll need.
~@radix-ui/react-tooltip
You can control the entire theme through the tailwind.css
file.
@tailwind base;@tailwind components;@tailwind utilities;
@layer base { :root { color-scheme: light;
--font-sans: Inter; --font-code: 'Fira Code VF';
--color-background: 0 0% 100%; --color-foreground: 0 0% 0%; --color-brand: 41 96.1% 40.4%; --color-brand-muted: 45 93.4% 47.5%; --color-success-title: 143 64.2% 24.1%; --color-success-foreground: 142 71.8% 29.2%; --color-success-background: 138 76.5% 96.7%; --color-info-title: 226 70.7% 40.2%; --color-info-foreground: 224 76.3% 48%; --color-info-background: 214 100% 96.9%; --color-warning-title: 32 81% 28.8%; --color-warning-foreground: 35 91.7% 32.9%; --color-warning-background: 55 91.7% 95.3%; --color-danger-title: 0 70% 35.3%; --color-danger-foreground: 0 73.7% 41.8%; --color-danger-background: 0 85.7% 97.3%; --color-muted-50: 210 20% 98%; --color-muted-100: 220 14.3% 95.9%; --color-muted-200: 220 13% 91%; --color-muted-300: 216 12.2% 83.9%; --color-muted-400: 218 10.6% 64.9%; --color-muted-500: 220 8.9% 46.1%; --color-muted-600: 215 13.8% 34.1%; --color-muted-700: 217 19.1% 26.7%; --color-muted-800: 215 27.9% 16.9%; --color-muted-900: 221 39.3% 11%; --color-muted-950: 224 71.4% 4.1%; --color-ring: 243 75.4% 58.6%;
--container-min-width: 20; /* 320px */ --container-max-width: 96; /* 1536px */ --container-padding-x: 1rem;
--text-size-deltaX: calc(var(--container-max-width) - var(--container-min-width)); /* 6xl */ --text-size-6xl-min-font-size: 4.5; /* π you can edit this value (in rem) */ --text-size-6xl-max-font-size: 8; /* π you can edit this value (in rem) */ --text-size-6xl-deltaY: calc(var(--text-size-6xl-max-font-size) - var(--text-size-6xl-min-font-size)); --text-size-6xl-gradient: calc(var(--text-size-6xl-deltaY) / var(--text-size-deltaX)); --text-size-6xl-intercept: calc(var(--text-size-6xl-min-font-size) - (var(--text-size-6xl-gradient) * var(--container-min-width))); --text-size-6xl-font-size: calc(var(--text-size-6xl-gradient) * 100vw + var(--text-size-6xl-intercept) * 1rem); --text-size-6xl: clamp(calc(var(--text-size-6xl-min-font-size) * 1rem), var(--text-size-6xl-font-size), calc(var(--text-size-6xl-max-font-size) * 1rem));
/* 5xl */ --text-size-5xl-min-font-size: 3; /* π you can edit this value (in rem) */ --text-size-5xl-max-font-size: 6; /* π you can edit this value (in rem) */ --text-size-5xl-deltaY: calc(var(--text-size-5xl-max-font-size) - var(--text-size-5xl-min-font-size)); --text-size-5xl-gradient: calc(var(--text-size-5xl-deltaY) / var(--text-size-deltaX)); --text-size-5xl-intercept: calc(var(--text-size-5xl-min-font-size) - (var(--text-size-5xl-gradient) * var(--container-min-width))); --text-size-5xl-font-size: calc(var(--text-size-5xl-gradient) * 100vw + var(--text-size-5xl-intercept) * 1rem); --text-size-5xl: clamp(calc(var(--text-size-5xl-min-font-size) * 1rem), var(--text-size-5xl-font-size), calc(var(--text-size-5xl-max-font-size) * 1rem));
/* 4xl */ --text-size-4xl-min-font-size: 2.25; /* π you can edit this value (in rem) */ --text-size-4xl-max-font-size: 3.75; /* π you can edit this value (in rem) */ --text-size-4xl-deltaY: calc(var(--text-size-4xl-max-font-size) - var(--text-size-4xl-min-font-size)); --text-size-4xl-gradient: calc(var(--text-size-4xl-deltaY) / var(--text-size-deltaX)); --text-size-4xl-intercept: calc(var(--text-size-4xl-min-font-size) - (var(--text-size-4xl-gradient) * var(--container-min-width))); --text-size-4xl-font-size: calc(var(--text-size-4xl-gradient) * 100vw + var(--text-size-4xl-intercept) * 1rem); --text-size-4xl: clamp(calc(var(--text-size-4xl-min-font-size) * 1rem), var(--text-size-4xl-font-size), calc(var(--text-size-4xl-max-font-size) * 1rem));
/* 3xl */ --text-size-3xl-min-font-size: 1.875; /* π you can edit this value (in rem) */ --text-size-3xl-max-font-size: 2.25; /* π you can edit this value (in rem) */ --text-size-3xl-deltaY: calc(var(--text-size-3xl-max-font-size) - var(--text-size-3xl-min-font-size)); --text-size-3xl-gradient: calc(var(--text-size-3xl-deltaY) / var(--text-size-deltaX)); --text-size-3xl-intercept: calc(var(--text-size-3xl-min-font-size) - (var(--text-size-3xl-gradient) * var(--container-min-width))); --text-size-3xl-font-size: calc(var(--text-size-3xl-gradient) * 100vw + var(--text-size-3xl-intercept) * 1rem); --text-size-3xl: clamp(calc(var(--text-size-3xl-min-font-size) * 1rem), var(--text-size-3xl-font-size), calc(var(--text-size-3xl-max-font-size) * 1rem));
/* 2xl */ --text-size-2xl-min-font-size: 1.5; /* π you can edit this value (in rem) */ --text-size-2xl-max-font-size: 1.875; /* π you can edit this value (in rem) */ --text-size-2xl-deltaY: calc(var(--text-size-2xl-max-font-size) - var(--text-size-2xl-min-font-size)); --text-size-2xl-gradient: calc(var(--text-size-2xl-deltaY) / var(--text-size-deltaX)); --text-size-2xl-intercept: calc(var(--text-size-2xl-min-font-size) - (var(--text-size-2xl-gradient) * var(--container-min-width))); --text-size-2xl-font-size: calc(var(--text-size-2xl-gradient) * 100vw + var(--text-size-2xl-intercept) * 1rem); --text-size-2xl: clamp(calc(var(--text-size-2xl-min-font-size) * 1rem), var(--text-size-2xl-font-size), calc(var(--text-size-2xl-max-font-size) * 1rem));
/* xl */ --text-size-xl-min-font-size: 1.25; /* π you can edit this value (in rem) */ --text-size-xl-max-font-size: 1.5; /* π you can edit this value (in rem) */ --text-size-xl-deltaY: calc(var(--text-size-xl-max-font-size) - var(--text-size-xl-min-font-size)); --text-size-xl-gradient: calc(var(--text-size-xl-deltaY) / var(--text-size-deltaX)); --text-size-xl-intercept: calc(var(--text-size-xl-min-font-size) - (var(--text-size-xl-gradient) * var(--container-min-width))); --text-size-xl-font-size: calc(var(--text-size-xl-gradient) * 100vw + var(--text-size-xl-intercept) * 1rem); --text-size-xl: clamp(calc(var(--text-size-xl-min-font-size) * 1rem), var(--text-size-xl-font-size), calc(var(--text-size-xl-max-font-size) * 1rem));
/* lg */ --text-size-lg-min-font-size: 1.125; /* π you can edit this value (in rem) */ --text-size-lg-max-font-size: 1.25; /* π you can edit this value (in rem) */ --text-size-lg-deltaY: calc(var(--text-size-lg-max-font-size) - var(--text-size-lg-min-font-size)); --text-size-lg-gradient: calc(var(--text-size-lg-deltaY) / var(--text-size-deltaX)); --text-size-lg-intercept: calc(var(--text-size-lg-min-font-size) - (var(--text-size-lg-gradient) * var(--container-min-width))); --text-size-lg-font-size: calc(var(--text-size-lg-gradient) * 100vw + var(--text-size-lg-intercept) * 1rem); --text-size-lg: clamp(calc(var(--text-size-lg-min-font-size) * 1rem), var(--text-size-lg-font-size), calc(var(--text-size-lg-max-font-size) * 1rem));
/* md */ --text-size-md-min-font-size: 1; /* π you can edit this value (in rem) */ --text-size-md-max-font-size: 1.125; /* π you can edit this value (in rem) */ --text-size-md-deltaY: calc(var(--text-size-md-max-font-size) - var(--text-size-md-min-font-size)); --text-size-md-gradient: calc(var(--text-size-md-deltaY) / var(--text-size-deltaX)); --text-size-md-intercept: calc(var(--text-size-md-min-font-size) - (var(--text-size-md-gradient) * var(--container-min-width))); --text-size-md-font-size: calc(var(--text-size-md-gradient) * 100vw + var(--text-size-md-intercept) * 1rem); --text-size-md: clamp(calc(var(--text-size-md-min-font-size) * 1rem), var(--text-size-md-font-size), calc(var(--text-size-md-max-font-size) * 1rem));
/* sm */ --text-size-sm-min-font-size: 0.875; /* π you can edit this value (in rem) */ --text-size-sm-max-font-size: 1; /* π you can edit this value (in rem) */ --text-size-sm-deltaY: calc(var(--text-size-sm-max-font-size) - var(--text-size-sm-min-font-size)); --text-size-sm-gradient: calc(var(--text-size-sm-deltaY) / var(--text-size-deltaX)); --text-size-sm-intercept: calc(var(--text-size-sm-min-font-size) - (var(--text-size-sm-gradient) * var(--container-min-width))); --text-size-sm-font-size: calc(var(--text-size-sm-gradient) * 100vw + var(--text-size-sm-intercept) * 1rem); --text-size-sm: clamp(calc(var(--text-size-sm-min-font-size) * 1rem), var(--text-size-sm-font-size), calc(var(--text-size-sm-max-font-size) * 1rem));
/* xs */ --text-size-xs-min-font-size: 0.75; /* π you can edit this value (in rem) */ --text-size-xs-max-font-size: 0.875; /* π you can edit this value (in rem) */ --text-size-xs-deltaY: calc(var(--text-size-xs-max-font-size) - var(--text-size-xs-min-font-size)); --text-size-xs-gradient: calc(var(--text-size-xs-deltaY) / var(--text-size-deltaX)); --text-size-xs-intercept: calc(var(--text-size-xs-min-font-size) - (var(--text-size-xs-gradient) * var(--container-min-width))); --text-size-xs-font-size: calc(var(--text-size-xs-gradient) * 100vw + var(--text-size-xs-intercept) * 1rem); --text-size-xs: clamp(calc(var(--text-size-xs-min-font-size) * 1rem), var(--text-size-xs-font-size), calc(var(--text-size-xs-max-font-size) * 1rem));
/* Line height (this depends on paragraph length) */ --text-size-xs-line-height: clamp(1.125rem, 6.2vw, 1.75rem); /* 12px <-> 14px => 18px <-> 28px */ --text-size-sm-line-height: clamp(1.25rem, 6.2vw, 2rem); /* 20px <-> 32px */ --text-size-md-line-height: clamp(1.5rem, 6vw, 2.25rem); /* 24px <-> 36px */ --text-size-lg-line-height: clamp(1.75rem, 5.8vw, 2.25rem); /* 28px <-> 36px */ --text-size-xl-line-height: clamp(2rem, 6.5vw, 2.5rem); /* 32px <-> 40px */ --text-size-2xl-line-height: 1.5; --text-size-3xl-line-height: 1.5; --text-size-4xl-line-height: 1.2; --text-size-5xl-line-height: 1.2; --text-size-6xl-line-height: 1.2;
/* Default tracking. Can be overridden by the 'tracking' prop on the <Heading /> and <Text /> components) */ --text-size-xs-tracking: 0em; --text-size-sm-tracking: 0em; --text-size-md-tracking: 0em; --text-size-lg-tracking: 0em; --text-size-xl-tracking: 0em; --text-size-2xl-tracking: -0.025em; --text-size-3xl-tracking: -0.025em; --text-size-4xl-tracking: -0.025em; --text-size-5xl-tracking: -0.05em; --text-size-6xl-tracking: -0.05em;
--radius: 0.375rem; --min-tap-target: 24px;
--tooltip-color-background: var(--color-foreground); --tooltip-color-foreground: var(--color-background); --tooltip-text-size: var(--text-size-sm); }
.dark { color-scheme: dark; --color-background: 224 35.7% 4.1%; --color-foreground: 0 0% 100%; --color-danger-foreground: 0 73.7% 61.8%; --color-muted-50: 224 71.4% 4.1%; --color-muted-100: 221 39.3% 11%; --color-muted-200: 215 27.9% 16.9%; --color-muted-300: 217 19.1% 26.7%; --color-muted-400: 215 13.8% 34.1%; --color-muted-500: 220 8.9% 46.1%; --color-muted-600: 218 10.6% 64.9%; --color-muted-700: 216 12.2% 83.9%; --color-muted-800: 220 13% 91%; --color-muted-900: 220 14.3% 95.9%; --color-muted-950: 210 20% 98%; --color-ring: 239 86.4% 59.6%;
--color-info-title: 226 70.7% 40.2%; --color-info-foreground: 224 76.3% 48%; --color-info-background: 221 39.3% 11%; }}
@media (pointer: coarse) { :root { --min-tap-target: 44px; }}
Then, you'll need the tailwind.config.ts
that uses those CSS variables.
import type { Config } from 'tailwindcss'import animatePlugin from 'tailwindcss-animate'import radixPlugin from 'tailwindcss-radix'import defaultTheme from 'tailwindcss/defaultTheme.js'
export default { content: ['./app/**/*.{ts,tsx,jsx,js}'], darkMode: 'class', theme: { container: { center: true, padding: '2rem', screens: { '2xl': '1400px', }, }, extend: { colors: { background: 'hsl(var(--color-background) / <alpha-value>)', foreground: 'hsl(var(--color-foreground) / <alpha-value>)', brand: { DEFAULT: 'hsl(var(--color-brand) / <alpha-value>)', muted: 'hsl(var(--color-brand-muted) / <alpha-value>)', }, muted: { 50: 'hsl(var(--color-muted-50) / <alpha-value>)', 100: 'hsl(var(--color-muted-100) / <alpha-value>)', 200: 'hsl(var(--color-muted-200) / <alpha-value>)', 300: 'hsl(var(--color-muted-300) / <alpha-value>)', 400: 'hsl(var(--color-muted-400) / <alpha-value>)', 500: 'hsl(var(--color-muted-500) / <alpha-value>)', 600: 'hsl(var(--color-muted-600) / <alpha-value>)', 700: 'hsl(var(--color-muted-700) / <alpha-value>)', 800: 'hsl(var(--color-muted-800) / <alpha-value>)', 900: 'hsl(var(--color-muted-900) / <alpha-value>)', 950: 'hsl(var(--color-muted-950) / <alpha-value>)', }, success: { foreground: 'hsl(var(--color-success-foreground) / <alpha-value>)', title: 'hsl(var(--color-success-title) / <alpha-value>)', background: 'hsl(var(--color-success-background) / <alpha-value>)', }, info: { foreground: 'hsl(var(--color-info-foreground) / <alpha-value>)', title: 'hsl(var(--color-info-title) / <alpha-value>)', background: 'hsl(var(--color-info-background) / <alpha-value>)', }, warning: { foreground: 'hsl(var(--color-warning-foreground) / <alpha-value>)', title: 'hsl(var(--color-warning-title) / <alpha-value>)', background: 'hsl(var(--color-warning-background) / <alpha-value>)', }, danger: { foreground: 'hsl(var(--color-danger-foreground) / <alpha-value>)', title: 'hsl(var(--color-danger-title) / <alpha-value>)', background: 'hsl(var(--color-danger-background) / <alpha-value>)', }, tooltip: { background: 'hsl(var(--tooltip-color-background) / <alpha-value>)', foreground: 'hsl(var(--tooltip-color-foreground) / <alpha-value>)', }, ring: 'hsl(var(--color-ring) / <alpha-value>)', }, fontFamily: { sans: ['var(--font-sans)', ...defaultTheme.fontFamily.sans], code: ['var(--font-code)', ...defaultTheme.fontFamily.mono], }, fontSize: { 'size-6xl': ['var(--text-size-6xl)', { lineHeight: 'var(--text-size-6xl-line-height)', letterSpacing: 'var(--text-size-6xl-tracking)' }], 'size-5xl': ['var(--text-size-5xl)', { lineHeight: 'var(--text-size-5xl-line-height)', letterSpacing: 'var(--text-size-5xl-tracking)' }], 'size-4xl': ['var(--text-size-4xl)', { lineHeight: 'var(--text-size-4xl-line-height)', letterSpacing: 'var(--text-size-4xl-tracking)' }], 'size-3xl': ['var(--text-size-3xl)', { lineHeight: 'var(--text-size-3xl-line-height)', letterSpacing: 'var(--text-size-3xl-tracking)' }], 'size-2xl': ['var(--text-size-2xl)', { lineHeight: 'var(--text-size-2xl-line-height)', letterSpacing: 'var(--text-size-2xl-tracking)' }], 'size-xl': ['var(--text-size-xl)', { lineHeight: 'var(--text-size-xl-line-height)', letterSpacing: 'var(--text-size-xl-tracking)' }], 'size-lg': ['var(--text-size-lg)', { lineHeight: 'var(--text-size-lg-line-height)', letterSpacing: 'var(--text-size-lg-tracking)' }], 'size-md': ['var(--text-size-md)', { lineHeight: 'var(--text-size-md-line-height)', letterSpacing: 'var(--text-size-md-tracking)' }], 'size-sm': ['var(--text-size-sm)', { lineHeight: 'var(--text-size-sm-line-height)', letterSpacing: 'var(--text-size-sm-tracking)' }], 'size-xs': ['var(--text-size-xs)', { lineHeight: 'var(--text-size-xs-line-height)', letterSpacing: 'var(--text-size-xs-tracking)' }], 'size-tooltip': 'var(--tooltip-text-size)', }, backgroundImage: { diamonds: 'repeating-linear-gradient(-45deg, hsl(var(--color-background)), hsl(var(--color-background)) 4px, transparent 4px, transparent 10px), repeating-linear-gradient(45deg, hsl(var(--color-background)), hsl(var(--color-background)) 4px, hsl(var(--color-muted-200)) 4px, hsl(var(--color-muted-200)) 10px)', }, borderRadius: { lg: 'calc(var(--radius))', md: 'calc(var(--radius) - 2px)', sm: 'calc(var(--radius) - 4px)', }, keyframes: { 'accordion-down': { from: { height: '0' }, to: { height: 'var(--radix-accordion-content-height)' }, }, 'accordion-up': { from: { height: 'var(--radix-accordion-content-height)' }, to: { height: '0' }, }, }, animation: { 'accordion-down': 'accordion-down 0.2s ease-out', 'accordion-up': 'accordion-up 0.2s ease-out', }, transitionProperty: { width: 'width', 'max-width': 'max-width', }, spacing: { container: 'var(--container-padding-x)', 'container-sm': 'calc(var(--container-padding-x) + 0.5rem)', 'container-lg': 'calc(var(--container-padding-x) + 1rem)', }, maxWidth: { container: 'calc(var(--container-max-width) * 1rem)', }, minWidth: { tap: 'var(--min-tap-target)', }, minHeight: { tap: 'var(--min-tap-target)', }, gridTemplateColumns: { responsive: 'repeat(auto-fit, minmax(14rem, 1fr))', }, }, }, plugins: [animatePlugin, radixPlugin],} satisfies Config
Finally, you'll also need the utility cn()
function. The extendTailwindMerge()
is there to tell twMerge()
which classes belong to which groups, so it knows how to merge them.
import { type ClassValue, clsx } from 'clsx'import { extendTailwindMerge } from 'tailwind-merge'
const twMerge = extendTailwindMerge({ classGroups: { colors: [{ 'text-muted': ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950'] }], spacing: [{ 'text-size': ['6xl', '5xl', '4xl', '3xl', '2xl', 'xl', 'lg', 'md', 'sm', 'xs', 'tooltip'] }], },})
export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs))}
In order to use the <Tooltip />
component, you need to wrap your app with the tooltip's provider.
import * as TooltipPrimitive from '@radix-ui/react-tooltip'...
function App() { return ( <html> <head> ... </head> <body> <TooltipPrimitive.Provider delayDuration={300}> <Header /> <Outlet /> <Footer /> ... </TooltipPrimitive.Provider> </body> </html> )}
Ok, you're ready to start using the components. You can copy+paste the ones you need or download the repo. You'll find all components here.
Installation didn't work? Is something missing? Please file an issue here and I'll do my best to add any missing steps to the documentation.