uxskill
Star on GitHub
Language
English 简体中文 繁體中文 日本語 한국어 हिन्दी Bahasa Indonesia Tiếng Việt ไทย العربية Español Français Deutsch Português Русский Türkçe Italiano

## Why the breakpoint reflex is wrong

A media query asks the wrong question. It measures the viewport. But a card grid does not live in the viewport; it lives in a column, a modal, a sidebar, a dashboard cell. The same grid can sit in a 1200px hero one screen and a 320px drawer the next. A breakpoint tuned to the viewport has no idea which of those it is in, so it guesses, and the guess is wrong every time the component is reused somewhere it was not designed for.

AI tools reach for breakpoints anyway, because breakpoints are what the training data is full of. A decade of Bootstrap and Tailwind tutorials taught the model that "responsive" means a `md:` prefix. So the model ships `grid-cols-1 md:grid-cols-2 lg:grid-cols-3` on reflex, the layout looks fine in the preview, and it breaks the moment a human nests it.

## Two primitives that ask the right question

The fix is to let the content decide. Two CSS features do this without a single size threshold.

### 1. The auto-fit grid

Tell the grid a minimum comfortable column width and let it pack in as many as fit. No column count, no breakpoint.

    .grid {
      display: grid;
      gap: 1.5rem;
      grid-template-columns:
        repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
    }

One column in a 320px drawer, four in a 1200px hero, and every count in between, all from one rule. The `min(16rem, 100%)` is the part most hand-written grids miss: without it, a 16rem track is wider than a 320px parent and the layout overflows with a horizontal scrollbar. With it, the track caps at the parent width and the overflow is impossible.

### 2. The container query

When a component needs to change its internal shape, not just its column count, it should respond to its own box, not the page. Mark a parent as a query container and the child reacts to the space it actually has.

    .card-host { container-type: inline-size; }

    .card { display: grid; gap: 1rem; }

    @container (min-width: 28rem) {
      .card { grid-template-columns: 8rem 1fr; }
    }

Drop that card into a wide region and the image sits beside the text. Drop the identical card into a narrow rail and it stacks. The component is now portable: it carries its own responsiveness instead of depending on a viewport table maintained somewhere else. Note that `@container` is not a size-based `@media` query. It is the one query type that survives reuse.

## What the engine emits

This is the rule ux-skill follows when it composes layout. Its seven layout primitives (stack, cluster, grid, sidebar, cover, frame, and split) are built on `auto-fit minmax` and intrinsic, content-driven sizing, with container queries reserved for the components that need to change their internal shape. The generated layout CSS contains no size-based `@media` query at all. The only media queries that survive are the ones tied to user intent rather than screen width, chiefly `prefers-reduced-motion`, which is about a person's settings, not a breakpoint.

That constraint is testable, which is the point. The linter treats a viewport breakpoint on a component-level grid as a tell, the same way it treats Inter at 90px or a three-equal-card row. You can read every rule it checks at the anti-patterns catalogue, and the layout commands that produce this CSS are listed under commands.

## When you still want a media query

Container queries replace component breakpoints, not page architecture. A media query is still the honest tool for whole-page decisions: collapsing a top navigation into a drawer, switching a two-pane app shell to a single stack, or changing the page grid template. The rule is simple. If the decision is about the document, use a media query. If it is about a component that could appear anywhere, use a container query or an auto-fit grid and let the box decide.

> A breakpoint encodes an assumption about where a component will live. Container queries let it work without one.

## Try it

    pip install uxskill
    uxskill recommend --brief-file=.ux/last-discovery.json
    uxskill lint ./src --threshold high

The recommender returns layout primitives wired this way by default, and the linter flags component-level breakpoints on the way back. The source is on GitHub if you want to read the exact CSS the layout module produces.

## Related

- Why AI-generated dashboards all look the same, and the density fix
- The bento grid is the new three-card row
- The 7 axes of a design system, with the actual math
- A deterministic design engine that learns offline