A rule between content groups.
Components
114 components in eleven families. Each ships a guide, a typed props contract, source, and a live card. Read the guide before you use one: it carries the rules the types cannot.
Primitives
Layout with no opinion about content. These own spacing, stacking and rhythm, and they draw almost nothing themselves.
Equal-width column layout. Tracks use minmax(0, 1fr) so a long word in one cell cannot blow out the row.
Horizontal layout primitive. Wraps by default, because a row of buttons that overflows on a phone is the most common responsive bug in a design system.
Empty space as an element. It exists for one job: pushing siblings apart inside a flex container.
Vertical layout primitive. It owns the space between its children, so nothing inside needs a margin.
Text that screen readers announce and sighted users never see.
A page section: the page column, the module padding above and below, and an optional surface. Its fill can span the screen or sit inset in the page column. With media it becomes a photo band. It is the scaffold both example sites built for themselves before it existed.
Typography
Headings and running text, set from the type roles. Level and size are separate props, so a page's outline and its visual scale can each be right.
A heading whose level and size are two props. level sets the tag, so the document outline stays honest; size sets the type role, so the page can still look the way it should.
Running text in one of the system's type roles: an eyebrow, a lead paragraph, body copy, small print, fine print, a label. It replaces the hand-written .eyebrow, .lead and .fine classes every brand otherwise writes for itself.
Actions
Everything a person can press. One visual hierarchy across all of them, so importance reads the same whether the target is a button or a link.
The system's action control. Use exactly one primary button per view. If two things are equally important, neither is.
Groups related buttons. Detached by default; attached joins them into a segmented control.
A button whose only content is an icon. label is a required prop and the type system enforces it.
Navigation. The rule: if it changes the page, it is a Link; if it changes data, it is a Button.
Forms
Inputs and the structure around them. Field owns the label, hint and error for every control, so validation looks and announces the same everywhere.
Binary choice inside a form, committed when the form is submitted.
A labelled set of checkboxes answering one question. Use it whenever two or more checkboxes belong together, because a loose column of Checkbox has no accessible name, so a screen reader reads the options without ever saying what they are for.
The wrapper that gives a control its label, hint, and error. Input, Textarea, and Select render their own Field when you pass them a label; use Field directly when you need to wrap something else.
Single-line text field. Builds its own Field when given a label.
One choice from a mutually exclusive set, with all options visible.
A labelled set of radios where exactly one option wins. Always use the group rather than loose Radio elements: it generates the shared name, applies role="radiogroup", and gives the set an accessible name.
Dropdown for a known set of options. Uses a native select, so it gets the platform's picker on mobile for free.
A range input for a value on a continuum where the approximate position matters more than the exact number: volume, opacity, a price ceiling.
An instant on/off setting. Flipping it applies the change; there is no Save button.
Multi-line text field.
A text input that filters a known list. Reach for it when a Select has grown past
Display
Read-only presentation of data that already exists. They render what they are given and never fetch, sort or filter it.
Identifies a person or entity at a glance. Use it in lists, tables, comment threads, and
Shows who is involved without spending a full row per person. Use it for project members,
A small, non-interactive label. Badges report state; they never do anything.
A content container. Card is the clearest demonstration of the context system: the same component is a bordered, flat panel in product and a borderless, softly raised content block in marketing.
Items moving on a path: a ring, a row, a stack that shuffles, a grid that trades places. Reach for it when a set of things should feel alive on a marketing page: a collection, proof, a run of features. Any component can be an item.
Marks text that must be read literally: token names, file paths, commands, snippets.
Turns a blank region into an instruction. Use it for first-run views, cleared filters,
Presents a sequence of comparable records: files, members, notifications, settings.
Holds the shape of content that is still loading, so the layout does not jump when data
Puts one number where the eye lands first. Use it in dashboard headers, summary bars, and
Compares records across several fields at once. Use it when the user reads across a row
An interactive chip. If it cannot be clicked or removed, it is a Badge.
Navigation
Moving between places, and showing where you are. Each one takes the current location as a prop rather than reading the URL, so they suit any router.
Tells the user where they are in a nested structure and gives them one click back up.
A site or app menu as one sheet: sections set large, a layer per section that slides in from the side, and search in the footer beside close. It grows out of the button that opened it and shrinks back into it. This is the menu the Dovetail site uses on a phone.
Top-level navigation for marketing pages and product shells with few destinations.
Moves through a result set one page at a time. Use it when the user needs a stable
Persistent navigation for products with more destinations than a Navbar can hold.
Shows how far through a sequential task the user is and how much remains. Use it for
Switches between sibling views that share a purpose. Use tabs when the user compares or
Switches between sibling views that share a purpose. Use tabs when the user compares or
The frame of a phone app: a top bar, a body that scrolls, and a bottom navigation. It honours the device's safe areas on every side, so nothing sits under the notch, the home indicator or a rounded corner in landscape.
A phone app's primary navigation: three to five destinations docked to the bottom of the screen, where a thumb can reach them, with the home indicator's safe area kept clear.
Feedback
Telling someone what happened, or asking before it does. Severity is a prop, and the overlays share one layer, focus trap and dismissal behaviour.
An inline message about the state of the page or a form. It sits in the layout and stays until the condition clears.
Page-level message about the state of the whole view: trial expiry, degraded service,
A modal that interrupts. Every dialog costs the user their place, so open one only for a decision that genuinely cannot wait.
Modal side panel for detail views, filters, and secondary forms that need more room than
A modal panel that rises from the bottom of a phone, inset from its edges, and opens as a centred dialog on a wide screen. It is the surface phone apps are built from: a detail view, a form, a picker, a settings page, or a sheet opened from another sheet.
Anchored panel holding secondary controls or detail: a filter set, a field explanation,
Shows how much of a known task is done. Use it for uploads, imports, quota meters, and
Signals an indeterminate wait where the resulting content has no known shape.
Confirms that something happened, out of the user's way. Use it for results of actions
Confirms that something happened, out of the user's way. Use it for results of actions
Names a control whose purpose is not obvious from its face, most often an IconButton.
What an assistant is doing while you wait: connecting, listening, thinking, searching or speaking. Five states, drawn as a fluid or as a texture of small marks, all read from the system's tokens, that sit inline in a chat or fill the screen for a voice session.
Content
Long-form and editorial shapes, including the pieces a CMS drives. Media reserves its space before it loads, so a page never jumps.
Collapses reference content so the page stays scannable. Use it for FAQs, spec sheets,
Reserves a fixed proportion of space before its content loads. Use it around every image, video, and map. Without it the page reflows when media arrives, which costs you layout stability and reads as a broken load.
Text laid over a full-bleed image. A marketing hero, a lifestyle poster and a social
Aside within documentation or long-form content. Use it to lift out a caveat, a
Pairs media with a caption. Use it in editorial and article layouts where the image carries information the surrounding prose does not.
A ratio-locked image with a built-in placeholder. Use it instead of a bare <img> anywhere in a template. It reserves space, crops predictably, and degrades to a labelled frame when the content system has not supplied a file yet.
A ratio-locked video, built the same way as Image and sharing its placeholder: reserve
One glyph from Dovetail Icons, the system's own library of 320 icons, to mark an action, a status or a kind of thing.
A media-and-copy row. This is the block a marketing or lifestyle page is mostly made of, so it is a component rather than something each template re-lays-out.
Wraps long-form editorial text (articles, documentation, changelogs) in a readable
Testimonial or pull quote. Uses figure and blockquote so the attribution is
Renders an array of content blocks through a registry of components. This is the whole
A social post drawn on an artboard at the format's native pixel size and scaled to fit wherever it is shown: an Instagram story (9:16, 1080 × 1920) or a grid post (4:5, 1080 × 1350, or 1:1, 1080 × 1080). A screenshot of the artboard at full size is ready to publish.
Commerce
Buying things: prices, products, carts, checkout and food ordering. Each renders what it is given and calls back on change, so the cart, the payment and the order stay in your app.
An amount of money, formatted for its currency and locale by Intl.NumberFormat, with an optional compare-at price for a sale and a unit such as "/ month".
A star rating: a read-only display of an average, with half stars and a review count, or, given onChange, a radio group the user sets.
A − value + control for choosing how many of something: a cart line, a menu item, a ticket count. The value can also be typed.
A product in a grid, a carousel, a list or search results: its photo, name, price and, optionally, rating, colours, a flag and an add action, with the whole card linking to the product page.
The images on a product page: a main image with previous and next buttons, a strip of thumbnails that choose it, and a counter.
Choose one variant of a product, such as a size, a colour or a material: a radio group of chips or colour swatches, or the system Select for a long list.
The top of a restaurant or store page: a cover, a logo, the name, a rating, a line of short facts, the delivery time and fee, and whether the store is open.
A segmented control for how an order reaches its buyer, delivery or pickup, with radio semantics and an optional detail line under each segment.
A titled group of menu items, such as "Starters" or "Noodles", laid out as a list or as a grid of cards.
One dish on a menu: the name, a two-line description, the price, dietary tags and a thumbnail, with an add button, a basket count and an optional stepper.
A group of options for a dish, such as "Choose a size" or "Add extras": radios for one choice or checkboxes for several, in full-width rows with the price change on the right.
One line in a cart, bag or order confirmation: a thumbnail, the item's name and variant details, a quantity stepper with remove, and the price. It is presentational and controlled: it shows what it is given and calls back, and holds no cart state, adds nothing up and talks to no server.
The money breakdown of a cart or checkout: subtotal, shipping, tax and discounts as a description list, an emphasised total, an optional free-shipping progress bar, and a footer for the checkout button. It is presentational: it prints the figures it is given and adds nothing up, so your cart or your commerce platform stays the source of truth.
A field for a promo, discount or gift code, with an Apply button. It starts as a "Have a promo code?" disclosure so it doesn't tempt people without a code to leave and search for one, shows the server's error under the field, and replaces the field with a removable chip once a code is applied. It is presentational and controlled: it never validates a code or calls a server; onApply hands the code to your app, and you pass back loading, error or applied.
A fieldset of postal address inputs, built from Input and Select, with the right autoComplete token on every field so browsers and password managers fill it in one tap. It is presentational and controlled: it reports each change as a whole Address, and never validates, looks up or sends the address.
Card entry fields (number, expiry, security code and name) that format as the user types and label the card brand. This is the UI only.
A timeline of an order's or a delivery's progress: completed steps with a check, the current step, and the steps still to come, with times and a line of detail. It can say an order is delayed or cancelled. It is presentational: it shows the steps and the current id you pass, and never tracks, polls or fetches anything.
The full-width bar at the foot of a phone ordering screen that says how many things are in the basket and what they come to, and opens it: "3 · View basket · $42.50". It renders nothing while the basket is empty.
Chat
Conversations, with a person or an assistant. Messages, presence and status are props; sending, storing and streaming stay in your app.
The bar above a conversation: who it is with, whether they are there, a way back, and a slot for a few actions.
The scrolling log of a conversation, kept pinned to the newest message unless the reader has scrolled up to read history. MessageDivider, exported from the same file, labels days and events across it.
The scrolling log of a conversation, kept pinned to the newest message unless the reader has scrolled up to read history. MessageDivider, exported from the same file, labels days and events across it.
One message in a conversation: sent on the right in the action colours, received on the left in a quiet surface, with its time, delivery status and, in groups, its author.
The field where a message is written, with an optional attach button and a send button. Enter sends, Shift+Enter adds a line, and the field grows with its text.
Three dots in a received bubble while the other side of a conversation is typing.
Suggested answers under a message, as a row of chip buttons the person can pick instead of typing.
A gradient border that turns around its container and answers to a voice conversation. The state says who has the floor and sets the colours. The voice's level brightens the glow behind the ring and speeds up the turn. VoiceInput and VoiceOverlay are built on it. Use it on its own to give any container the same voice treatment, such as a chat panel, a card or a call screen.
Voice input for a conversation, sized to sit in a page. It comes as a bar that takes the place of a composer, or as a panel that holds the exchange. An ambient border turns around it in the brand colour while the person speaks and in the secondary colour while the assistant answers, and its glow follows their voices.
A voice conversation over the whole screen. An ambient border runs round the edge of the screen and glows inward. The exchange is set large in the middle. The microphone and End sit at the foot, where a thumb reaches them. It's dark by default, so it looks the same over any page.
Blocks
Page sections that stack into a landing page. Each is a Section with its layout decided and its content as props, so a page is a list of blocks.
The top of a page. split sets the copy beside a picture and stacks them on a phone, centered centres it over the picture, and background turns the block into a photo band with the copy on a scrim.
A header over a grid of features: an icon on a brand tint, a title and a sentence each. Columns drop as the width runs out, down to one on a phone.
Copy beside media: a picture, a video, a product shot or a live component. The two sit side by side on a wide screen and stack on a phone; points become a checked list.
A few numbers that make the case, set large, each over a hairline, in a row that wraps on a phone.
What customers say. One quote is set large and centred; several sit in a grid on the card surface.
Questions before someone commits, in an Accordion. split puts the header beside the answers on a wide screen; stacked centres it above them.
The close of a page: one ask and its buttons, on the brand fill by default. With media the copy sits beside an illustration or product shot.
The eyebrow, title, lead and actions every block opens with, so a page of blocks keeps one rhythm. Use it to start a block of your own.
A whole conversation from data. Pass the messages as an array and the block draws the header, a scrolling log with day dividers, events and runs worked out for you, typing, quick replies and the composer, in one fixed-height panel.
A restaurant's ordering page body: the store's StoreHeader, an optional delivery or pickup FulfilmentToggle, a sticky row of category pills that follows the reader down the menu, and the menu as MenuSections of MenuItems. Reach for it for the main screen of a food ordering app or site.
The screen after checkout: when the order arrives, how far it has got, a map, who is bringing it, and what was ordered. Two columns when there is room (progress on the left, the order on the right), one on a phone.
A page section with a header over a responsive grid of ProductCards. Reach for it for a collection page, search results, or a "You may also like" row under a product.
The top of a product page: a ProductGallery on the left and a buy box on the right with the name, price, rating, description, variant pickers, quantity, a full-width add button and collapsible details. The two stack on a narrow screen, gallery first.
The cart page: the cart's lines on the left and an OrderSummary on the right with a promo field and the checkout action, sticky while a long cart scrolls. On a narrow screen the summary wraps below the lines. With no lines it shows an empty state.
A one-page checkout. The form (contact email, shipping address, delivery method, card details and the place-order action) sits on the left; the order, read-only lines over an OrderSummary, sits on the right and stays in view. When the block is narrower than collapseBelow the order moves to the top, folded into a "Show order summary · $141.09" disclosure, the usual phone pattern.