Skip to content

sv - Style Variants

Create variants for inline CSS styles (React’s style prop, Vue’s :style, etc.).

import { sv } from 'css-variants'
function sv<T extends StyleVariantRecord | undefined>(
config: StyleVariantDefinition<T>
): StyleVariantFn<T>
interface StyleVariantDefinition<T> {
base?: CssProperties
variants?: T
compoundVariants?: (ObjectKeyArrayPicker<T> & { style: CssProperties })[]
defaultVariants?: ObjectKeyPicker<T>
}
type CssProperties = Properties<string | number> & {
[key: `--${string}`]: string | number // CSS custom properties
}
ParameterTypeDescription
baseCssPropertiesBase styles applied to all instances
variantsRecord<string, Record<string, CssProperties>>Variant definitions
compoundVariantsArrayConditional styles with style property
defaultVariantsObjectDefault variant selections

Returns a function that accepts variant props and returns a CSS properties object.

type StyleVariantFn<T> = (props?: VariantProps<T> & { style?: CssProperties }) => CssProperties
const box = sv({
base: {
display: 'flex',
borderRadius: '8px',
},
variants: {
size: {
sm: { padding: '8px', fontSize: '14px' },
md: { padding: '16px', fontSize: '16px' },
lg: { padding: '24px', fontSize: '18px' },
},
color: {
gray: { backgroundColor: '#f3f4f6', color: '#1f2937' },
blue: { backgroundColor: '#dbeafe', color: '#1e40af' },
red: { backgroundColor: '#fee2e2', color: '#991b1b' },
},
},
defaultVariants: {
size: 'md',
color: 'gray',
},
})
box({ size: 'lg', color: 'blue' })
// => {
// display: 'flex',
// borderRadius: '8px',
// padding: '24px',
// fontSize: '18px',
// backgroundColor: '#dbeafe',
// color: '#1e40af'
// }
const theme = sv({
base: {
'--spacing-unit': '8px',
'--border-radius': '4px',
},
variants: {
theme: {
light: {
'--color-bg': '#ffffff',
'--color-text': '#000000',
},
dark: {
'--color-bg': '#1a1a1a',
'--color-text': '#ffffff',
},
},
},
})
// React usage
<div style={theme({ theme: 'dark' })}>
<p style={{ color: 'var(--color-text)' }}>Dark mode text</p>
</div>
const progressBar = sv({
base: {
width: '100%',
height: '8px',
borderRadius: '9999px',
overflow: 'hidden',
},
variants: {
variant: {
default: { backgroundColor: '#e5e7eb' },
success: { backgroundColor: '#d1fae5' },
error: { backgroundColor: '#fee2e2' },
},
animated: {
true: { transition: 'all 0.3s ease' },
false: {},
},
},
compoundVariants: [
{
variant: 'success',
animated: true,
style: {
boxShadow: '0 0 0 2px rgba(16, 185, 129, 0.2)',
},
},
],
})

Add or override styles at runtime:

const card = sv({
base: { padding: '16px', borderRadius: '8px' },
})
card({ style: { marginTop: '24px', boxShadow: '0 2px 4px rgba(0,0,0,0.1)' } })
// => {
// padding: '16px',
// borderRadius: '8px',
// marginTop: '24px',
// boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
// }
import type { CSSProperties, PropsWithChildren } from 'react'
const boxStyles = sv({
base: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
variants: {
size: {
sm: { width: '100px', height: '100px' },
md: { width: '200px', height: '200px' },
lg: { width: '300px', height: '300px' },
},
color: {
blue: { backgroundColor: '#3b82f6' },
green: { backgroundColor: '#10b981' },
red: { backgroundColor: '#ef4444' },
},
},
})
type BoxProps = PropsWithChildren<Parameters<typeof boxStyles>[0]>
function Box({ size, color, children, style }: BoxProps) {
return (
<div style={boxStyles({ size, color, style })}>
{children}
</div>
)
}
// Usage
<Box size="lg" color="blue" style={{ border: '2px solid white' }}>
Content
</Box>
<template>
<div :style="boxStyle">
<slot />
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { sv } from 'css-variants'
const box = sv({
base: { padding: '16px', borderRadius: '8px' },
variants: {
variant: {
solid: { backgroundColor: '#3b82f6', color: 'white' },
outline: { border: '2px solid #3b82f6', color: '#3b82f6' },
},
},
})
type BoxProps = Parameters<typeof box>[0]
const { variant } = defineProps<BoxProps>()
const boxStyle = computed(() => box({ variant }))
</script>
const box = sv({
variants: {
size: { sm: {...}, md: {...}, lg: {...} },
color: { blue: {...}, green: {...} },
},
})
type BoxVariants = Parameters<typeof box>[0]
// => { size?: 'sm' | 'md' | 'lg', color?: 'blue' | 'green', style?: CssProperties }
Use sv when…Use cv when…
Working with inline stylesWorking with CSS classes
Need dynamic CSS valuesUsing Tailwind or CSS frameworks
Managing CSS custom propertiesWant class-based styling
Need programmatic style controlBuilding a class-based design system