Responsive Utility
Build responsive variants and classes with typed helpers for color, size, dimension, and class-variance-authority (cva) integrations.
Responsive Utility
The responsive helpers make it easy to:
- Generate typed Tailwind-style variant maps for colors, sizes, and dimensions
- Accept responsive values (single or array-per-breakpoint)
- Integrate with
class-variance-authority(cva) to produce classes for each breakpoint - Clean redundant classes for a compact, conflict-free output
All responsive arrays follow the breakpoint order: ["", "sm:", "md:", "lg:"]. Extra entries beyond 4 are ignored; partial arrays are allowed.
Breakpoints
- Default: no prefix ("")
- Small: sm:
- Medium: md:
- Large: lg:
A responsive array like [2, 4, 6, 8] will map across these breakpoints.
Types
Use ResponsiveValue<T> for single or per-breakpoint values and ResponsiveProps<T> to make every property in an object responsive.
type ResponsiveValue<T> = T | [T, T] | [T, T, T] | [T, T, T, T];
type ResponsiveProps<T> = {
[K in keyof T]: ResponsiveValue<T[K]>;
};
/* Color types */
type ColorShade = "50" | "100" | "200" | "300" | "400" | "500"
| "600" | "700" | "800" | "900" | "950";
type BaseColor =
| "card"
| "primary"
| "secondary"
| "accent"
| "background"
| "foreground"
| "destructive"
| "white"
| "sidebar-accent"
| "sidebar";
// Generates e.g. "bg-primary" or "bg-zinc-500" as "neutral-500"
type Color = BaseColor | `neutral-${ColorShade}`;Variant generators
These functions generate typed maps of Tailwind-like classes for use in cva or directly.
getSizeVariants
import { getSizeVariants } from '@loke/design-system/responsive';
// For padding "p", returns:
// { 0:'p-0', 1:'p-1', ..., 32:'p-32', small:'p-3', medium:'p-5', ... }
const paddingVariants = getSizeVariants('p');getNegativeSizeVariants
import { getNegativeSizeVariants } from '@loke/design-system/responsive';
// For top offsets "top", returns:
// { '-1': '-top-1', '-2': '-top-2', ... }
const negativeTop = getNegativeSizeVariants('top');getColorVariants
import { getColorVariants } from '@loke/design-system/responsive';
// For background "bg", returns base + neutral shades:
// { primary:'bg-primary', background:'bg-background', 'neutral-500':'bg-zinc-500', ... }
const bgVariants = getColorVariants('bg');getDimensionVariants
import { getDimensionVariants } from '@loke/design-system/responsive';
// For width "w", returns a merged map including:
// numeric sizes (w-0..w-32), fractions (w-1/2, w-3/4, ...), specials (w-full, w-auto, w-screen, ...).
const widthVariants = getDimensionVariants('w');cva integrations
The helpers interoperate with class-variance-authority to produce responsive classes.
createResponsiveVariants
Generate classes for an entire prop object (mix of single and responsive array props).
import { cva } from 'class-variance-authority';
import {
createResponsiveVariants,
getSizeVariants,
} from '@loke/design-system/responsive';
// Define your variants
const buttonVariants = cva('btn', {
defaultVariants: {
size: 2, // maps to p-2
variant: 'solid',
},
variants: {
size: getSizeVariants('p'), // p-0..p-32 and named sizes
variant: {
outline: 'border border-blue-500 text-blue-500',
solid: 'bg-blue-500 text-white',
},
},
});
// Create responsive classes for given props
const classes = createResponsiveVariants(
buttonVariants,
{
size: [1, 2, 4], // default: p-1, sm:p-2, md:p-4
variant: 'outline', // same at all breakpoints
}
);
// => "btn p-1 sm:p-2 md:p-4 border border-blue-500 text-blue-500"Partial arrays are allowed: e.g., size=smlg. Breakpoint entries beyond 4 are ignored.
createResponsiveComponent
Wrap a cva variants object to get a convenience function that accepts responsive props.
import {
createResponsiveComponent,
getSizeVariants,
} from '@loke/design-system/responsive';
import { cva } from 'class-variance-authority';
const cardVariants = cva('card', {
defaultVariants: {
pad: 4,
tone: 'default',
},
variants: {
pad: getSizeVariants('p'), // p-0..p-32 & named sizes (sm, md, etc.)
tone: {
default: 'bg-card text-card-foreground',
muted: 'bg-muted text-muted-foreground',
},
},
});
const { createResponsive } = createResponsiveComponent(cardVariants);
// Single + responsive array
const classes = createResponsive({
pad: [2, 4, 6],
tone: 'muted',
});
// => "card p-2 sm:p-4 md:p-6 bg-muted text-muted-foreground"API reference
getSizeVariants(type)
Prop
Type
Returns an object with keys for numeric sizes (0..32 a subset) and named sizes (xxsmall, xsmall, small, medium, large, xlarge, xxlarge) mapped to Tailwind-like classes.
getNegativeSizeVariants(type)
Prop
Type
Returns -<type>-<n> classes for supported sizes, typed as negative size keys (e.g., -1, -2, …).
getColorVariants(type)
Prop
Type
Returns a map of base semantic colors (primary, background, etc.) plus neutral shades as zinc (e.g., neutral-500 -> text-zinc-500).
getDimensionVariants(type)
Prop
Type
Returns a merged map of numeric, fraction, and special dimension classes (e.g., w-1/2, w-full, w-screen).
createResponsiveVariants(variants, props)
Prop
Type
Returns a string of classes for default and each breakpoint, deduplicated and “cleaned.”
createResponsiveComponent(variants)
Prop
Type
Returns { createResponsive } — a convenience function that accepts ResponsiveProps<VariantProps<typeof variants>> and returns the class string.
Behavior and class cleaning
- Default classes (from your
cvadefinition) are included once - For each breakpoint (sm, md, lg), only classes that differ from earlier breakpoints are included
- Duplicates and redundant responsive classes are removed (e.g., if
text-red-500is set at default,sm:text-red-500is omitted)
This produces concise, predictable class strings.
Examples
Partial arrays (mobile + sm only)
const classes = createResponsiveVariants(buttonVariants, {
size: [1, 2], // p-1, sm:p-2
variant: 'solid'
});Mixed arrays and singles
const classes = createResponsiveVariants(buttonVariants, {
size: [1, undefined, 4], // default p-1, md:p-4
variant: 'outline',
});Dimensions and colors
import { cva } from 'class-variance-authority';
import {
getDimensionVariants,
getColorVariants,
createResponsiveComponent,
} from '@loke/design-system/responsive';
const boxVariants = cva('box', {
variants: {
w: getDimensionVariants('w'),
bg: getColorVariants('bg'),
},
});
const { createResponsive } = createResponsiveComponent(boxVariants);
const classes = createResponsive({
w: ['full', '1/2', '1/3', '1/4'],
bg: 'primary',
});
// => "box w-full sm:w-1/2 md:w-1/3 lg:w-1/4 bg-primary"Tips
- Prefer semantic colors (e.g., primary, background, destructive) over raw shades where possible
- Keep responsive arrays short and meaningful — extra entries are ignored after 4 breakpoints
- Combine with your component library tokens to ensure consistent spacing and sizing
- Start with single-value props, add responsive arrays only where UX needs change across breakpoints
Troubleshooting
- “Classes look duplicated”
- Ensure you’re not manually appending conflicting classes outside the helper
- “Breakpoint class isn’t applied”
- Verify the array index aligns with ["", "sm:", "md:", "lg:"] and that the value actually differs from earlier breakpoints
- “Type errors with variants”
- Make sure your
cvadefinition’svariantskeys and values match the types expected by the helpers (e.g., size maps, color maps)
- Make sure your