LOKE Design System
Lib

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 cva definition) 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-500 is set at default, sm:text-red-500 is 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 cva definition’s variants keys and values match the types expected by the helpers (e.g., size maps, color maps)