# Responsive Breakpoints

> Framer-style responsive breakpoint editing in Design: one DOM tree per screen with cascading width-scoped overrides. Use when adding/removing breakpoints, editing a design at a specific device width, resolving which value renders at a width, or persisting responsive style changes.

- Skill: `builderio/responsive-breakpoints` (Agent Skill)
- Install (CLI): `npx skillmds@latest add builderio/responsive-breakpoints`
- Raw SKILL.md: https://api.skillmd.com/api/skills/builderio/responsive-breakpoints/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Builder.io (https://skillmd.com/u/builderio)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/builderio/responsive-breakpoints

---


# Responsive Breakpoints

Design uses the **Framer model**: every screen is ONE document (one DOM
tree). Breakpoint frames are the SAME document rendered at different
viewport widths — never separate copies. Edits cascade:

- The **base** is the primary (widest) frame. Base edits are plain
  unprefixed classes / inline styles and cascade down to every narrower
  breakpoint unless overridden there.
- Edits made while a **narrower breakpoint is active** persist as
  width-scoped overrides that apply from just below the next-wider frame
  down to zero. Narrower breakpoints layer their own overrides on top.

The bound for an override is `next-wider frame width - 1` (e.g. breakpoints
390/810 with a 1280 primary: editing 810 scopes to `≤ 1279px`, editing 390
scopes to `≤ 809px`). `breakpointUpperBoundPx` in
`shared/responsive-classes.ts` is the canonical implementation.

## Managing breakpoints

A newly generated design's frame set comes from `generate-design`'s `devices`
param (`("mobile"|"tablet"|"desktop")[]`, default `["desktop","mobile"]`). The
cascade is **desktop-base**: the widest requested device is the primary/base
frame and each narrower device is a breakpoint frame. The default injected set
is therefore a Desktop base plus a single Mobile (390) breakpoint frame — the
narrower frame(s) only, never a frame at the primary desktop width and never an
auto-added tablet.

- `add-breakpoint` — adds a device-width frame to `designs.data.breakpointSet`
  (Framer defaults: Desktop 1200 / Tablet 810 / Phone 390, or custom 320-3840).
  Duplicate widths are ignored.
- `remove-breakpoint` — removes one by id (overrides already written stay in
  the document until removed).
- `set-active-breakpoint` — persists the active edit scope to application
  state (`design-active-breakpoint:<designId>`); the UI mirrors it in the
  `design-selection` state (`activeBreakpointId`). Check the active
  breakpoint via `view-screen` before making responsive-only edits. Its
  `editScope` is `cascade-smaller` by default (edits at this breakpoint also
  apply to every narrower one); pass `only` when the user explicitly wants a
  bounded, breakpoint-only override.

The UI's breakpoint bar (chips above the focused screen) and the overview's
side-by-side linked frames drive the same state — a chip click changes the
iframe viewport width AND calls `set-active-breakpoint`.

## Editing responsively (agent parity)

Use `apply-visual-edit` with `activeFrameWidthPx` set to the ACTIVE
breakpoint frame width. This scopes BOTH `class` and `style` intents
uniformly through one class-vs-media decision:

- Values that are Tailwind utilities (`text-lg`, `p-4`) become width-scoped
  classes: `max-[809px]:text-lg` (arbitrary max-width variants the Tailwind
  CDN JIT compiles to `@media (max-width: 809px)`).
- Raw CSS values (`137px`, `rgb(...)`, `calc(...)`) persist into the managed
  `<style data-agent-native-breakpoints>` block as
  `@media (max-width: <bound>px)` rules targeting the element's
  `data-agent-native-node-id`. The block is deterministic (wider buckets
  first so narrower ranges win by source order) and readable/editable in the
  Code panel.

Other targeting options:

- `maxWidthPx` — explicit desktop-down bound, overrides derivation.
- `intent.kind: "breakpoint-style"` — direct managed-media write/remove:
  `{ target, maxWidthPx, property, value, operation: "set" | "remove" }`.
- `activeBreakpoint: "md"` etc. — LEGACY min-width prefix scoping for class
  edits only (mobile-first `md:text-lg`). Still supported for documents
  authored mobile-first; prefer `activeFrameWidthPx` for breakpoint-bar
  parity.

When the design has no breakpoint set, `activeFrameWidthPx` falls back to
the legacy min-width prefix behaviour for class edits. When the active frame
IS the widest context, edits are base writes (correct — the widest frame is
the base).

## Resolving and inspecting overrides

- `effectiveUtilityAtWidth(className, stem, widthPx)` — which utility
  renders at a width (narrowest applicable max-width scope wins, then
  min-width prefixes, then base).
- `getBreakpointOverrideState({ className, html, nodeId, property,
  breakpointWidths, baseWidthPx, activeWidthPx })` from
  `shared/breakpoint-media.ts` — aggregates class + media overrides for the
  inspector's overridden-at-this-breakpoint indicators.
- To RESET an override: remove the scoped class
  (`responsive-class` remove with `maxWidthPx` + stem) or the media rule
  (`breakpoint-style` with `operation: "remove"`); the base value cascades
  back down.

## Cautions

- Managed media rules anchor on `data-agent-native-node-id`. Inline (SQL)
  designs keep these ids; localhost write-back strips them — for local-file
  sources prefer scoped classes.
- Never rewrite the managed block wholesale by hand; use the edit intents so
  same-property writes replace instead of accumulate. The parser tolerates
  Code-panel edits but unknown selectors are ignored.
- Adding a breakpoint between two existing widths changes the bound for NEW
  overrides only; previously written overrides keep their original bounds.

