Skip to content
← Doctrine

Components use components

Every control is assembled from other controls. Consistency is not something an author remembers to apply — it is built into the structure.

A Dialog contains a Button. That Button contains an Icon. The Icon is the same Icon that sits inside a Badge, a Menu item, an Alert. Nothing in this system is drawn from scratch at the point of use — each unit is composed from the smaller units beneath it, all the way down to a handful of primitives.

Because the pieces are shared, consistency is structural rather than disciplinary. A thing that looks like an input is the Input — there is no second way to render one. Spacing, focus ring, disabled state, the exact height at a given size: they arrive with the component, not from a convention someone has to hold in their head.

This is why output lands looking right no matter who wrote it. A human reaching for Button and a model reaching for Button get the identical artefact, because there is exactly one Button and it carries its own correctness. Composition, not review, is what keeps the surface whole.

What everything is built from

The units the most other units compose. Each count is a live edge in the manifest's composition graph — the number of documented components and compositions whose implementation imports this one.

  • Icon42 dependents

    Renders the given icon as a decorative SVG — always aria-hidden and non-focusable, with a fixed 1.5 stroke width.

  • Button21 dependents

    Button styled by a tone × emphasis grammar: tone sets the semantic colour (neutral / brand / danger) and emphasis sets the visual weight (solid / soft / outline / ghost / transparent / link). Any pairing is valid — there is no fixed "primary/secondary" list; those looks are expressed as tone × emphasis pairs. An icon-only button (an icon-* size, or icon with no children) requires an aria-label; a dev-only console warning fires when it is missing. With asChild, styling and icon/dot decorations are merged onto the single child via Slot.

  • Text13 dependents

    The typography primitive for body copy — renders a <p> (or any element via asChild). Ships without a line-length cap; add max-w-prose at the call site for long-form prose. Size defaults to sm and accepts responsive values: size={{ base: "sm", md: "lg" }}.

  • Dot9 dependents

    Dot component for indicating status, color, or presence. Uses semantic design token variants by default, with optional custom color override.

  • MenuItem8 dependents

    A standalone menu item component for use in popovers, sheets, and custom menus. This is the base component that DropdownMenuItem and other menu items build upon.

  • Stack8 dependents

    Flex container for arranging children in a line with consistent spacing. Layout props (direction, align, justify, gap) accept a single value or a per-breakpoint responsive object like { base: "column", md: "row" }; set responsiveMode to "container" to resolve breakpoints against the nearest container instead of the viewport. Stacks vertically by default.

  • Spinner7 dependents

    Loading spinner component. Animated circular indicator for async operations.

  • Popover6 dependents

    Root component for a popover (non-modal floating panel). Manages open/closed state.

One scale, everywhere

A single size scale threads through every control. Icon, Button, Badge and Input all resolve their height and glyph size from these eight tokens, so compositions align without anyone measuring.

SizeControl heightIcon
2xs
h-6
xs
h-7
sm
h-8
base
h-9
lg
h-10
xl
h-12
2xl
h-14
3xl
h-16

Composition runs one way

The building-on only ever points downward: primitives know nothing of the components that use them, components know nothing of the compositions above. That one-directional layering is a rule the build enforces — the full allowed-imports matrix lives on the doctrine page.