Skip to main content
This guide covers changes to the beta Details component. These APIs may continue to evolve before the stable release.

Overview

The beta Details.Summary replaces children with a label string and lays the label and its attribute cells out on a shared grid:
  • label - The summary text. It supports *italic* and ==highlight== markup.
  • labelProps - Sets the label’s text size and adds an optional moreInfo tooltip.
  • attributes - Adds text, chip, button, and menu cells after the label.
  • templateColumns - Defines the grid tracks the label and cells share.
  • gap - Sets the space between those tracks.
These changes reflect our design philosophy of constrained content. Rather than accepting arbitrary React elements in the summary, the new API accepts a string and a fixed set of cells, so every summary keeps the same typography, alignment, and click behavior.

Design Rationale

Why label instead of children?

Previously, children accepted any React element, so each summary styled its own content:
The new approach takes a string for the label and moves everything else into typed cells:
The label renders as Text with the summary’s original typography, so a plain label looks the same as it did with plain-text children.

Why a grid?

The label and the cells are siblings in one grid, so a single templateColumns value sets the width of each. The label takes the first track, and each cell takes the next one. Use the same proportions as the content below to keep each cell near its column. The grid sits inside the summary’s padding, so the cells are offset by a few pixels from the content’s own columns.

Label Reference

The label is bold and uses the display font by default. Two inline markers are supported: Any other marker, including **bold** and backticks, renders as literal text. The label is already bold, so bold markup is not needed.

Label sizes

Use labelProps.size to change the label’s text scale. The expand arrow scales with the label at 1.5 times its font size. medium is the default.

More info

Set labelProps.moreInfo to a string to add an info button right after the label. Hovering or focusing the button shows the string in a tooltip.
The info button sits beside the toggle button, not inside it. Pressing it opens the tooltip and does not expand or collapse the Details, and keyboard users reach it with one Tab after the toggle. The button scales with labelProps.size. moreInfo takes a string only.

Best practice: use sentence case

Write labels in sentence case: capitalize only the first letter of the first word, plus proper nouns. Sentence case keeps labels easy to scan and matches the rest of Anvil2. The expand arrow aligns to the height of capital letters, so a label that starts with a capital letter sits most evenly beside it.

Attributes Reference

attributes takes an array of cells. Each cell has a type and renders after the label in the order given. Cells are aligned to the end of their track and do not wrap.

Text cells

label and value are both optional. The colon appears only when both are set, and an empty string counts as not set. size takes the same values as the label and defaults to medium.

Chip cells

label and color are the Chip props of the same names.

Button cells

A button cell is always a labeled button, so there is no icon-only form. appearance defaults to secondary. A bare icon renders before the label. Use { after: Icon } to place it after.
A menu cell takes items, each with a label, an optional onClick, and an optional disabled. Selecting an item runs its onClick and closes the menu. Submenus are not supported.

Placing cells on the grid

templateColumns defaults to 1fr, which gives the label the whole row. Add one track for each cell:
Size a track wider for a cell that needs more room. A cell beyond the last track wraps to a new row. gap takes the same values as the Grid component.

Click behavior

The whole summary row toggles the Details. Clicking the label, a text or chip cell, or empty space in the row opens or closes it. Button and menu cells sit beside the toggle button, not inside it. They run only their own action and do not toggle: pressing a button, opening a menu, or choosing a menu item leaves the Details as it is. Keyboard users toggle the Details with the toggle button, and tab to each button or menu cell as a separate control. The toggle button’s accessible name is the label alone.

Styling the summary

className and style apply to the summary row, so they style the whole summary: background, padding, border, and radius. Every other prop, including ref, onClick, aria-*, and data-*, goes to the toggle button. Override the summary’s colors with custom properties on the row, set through style:
Prefer style for these properties. A class with a single selector can lose to the component’s own rule. The focus ring is drawn around the whole row when the toggle button has keyboard focus. To style focus from your own className, use :has([data-anv="details-summary"]:focus-visible) on the row. Avoid :focus-within, which also matches when a button or menu cell in the row has focus.

Migration Guide

Plain text summaries

Summaries with inline emphasis

Summaries with extra elements

Move chips, buttons, and values out of children and into attributes:

Summaries with a different text size

Breaking Changes

These changes require code updates when you move to the beta Details. The stable Details is unchanged.
The following changes apply to the beta Details.Summary:
  • children has been removed - Use label instead.
  • className and style now apply to the summary row, not the <button> element - A rule that targeted the button itself, such as its display or :focus-visible, needs to target the row. Overrides that set padding, background, or the --a2-mod-details-summary-* custom properties work unchanged.
  • The label renders as Text - Content that relied on custom elements, fonts, or colors inside the summary must move to attributes cells or be dropped.

Why Breaking?

The point of the new API is a constrained summary. Accepting children alongside label would keep the arbitrary content the change removes, and beta status allows the API to change.

New Exports

The following are now exported from @servicetitan/anvil2/beta:
  • Details - The disclosure container, with Details.Summary and Details.Content.
  • DetailsProps - Props for Details.
  • DetailsSummaryProps - Props for Details.Summary.
  • DetailsSummaryLabelProps - The labelProps options.
  • DetailsSummaryColumnAttribute - An attribute cell.
  • DetailsSummaryTextAttribute - A text cell.
  • DetailsSummaryChipAttribute - A chip cell.
  • DetailsSummaryButtonAttribute - A button cell.
  • DetailsSummaryMenuAttribute - A menu cell.
  • DetailsSummaryMenuItem - An item in a menu cell.
  • DetailsContentProps - Props for Details.Content.
Last modified on October 29, 2026