Overview
styled-static provides two APIs for creating variants:
styledVariants: Creates a component with variant props
cssVariants: Returns a function that generates class strings
Both APIs are fully type-safe and extract CSS at build time.
styledVariants
Create components with type-safe variant props:
import { styled, css, styledVariants } from '@alex.radulescu/styled-static';
const Button = styledVariants({
component: 'button',
css: css`
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
`,
variants: {
color: {
primary: css`background: blue; color: white;`,
danger: css`background: red; color: white;`,
success: css`background: green; color: white;`,
},
size: {
sm: css`font-size: 0.875rem; padding: 0.25rem 0.5rem;`,
lg: css`font-size: 1.125rem; padding: 0.75rem 1.5rem;`,
},
},
});
// Usage - variant props are type-safe
<Button color="primary" size="lg">Large Primary</Button>
<Button color="danger" size="sm">Small Danger</Button>
Wrap CSS strings in css\…“ to get IDE syntax highlighting from the styled-components VSCode extension.
Default Variants
Specify default values for variants:
const Button = styledVariants({
component: 'button',
css: css`padding: 0.5rem 1rem;`,
variants: {
color: {
primary: css`background: blue;`,
secondary: css`background: gray;`,
},
size: {
sm: css`font-size: 0.875rem;`,
md: css`font-size: 1rem;`,
lg: css`font-size: 1.125rem;`,
},
},
defaultVariants: {
color: 'primary',
size: 'md',
},
});
// Uses defaults: color="primary", size="md"
<Button>Click me</Button>
// Override defaults
<Button size="lg">Large Button</Button>
Compound Variants
Apply special styles when multiple variants are combined:
const Button = styledVariants({
component: 'button',
css: css`
padding: 0.5rem 1rem;
font-size: 1rem;
`,
variants: {
color: {
primary: css`background: blue;`,
danger: css`background: red;`,
},
size: {
sm: css`font-size: 0.875rem;`,
lg: css`font-size: 1.125rem;`,
},
},
compoundVariants: [
{
size: 'lg',
color: 'danger',
css: css`
font-weight: 900;
text-transform: uppercase;
`,
},
],
});
// Gets compound styles (font-weight: 900, text-transform: uppercase)
<Button size="lg" color="danger">Delete</Button>
Compound variant conditions must match all specified variants to apply.
cssVariants
Generate class strings instead of components:
import { css, cssVariants, cx } from '@alex.radulescu/styled-static';
const badgeCss = cssVariants({
css: css`
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
`,
variants: {
variant: {
info: css`background: #e0f2fe; color: #0369a1;`,
success: css`background: #dcfce7; color: #166534;`,
warning: css`background: #fef3c7; color: #92400e;`,
},
},
});
// Returns class string
<span className={badgeCss({ variant: 'info' })}>Info</span>
// Returns: "ss-xyz ss-xyz--variant-info"
// Combine with other classes
<span className={cx(badgeCss({ variant: 'success' }), 'ml-2')}>
Success
</span>
Real-World Example
Here’s the Button component from the styled-static documentation:
import { css, styledVariants } from '@alex.radulescu/styled-static';
export const Button = styledVariants({
component: 'button',
css: css`
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
font-family: inherit;
border: none;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
&:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.3);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`,
variants: {
variant: {
primary: css`
background: var(--color-primary);
color: white;
&:hover:not(:disabled) {
background: var(--color-primary-hover);
}
`,
secondary: css`
background: var(--color-border);
color: var(--color-text);
&:hover:not(:disabled) {
background: var(--color-text-secondary);
color: white;
}
`,
ghost: css`
background: transparent;
color: var(--color-text-secondary);
&:hover:not(:disabled) {
background: var(--color-border);
color: var(--color-text);
}
`,
},
size: {
sm: css`
padding: 0.375rem 0.75rem;
font-size: 0.8125rem;
`,
md: css`
padding: 0.5rem 1rem;
font-size: 0.875rem;
`,
lg: css`
padding: 0.75rem 1.5rem;
font-size: 1rem;
`,
},
},
});
// Usage
<Button variant="primary" size="lg">Large Primary</Button>
<Button variant="ghost" size="sm">Small Ghost</Button>
Variant Class Naming
Variant classes follow a BEM-like pattern:
const Button = styledVariants({
component: 'button',
css: css`padding: 1rem;`,
variants: {
color: {
primary: css`background: blue;`,
},
},
});
<Button color="primary" />
// Renders: class="ss-abc ss-abc--color-primary"
// ↑ base ↑ variant modifier
Type Safety
Variants are fully type-safe with autocomplete:
const Button = styledVariants({
component: 'button',
css: css`padding: 1rem;`,
variants: {
color: { primary: css``, danger: css`` },
size: { sm: css``, lg: css`` },
},
});
// ✅ Autocomplete for variant names and values
<Button color="primary" size="lg" />
// ❌ TypeScript error: invalid variant value
<Button color="invalid" />
// ❌ TypeScript error: unknown variant name
<Button theme="dark" />
Build-Time Generation
All variant CSS is extracted at build time:
// What you write:
const Button = styledVariants({
component: 'button',
css: css`padding: 1rem;`,
variants: {
color: {
blue: css`background: blue;`,
red: css`background: red;`,
},
},
});
// Generated CSS (extracted to static file):
.ss-abc { padding: 1rem; }
.ss-abc--color-blue { background: blue; }
.ss-abc--color-red { background: red; }
// Generated component (runtime):
const Button = Object.assign(
(props) => {
// Runtime: map variant props to modifier classes
const variants = props.color ? `ss-abc--color-${props.color}` : '';
return createElement('button', {
...props,
className: m(`ss-abc ${variants}`, props.className)
});
},
{ className: 'ss-abc' }
);
The runtime only performs string concatenation and sanitization. All CSS generation happens at build time.
Variants have minimal runtime overhead:
- CSS is extracted to static files at build time
- Runtime only does string concatenation
- Variant values are sanitized to prevent CSS injection
- No style computation or stylesheet insertion
- Parse variant definitions
- Generate CSS for base + all variants
- Extract to static CSS files
- Generate type-safe component
- Map variant props to class names
- Sanitize variant values
- Concatenate class strings
- Render React element