Vitus Labs
Unistyle

Responsive System

Breakpoints, media queries, responsive value formats, and the transformation pipeline.

Unistyle's responsive system transforms responsive value declarations into optimized, mobile-first media queries.

Default Breakpoints

{
  xs: 0,       // Mobile (no media query — baseline)
  sm: 576,     // Small devices
  md: 768,     // Tablets
  lg: 992,     // Desktops
  xl: 1200,    // Large desktops
  xxl: 1440,   // Extra-large
}

Media queries use em units for accessibility (respects browser font-size settings):

BreakpointPixelEmMedia Query
xs0No wrapper (baseline)
sm57636em@media only screen and (min-width: 36em)
md76848em@media only screen and (min-width: 48em)
lg99262em@media only screen and (min-width: 62em)
xl120075em@media only screen and (min-width: 75em)
xxl144090em@media only screen and (min-width: 90em)

Responsive Value Formats

Every theme property supports three responsive formats:

Scalar (Single Value)

Applied to all breakpoints:

{ fontSize: 16, color: 'red' }
// All breakpoints: fontSize=16, color=red

Array (Positional)

Values map to breakpoints in order. Last value carries forward:

{ fontSize: [12, 14, 16, 18] }
// xs: 12, sm: 14, md: 16, lg: 18, xl: 18, xxl: 18
//                                   ↑ last value repeats

Object (Breakpoint-Keyed)

Explicit breakpoint assignment. Gaps filled by previous breakpoint:

{ fontSize: { xs: 12, md: 16, lg: 18 } }
// xs: 12, sm: 12, md: 16, lg: 18, xl: 18, xxl: 18
//         ↑ inherited from xs

Transformation Pipeline

The responsive engine applies 5 transformations:

1. Normalize

Expands arrays and objects to full breakpoint maps:

// Input
{ fontSize: [12, 14, 16], padding: { xs: 8, md: 16 }, color: 'red' }

// After normalize
{
  fontSize: { xs: 12, sm: 14, md: 16, lg: 16, xl: 16, xxl: 16 },
  padding:  { xs: 8,  sm: 8,  md: 16, lg: 16, xl: 16, xxl: 16 },
  color:    'red'  // Scalars unchanged
}

Fast path: If no arrays or objects are detected among the values, normalization is skipped entirely — scalars pass through unchanged, avoiding the overhead of creating full breakpoint maps.

2. Transform

Pivots from property-centric to breakpoint-centric layout:

// Input (normalized)
{
  fontSize: { xs: 12, sm: 14, md: 16 },
  padding:  { xs: 8, md: 16 },
  color:    'red'
}

// After transform
{
  xs: { fontSize: 12, padding: 8, color: 'red' },
  sm: { fontSize: 14 },
  md: { fontSize: 16, padding: 16 },
}

3. Optimize

Removes duplicate breakpoints with identical styles:

// Input
{
  xs: { fontSize: 16, color: 'red' },
  sm: { fontSize: 16, color: 'red' },  // Identical to xs
  md: { fontSize: 18, color: 'red' },
  lg: { fontSize: 18, color: 'red' },  // Identical to md
}

// After optimize
{
  xs: { fontSize: 16, color: 'red' },
  md: { fontSize: 18, color: 'red' },
}
// sm and lg removed — avoids duplicate @media blocks

4. Per-Breakpoint Delta Diff

After each breakpoint's CSS is rendered, the cascade optimizer compares it to the running cascade and drops declarations whose prop: value is unchanged. Mobile-first @media (min-width: …) already inherits earlier declarations, so re-emitting them at every breakpoint is pure byte waste.

/* Without delta diff — every breakpoint repeats every property */
{ position: absolute; bottom: -4.375rem; right: -12.5rem; height: 28.75rem; }
@media (min-width: 36em) {
  position: absolute; bottom: -4.375rem; right: -11.25rem; height: 28.75rem;
}
@media (min-width: 48em) {
  position: absolute; bottom: 0; right: -11.25rem; height: 40rem;
}

/* With delta diff — each breakpoint only emits what actually changed */
{ position: absolute; bottom: -4.375rem; right: -12.5rem; height: 28.75rem; }
@media (min-width: 36em) { right: -11.25rem; }
@media (min-width: 48em) { bottom: 0; height: 40rem; }

The diff operates on the rendered CSS strings, parsing top-level declarations + opaque blocks (nested selectors / at-rules) with quote-, paren-, and brace-aware scanning. Selector blocks like &:hover { … } are deduped by exact text match.

Limitations the optimizer doesn't model:

  • Shorthand vs longhand — if breakpoint A sets padding: 1rem and B sets padding-top: 0, both are kept (different prop keys). Conversely, B's padding shorthand is always emitted because it resets sides A didn't set.
  • Whitespace-equivalent blocks&:hover{color:red} and &:hover { color: red; } would both be emitted.

Engine compatibility — the diff runs on stringified results. If a styles callback returns an engine-specific object whose default toString() yields [object Object], the optimizer bypasses and the original unoptimized path runs unchanged. Styler's CSSResult.toString() resolves with empty props, so styles callbacks that destructure theme at call-time (the project convention) work transparently.

5. Media Query Wrapping

Each remaining breakpoint gets wrapped in its media query:

/* xs (baseline, no @media) */
font-size: 0.75rem;
color: red;

/* md and larger */
@media only screen and (min-width: 48em) {
  font-size: 1.125rem;
}

makeItResponsive()

The core responsive engine used by styled components:

import { makeItResponsive, styles } from '@vitus-labs/unistyle'
import { config } from '@vitus-labs/core'

const ResponsiveBox = config.styled('div')`
  ${makeItResponsive({
    key: '$box',
    css: config.css,
    styles: (props) => styles({
      theme: props.theme,
      css: config.css,
      rootSize: 16,
    }),
  })}
`

Options

OptionTypeDescription
keystringTheme prop name (e.g., '$box')
cssfunctionCSS tagged template function
stylesfunctionStyle processor callback
normalizebooleanEnable/disable normalization (default: true)

No-Breakpoint Fast Path

When the theme has no breakpoints at all (no __VITUS_LABS__ data), makeItResponsive skips the entire normalize/transform/optimize pipeline and renders plain CSS directly — a single pass through the styles function.

Caching

makeItResponsive uses a WeakMap to cache optimized themes per component:

  • Key: Theme prop object reference
  • Hit criteria: Same object reference + same sorted breakpoints
  • Result: The expensive normalize → transform → optimize pipeline runs only once per unique theme object

When the theme prop reference doesn't change between renders, all breakpoint processing is skipped and the cached result is reused.

Responsive Alignment

The alignment system supports responsive values:

const theme = {
  display: 'flex',
  direction: { xs: 'rows', md: 'inline' },       // Column on mobile, row on desktop
  alignX: { xs: 'center', md: 'left' },
  alignY: 'center',
  gap: [8, 12, 16],                                // Scales up with breakpoints
}

Custom Breakpoints

Configure custom breakpoints via the Provider:

<Provider
  theme={{
    rootSize: 16,
    breakpoints: {
      mobile: 0,
      tablet: 600,
      desktop: 1024,
      wide: 1440,
    },
  }}
>
  <App />
</Provider>

// Usage with custom breakpoint names
<Box $box={{
  fontSize: { mobile: 14, tablet: 16, desktop: 18 },
  padding: { mobile: 8, desktop: 24 },
}} />

API Reference

Breakpoints

Prop

Type

Value

Prop

Type

MakeItResponsive

Prop

Type

On this page