Skip to main content

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:
Button.tsx
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)
1

Property: --padding

The --padding: 4 sets padding to 1rem (4 × 0.25rem grid)
2

Token: var(--color_sky-500)

References the sky-500 token from your color theme group
3

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:
Button.tsx
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:
Button.tsx
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:
Button.tsx
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:
Card.tsx
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:
App.tsx
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.