Components
Build typed variant APIs with styles.component
Components
styles.component() is the first-class API for variant-driven component styling.
styles.component() is the unified API for all component styling. For flat configs (no dimensioned variants), see Styles.
Use the dimensioned config when you want a typed interface with:
basestylesvariantsdimensionscompoundVariantsfor combinationsdefaultVariants
Basic component
import { styles } from 'typestyles';
export const button = styles.component('button', {
base: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid transparent',
borderRadius: '8px',
fontWeight: 500,
},
variants: {
intent: {
primary: { backgroundColor: '#2563eb', color: 'white' },
ghost: { backgroundColor: 'transparent', color: '#1f2937' },
},
size: {
sm: { padding: '6px 10px', fontSize: '14px' },
lg: { padding: '10px 16px', fontSize: '16px' },
},
},
defaultVariants: {
intent: 'primary',
size: 'sm',
},
});
// Call as a function -- base styles are always auto-applied:
button(); // "button-base button-intent-primary button-size-sm"
button({ size: 'lg' }); // "button-base button-intent-primary button-size-lg"
button({ intent: 'ghost', size: 'lg' }); // "button-base button-intent-ghost button-size-lg"
// Or destructure for direct class access:
const { base } = button;
Class strings follow the global class naming configuration (semantic by default).
Compound variants
Use compoundVariants for styles that should apply only when multiple variant values match.
const badge = styles.component('badge', {
variants: {
tone: {
success: { color: '#166534' },
warning: { color: '#92400e' },
danger: { color: '#991b1b' },
},
size: {
sm: { fontSize: '12px' },
lg: { fontSize: '14px' },
},
},
compoundVariants: [
{
variants: { tone: ['success', 'warning'], size: 'lg' },
style: { fontWeight: 700 },
},
],
});
badge({ tone: 'success', size: 'lg' }); // includes "badge-compound-0"
badge({ tone: 'danger', size: 'lg' }); // does not include compound class
compoundVariants supports:
- single values:
{ size: 'lg' } - multi-value arrays:
{ tone: ['success', 'warning'] }
Boolean variants
Boolean variant dimensions are represented with "true" / "false" option keys.
const input = styles.component('input', {
base: { border: '1px solid #d1d5db' },
variants: {
invalid: {
true: { borderColor: '#ef4444' },
false: { borderColor: '#d1d5db' },
},
},
defaultVariants: {
invalid: false,
},
});
input(); // "input-base input-invalid-false"
input({ invalid: true }); // "input-base input-invalid-true"
Multipart slots
Pass a slots array for components with multiple parts (for example root, trigger, and panel). base, variants, compoundVariants, and defaultVariants can each target specific slot keys.
const tabs = styles.component('tabs', {
slots: ['root', 'trigger', 'content'] as const,
base: {
root: { display: 'grid' },
trigger: { cursor: 'pointer' },
},
variants: {
size: {
sm: {
trigger: { fontSize: '12px' },
content: { padding: '8px' },
},
lg: {
trigger: { fontSize: '16px' },
content: { padding: '12px' },
},
},
},
defaultVariants: { size: 'sm' },
});
const c = tabs();
c.root; // class string for the root element
c.trigger;
c.content;
Data and ARIA selectors
styles.component supports all CSS selectors:
const accordionTrigger = styles.component('accordion-trigger', {
base: {
'&[data-state="open"]': { fontWeight: 600 },
'&[aria-expanded="true"]': { color: '#1d4ed8' },
},
});
Migration quick-start
From CVA
CVA config maps directly:
cva(base, { variants, compoundVariants, defaultVariants })- to
styles.component(name, { base, variants, compoundVariants, defaultVariants })
The main difference is class generation/injection is handled by typestyles.
See the Migration Guide for library-specific examples.