Quickstart
This guide will walk you through creating your first Tokenami components. You’ll learn the core concepts and patterns that make Tokenami powerful for building design systems.
This guide assumes you’ve already installed Tokenami and have your development environment set up.
Your First Styled Component
Let’s start with a simple button component. With Tokenami, you use the css() utility to apply styles:
import { css } from '@tokenami/css' ;
function Button ( props ) {
return (
< button
{ ... props }
style = { css ({
'--padding' : 4 ,
'--background-color' : 'var(--color_sky-500)' ,
'--color' : 'white' ,
'--border-radius' : 'var(--radii_rounded)' ,
'--border' : 'none' ,
'--cursor' : 'pointer' ,
}) }
>
{ props . children }
</ button >
);
}
export default Button ;
Understanding the Syntax
Tokenami uses a simple naming convention:
Property names : Add -- prefix to any CSS property (padding → --padding)
Theme tokens : Reference with var(--group_token) syntax
Grid values : Numbers multiply by your grid value (default 0.25rem)
Property: --padding
The --padding: 4 sets padding to 1rem (4 × 0.25rem grid)
Token: var(--color_sky-500)
References the sky-500 token from your color theme group
Direct values
You can use strings like 'white' or 'none' directly
Adding Hover States
Add pseudo-selectors by prefixing the property name with the selector and an underscore:
< button style = { css ({
'--padding' : 4 ,
'--background-color' : 'var(--color_sky-500)' ,
'--hover_background-color' : 'var(--color_sky-600)' ,
'--color' : 'white' ,
'--hover_color' : 'white' ,
}) } />
Common selectors include:
--hover_ for :hover
--focus_ for :focus
--active_ for :active
--disabled_ for :disabled
Responsive Design
Add breakpoints the same way as pseudo-selectors:
< div style = { css ({
'--padding' : 2 , // Base: 0.5rem
'--md_padding' : 4 , // Medium screens: 1rem
'--lg_padding' : 6 , // Large screens: 1.5rem
}) } />
You can combine breakpoints with pseudo-selectors:
< button style = { css ({
'--padding' : 2 ,
'--md_padding' : 4 , // Medium screens
'--hover_padding' : 3 , // Hover state
'--md_hover_padding' : 5 , // Medium screens + hover
}) } />
Define breakpoints in your tokenami.config.ts under the responsive key.
Composing Styles
For reusable components, extract styles with css.compose() to reduce repetition in your markup:
import { css } from '@tokenami/css' ;
const button = css . compose ({
'--display' : 'inline-flex' ,
'--align-items' : 'center' ,
'--justify-content' : 'center' ,
'--gap' : 2 ,
'--padding-block' : 2 ,
'--padding-inline' : 4 ,
'--background-color' : 'var(--color_sky-500)' ,
'--color' : 'white' ,
'--border-radius' : 'var(--radii_rounded)' ,
'--border' : 'none' ,
'--cursor' : 'pointer' ,
'--transition' : 'background-color 0.2s' ,
'--hover_background-color' : 'var(--color_sky-600)' ,
});
function Button ( props ) {
const [ cn , css ] = button ();
return (
< button
{ ... props }
className = { cn ( props . className ) }
style = { css ( props . style ) }
>
{ props . children }
</ button >
);
}
Now your component outputs clean HTML:
< button class = "tk-abc123" > Click me </ button >
The css.compose() API extracts styles into your stylesheet and replaces them with a class name, keeping your markup DRY.
Adding Variants
Create style variations with the variants option:
import { css } from '@tokenami/css' ;
const button = css . compose ({
'--display' : 'inline-flex' ,
'--align-items' : 'center' ,
'--justify-content' : 'center' ,
'--padding-block' : 2 ,
'--padding-inline' : 4 ,
'--border-radius' : 'var(--radii_rounded)' ,
'--border' : 'none' ,
'--cursor' : 'pointer' ,
variants: {
variant: {
primary: {
'--background-color' : 'var(--color_sky-500)' ,
'--color' : 'white' ,
'--hover_background-color' : 'var(--color_sky-600)' ,
},
secondary: {
'--background-color' : 'var(--color_slate-200)' ,
'--color' : 'var(--color_slate-900)' ,
'--hover_background-color' : 'var(--color_slate-300)' ,
},
danger: {
'--background-color' : 'var(--color_red-500)' ,
'--color' : 'white' ,
'--hover_background-color' : 'var(--color_red-600)' ,
},
},
size: {
small: {
'--padding-block' : 1 ,
'--padding-inline' : 2 ,
'--font-size' : '0.875rem' ,
},
medium: {
'--padding-block' : 2 ,
'--padding-inline' : 4 ,
'--font-size' : '1rem' ,
},
large: {
'--padding-block' : 3 ,
'--padding-inline' : 6 ,
'--font-size' : '1.125rem' ,
},
},
},
});
function Button ( props ) {
const [ cn , css ] = button ({
variant: props . variant || 'primary' ,
size: props . size || 'medium'
});
return (
< button
{ ... props }
className = { cn ( props . className ) }
style = { css ( props . style ) }
>
{ props . children }
</ button >
);
}
Usage:
< Button variant = "primary" size = "large" >
Primary Large
</ Button >
< Button variant = "danger" size = "small" >
Delete
</ Button >
Type-Safe Props
Use Tokenami’s type utilities to get full TypeScript support:
import { type Variants , type TokenamiStyle , css } from '@tokenami/css' ;
import { type ComponentProps } from 'react' ;
const button = css . compose ({
// ... your styles
variants: {
variant: {
primary: { /* ... */ },
secondary: { /* ... */ },
},
size: {
small: { /* ... */ },
large: { /* ... */ },
},
},
});
interface ButtonProps
extends TokenamiStyle < ComponentProps < 'button' >>,
Variants < typeof button > {}
function Button ( props : ButtonProps ) {
const [ cn , css ] = button ( props );
return (
< button
{ ... props }
className = { cn ( props . className ) }
style = { css ( props . style ) }
/>
);
}
Now you get:
Autocomplete for variant and size props
Type errors for invalid variant values
Full TypeScript support for the style prop
Building a Card Component
Let’s build a more complex example—a card component:
import { css } from '@tokenami/css' ;
const card = css . compose ({
'--display' : 'flex' ,
'--flex-direction' : 'column' ,
'--gap' : 3 ,
'--padding' : 4 ,
'--background-color' : 'white' ,
'--border' : '1px solid var(--color_slate-200)' ,
'--border-radius' : 'var(--radii_rounded)' ,
'--box-shadow' : '0 1px 3px rgba(0,0,0,0.1)' ,
variants: {
hoverable: {
true: {
'--transition' : 'all 0.2s' ,
'--hover_box-shadow' : '0 4px 6px rgba(0,0,0,0.1)' ,
'--hover_transform' : 'translateY(-2px)' ,
'--cursor' : 'pointer' ,
},
},
},
});
const cardTitle = css . compose ({
'--margin' : 0 ,
'--font-size' : '1.25rem' ,
'--font-weight' : 'bold' ,
'--color' : 'var(--color_slate-900)' ,
});
const cardContent = css . compose ({
'--color' : 'var(--color_slate-600)' ,
'--line-height' : 1.6 ,
});
export function Card ({ title , children , hoverable , ... props }) {
const [ cardCn , cardCss ] = card ({ hoverable });
const [ titleCn ] = cardTitle ();
const [ contentCn ] = cardContent ();
return (
< div { ... props } className = { cardCn ( props . className ) } style = { cardCss ( props . style ) } >
{ title && < h3 className = { titleCn } > { title } </ h3 > }
< div className = { contentCn } > { children } </ div >
</ div >
);
}
Usage:
< Card title = "Getting Started" hoverable >
< p > Start building with Tokenami today. </ p >
</ Card >
Handling Dynamic Values
Pass dynamic values directly to CSS properties:
function Box ({ color , size }) {
return (
< div style = { css ({
'--background-color' : color ,
'--width' : size ,
'--height' : size ,
}) } />
);
}
// Usage
< Box color = "var(--color_blue-500)" size = "100px" />
For one-off values not in your theme, use the triple-dash fallback:
< div style = { css ({
'--padding' : 'var(---, 20px)' , // Escapes type checking
'--background' : 'var(---, linear-gradient(to right, red, blue))' ,
}) } />
Tokenami intentionally adds friction for arbitrary values to encourage using your design tokens. Use this sparingly.
Style Overrides
The css() utility handles overrides gracefully—the last parameter wins:
function Button ( props ) {
const disabled = props . disabled && {
'--opacity' : 0.5 ,
'--cursor' : 'not-allowed' ,
'--pointer-events' : 'none' ,
};
return (
< button style = { css (
{ '--padding' : 4 }, // Base styles
disabled , // Conditional styles
props . style // Props override everything
) } />
);
}
// Override padding from parent
< Button style = { { '--padding' : 6 } } > Custom Padding </ Button >
Complete Example
Here’s a full example combining everything we’ve learned:
import { css , type Variants , type TokenamiStyle } from '@tokenami/css' ;
import { type ComponentProps } from 'react' ;
const button = css . compose ({
'--display' : 'inline-flex' ,
'--align-items' : 'center' ,
'--justify-content' : 'center' ,
'--gap' : 2 ,
'--padding-block' : 2 ,
'--padding-inline' : 4 ,
'--border-radius' : 'var(--radii_rounded)' ,
'--border' : 'none' ,
'--cursor' : 'pointer' ,
'--transition' : 'all 0.2s' ,
'--font-weight' : '500' ,
variants: {
variant: {
primary: {
'--background-color' : 'var(--color_sky-500)' ,
'--color' : 'white' ,
'--hover_background-color' : 'var(--color_sky-600)' ,
},
secondary: {
'--background-color' : 'transparent' ,
'--color' : 'var(--color_sky-500)' ,
'--border' : '1px solid var(--color_sky-500)' ,
'--hover_background-color' : 'var(--color_sky-50)' ,
},
},
},
});
interface ButtonProps
extends TokenamiStyle < ComponentProps < 'button' >>,
Variants < typeof button > {}
function Button ({ variant = 'primary' , ... props } : ButtonProps ) {
const [ cn , css ] = button ({ variant });
return < button { ... props } className = { cn ( props . className ) } style = { css ( props . style ) } /> ;
}
function App () {
return (
< div style = { css ({
'--padding' : 8 ,
'--display' : 'flex' ,
'--flex-direction' : 'column' ,
'--gap' : 4 ,
'--align-items' : 'flex-start' ,
}) } >
< h1 style = { css ({ '--margin' : 0 }) } > Tokenami Quickstart </ h1 >
< Button variant = "primary" onClick = { () => alert ( 'Primary!' ) } > Primary Button </ Button >
< Button variant = "secondary" onClick = { () => alert ( 'Secondary!' ) } > Secondary Button </ Button >
</ div >
);
}
export default App ;
Next Steps
You now know the fundamentals of Tokenami! Here are some next steps:
Core Concepts Learn about theming, grid values, and advanced selectors
Design Systems Build portable design systems with Tokenami
TypeScript Integration Deep dive into type utilities and CI setup
API Reference Explore the complete Tokenami API
Common Patterns
Layout Components
const stack = css . compose ({
'--display' : 'flex' ,
'--flex-direction' : 'column' ,
'--gap' : 4 ,
});
const row = css . compose ({
'--display' : 'flex' ,
'--align-items' : 'center' ,
'--gap' : 2 ,
});
Focus States
const input = css . compose ({
'--padding' : 2 ,
'--border' : '1px solid var(--color_slate-300)' ,
'--border-radius' : 'var(--radii_rounded)' ,
'--focus_outline' : '2px solid var(--color_sky-500)' ,
'--focus_outline-offset' : '2px' ,
});
Dark Mode
const card = css . compose ({
'--background-color' : 'white' ,
'--color' : 'var(--color_slate-900)' ,
'--dark_background-color' : 'var(--color_slate-800)' ,
'--dark_color' : 'white' ,
});
Custom selectors like dark need to be defined in your tokenami.config.ts under the selectors key.