Unistyle
Unistyle
Responsive CSS property mapping and design system utilities.
Unistyle is a data-driven CSS property mapping and responsive design system. It provides 170+ CSS property mappings with automatic unit conversion, responsive breakpoints, and semantic alignment utilities.
Overview
- 170+ CSS property mappings organized by descriptor type
- Responsive design engine with mobile-first media queries
- Automatic unit conversion — numbers to rem, px to rem
- Semantic alignment —
alignX,alignY,directionmapped to flex properties - Spacing shorthands — margin, padding, border with full/x/y/side syntax
- Theme Provider with pre-computed breakpoints and media queries
Installation
npm install @vitus-labs/unistyleRequires @vitus-labs/core and a CSS engine connector.
Try It Live
Drag the slider to simulate viewport width. The chip below shows which breakpoint Unistyle's responsive resolver picks for each width:
function ResponsiveDemo() { // Default mobile-first breakpoints (matches the unistyle defaults). const breakpoints = { xs: 0, sm: 576, md: 768, lg: 992, xl: 1200, xxl: 1400 } // Simulated responsive value: { sm: 14, md: 16, lg: 20, xl: 24 } const responsivePadding = { sm: 8, md: 16, lg: 24, xl: 32 } const [width, setWidth] = React.useState(800) // Pick the active breakpoint for the current width — mobile-first // resolver: largest breakpoint whose threshold <= width. const sortedKeys = Object.entries(breakpoints).sort((a, b) => a[1] - b[1]) const active = sortedKeys.reduce((acc, [k, min]) => width >= min ? k : acc, 'xs') // Pick the active responsive value: cascade down from active to find the // nearest defined value (mobile-first inheritance). const order = sortedKeys.map(([k]) => k) const activeIdx = order.indexOf(active) let resolvedPadding = 0 for (let i = activeIdx; i >= 0; i--) { if (responsivePadding[order[i]] != null) { resolvedPadding = responsivePadding[order[i]] break } } return ( <div style={{ fontFamily: 'system-ui' }}> <div style={{ marginBottom: 12, fontSize: 14, color: '#495057' }}> Simulated viewport: <strong>{width}px</strong> </div> <input type="range" min={320} max={1600} value={width} onChange={(e) => setWidth(Number(e.target.value))} style={{ width: '100%', marginBottom: 16 }} /> <div style={{ display: 'flex', gap: 6, marginBottom: 16, flexWrap: 'wrap' }}> {Object.entries(breakpoints).map(([k, min]) => ( <span key={k} style={{ padding: '4px 10px', borderRadius: 6, fontSize: 12, fontWeight: 500, background: k === active ? '#0d6efd' : '#e9ecef', color: k === active ? 'white' : '#495057', }} > {k} ≥ {min}px </span> ))} </div> <div style={{ background: '#f8f9fa', borderRadius: 8, padding: resolvedPadding, transition: 'padding 200ms' }}> <div style={{ background: '#0d6efd', color: 'white', padding: 12, borderRadius: 6, textAlign: 'center', fontSize: 13 }}> padding = {resolvedPadding}px (resolved from <code style={{ background: 'rgba(255,255,255,0.2)', padding: '2px 6px', borderRadius: 3 }}>{JSON.stringify(responsivePadding)}</code>) </div> </div> </div> ) }
Quick Example
import { makeItResponsive, styles } from '@vitus-labs/unistyle'
import { config } from '@vitus-labs/core'
const Box = config.styled('div')`
${makeItResponsive({
key: '$box',
css: config.css,
styles: (props) => styles({
theme: props.theme,
css: config.css,
rootSize: 16,
}),
})}
`
// Responsive styling
<Box $box={{
display: 'flex',
fontSize: [12, 14, 16], // xs: 12, sm: 14, md: 16
padding: { xs: 8, md: 16, lg: 24 }, // Breakpoint object
gap: 16, // Static value
alignItems: 'center',
}} />Exports
Responsive Utilities
| Export | Description |
|---|---|
makeItResponsive() | Core responsive engine — returns styled interpolation |
breakpoints | Default breakpoint config |
sortBreakpoints() | Sort breakpoint keys by pixel value |
createMediaQueries() | Build breakpoint → @media template functions |
transformTheme() | Pivot theme from property-centric to breakpoint-centric |
normalizeTheme() | Expand arrays/objects to full breakpoint maps |
CSS Utilities
| Export | Description |
|---|---|
styles() | Process 170+ CSS property descriptors |
alignContent() | Convert semantic alignment to flex properties |
extendCss() | Evaluate custom CSS (callback or string) |
ALIGN_CONTENT_DIRECTION | Direction value mappings |
ALIGN_CONTENT_MAP_X | X-axis alignment mappings |
ALIGN_CONTENT_MAP_Y | Y-axis alignment mappings |
Unit Utilities
| Export | Description |
|---|---|
stripUnit() | Remove CSS unit suffix from a value |
value() | Convert number to CSS value with unit conversion |
values() | Pick first non-null value, convert, join arrays |
Context
| Export | Description |
|---|---|
Provider | Theme provider enriched with breakpoints and media queries |
context | React context from @vitus-labs/core |
Provider Setup
import { Provider } from '@vitus-labs/unistyle'
const theme = {
rootSize: 16,
breakpoints: {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200,
xxl: 1440,
},
// ... your theme values
}
<Provider theme={theme}>
<App />
</Provider>The Provider enriches the theme with pre-computed __VITUS_LABS__ data:
theme.__VITUS_LABS__ = {
sortedBreakpoints: ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'],
media: {
xs: (css) => css`...`, // No @media wrapper (value=0)
sm: (css) => css`@media ...`, // min-width: 36em
md: (css) => css`@media ...`, // min-width: 48em
// ...
},
}How the Provider Works
- Reads
theme.breakpoints— object mapping names to pixel values - Sorts breakpoints by pixel value ascending via
sortBreakpoints() - Creates
mediaobject viacreateMediaQueries()— converts pixels toemfor accessibility (respects browser font-size settings), then creates tagged-template wrappers - Attaches both to
theme.__VITUS_LABS__(internal namespace) - Memoizes the enriched theme (recomputes only when the theme reference changes)
- Wraps children with the core Provider passing the enriched theme
The smallest breakpoint (typically xs: 0) produces no @media wrapper — its styles are the baseline. All other breakpoints use @media only screen and (min-width: <em>).
API Reference
TProvider
Prop
Type
AlignContent
Prop
Type