C · Components
Recognizable, configurable controls
Composed from primitives, never domain-aware. Each section lists the element's configuration axes (the React props) and shows the states. Use "Show markup" on any specimen to see the exact HTML.
| Axis | Values |
|---|---|
data-variant |
soft (default) · filled · outlined |
data-color |
info (default) · primary · success · warning · error · purple · electric-blue · pink · yellow · brown |
data-size · data-shape |
sm 24px (default) · md 32px / square |
data-clickable · aria-pressed · aria-disabled |
hover / selected / disabled |
.qf-chip__delete · .qf-avatar |
deletable / leading avatar slot |
| Axis | Values |
|---|---|
data-size |
3xs · 2xs · xs · sm · md (default) · lg · xl · 2xl |
data-color · data-shape |
primary (default) · info · success · … / circle · square |
.qf-avatar__img · .qf-icon · text |
src / icon / initials |
.qf-avatar__status[data-status] |
online · busy · away · offline |
.qf-avatar-group · [data-overflow] |
stack with +N |
Hover/focus description with a 300ms enter delay. Lives in a Layer[kind=tooltip]; the static host shown here is CSS-only.
| Axis | Values |
|---|---|
data-placement · data-arrow |
top (default) · bottom · … / arrow |
data-variant |
dark (default) · light |
.qf-tooltip__header |
bold title line |
Anchored floating surface with arbitrary content. Uses the native popover attribute for
light-dismiss; only one InfoPopover is open at a time.
| Axis | Values |
|---|---|
.qf-popover__header · __body · __footer |
slots; body accepts data-padding="none" |
.qf-info-rows |
label / value grid for InfoPopover content |
- Type
- Invoice
- Date
- 2026-08-14
- Owner
- jdJane Doe
- Status
- Approved
Every large dismissible surface: modal, drawer, inline panel, full page. A native
<dialog> supplies the scrim and focus trap. The header carries the chrome tokens, so it
goes navy.
| Axis | Values |
|---|---|
data-presentation |
modal (default) · drawer · panel · fullscreen |
data-size · data-placement |
sm · md · lg · xl / right (default) · left · bottom (drawer) |
__header · __title · __body · __footer · __resize |
slots; body accepts data-padding="none" |
Confirm-before-act in three presentations: dialog (Overlay sm), popover, and button (a button that opens either). Never a raw dialog.
| Axis | Values |
|---|---|
data-presentation |
dialog · popover |
confirmText · confirmColor · loading |
label, color and loading state of the confirm button |
The control shell: label, helper/error text, adornments, sizing. Every input-like component (TextField, Select, DatePicker, InputFieldMapper) is a Field around a Control.
| Axis | Values |
|---|---|
data-size |
md 44px (default) · sm 32px |
data-label-placement |
top (default) · start |
data-required · data-disabled · data-readonly · data-invalid / aria-invalid |
state |
data-mode |
click-to-edit (rest state looks like text) |
__label · __control · __adornment · __helper · __counter |
slots |
All twelve free-text variants are a type on the Control plus an optional adornment. Nothing
else changes.
| Axis | Values |
|---|---|
type |
text · email · password · search · tel · url · number · currency · percent · textarea · masked · code |
Binary or tri-state choice as checkbox, switch or radio. The native input stays in the DOM (visually hidden) so forms and assistive tech work unchanged.
| Axis | Values |
|---|---|
data-appearance |
checkbox (default) · switch · radio |
data-size · data-label-placement |
md · sm / end (default) · start |
:checked · :indeterminate · :disabled |
native state |
.qf-toggle-group[data-orientation] |
vertical (default) · horizontal |
Every option-picking control: plain select, combobox, async suggest, multi-select. The trigger is a Field control; the listbox is a Menu. Gated on useCombobox (P-14) for behavior; the visual contract is complete here.
| Axis | Values |
|---|---|
mode |
select · combobox · suggest (async) · multi |
.qf-select__trigger[aria-expanded] |
chevron rotates |
.qf-listbox · __option[aria-selected] · [data-state=active] · __group · __empty |
listbox parts |
.qf-select__chips |
multi-select value chips inside the control |
| Axis | Values |
|---|---|
mode |
date · datetime · range |
.qf-calendar__day |
aria-selected · data-today · data-outside · data-in-range · data-range-start/end · :disabled |
A date the user already knows, such as a date of birth or the day a benefit started, typed into Month, Day and Year instead of picked from a calendar (USWDS Memorable date). A fieldset and legend around three Fields; for a date the user has to look up or choose, use DatePicker.
| Axis | Values |
|---|---|
data-month |
select (default, a native select of month names) · text (a two-digit box) |
data-required · data-invalid · :disabled |
state on the fieldset; an invalid box's Field carries data-invalid and its input aria-invalid="true" |
__legend · __hint · __error · __fields |
the question, "For example: January 19 2000", the message, and the row of boxes |
__month · __day · __year (each a .qf-field) |
Day and Year: inputmode="numeric", pattern="[0-9]*", maxlength 2 and 4; aria-describedby the hint (and the error) |
autocomplete |
bday-month · bday-day · bday-year on a date of birth only (1.3.5); none otherwise |
| Axis | Values |
|---|---|
data-state |
idle · active · reject · disabled |
data-compact |
single-row variant |
.qf-dropzone-overlay |
whole-page target for OS drags (overlayScope=page) |
Choose a sprite icon and its color, as for a saved search's sidebar row (Change icon… on its row menu, in a dialog titled "Icon for <name>"): a search field, the color keys as a ColorPicker row, a label naming the chosen icon, then the sprite's icons as a scrolling, keyboard-navigable grid. The chosen icon is drawn in the chosen color.
| Axis / element | Values |
|---|---|
__colors |
a .qf-color-picker of the ten color keys, flush with the field above |
__grid · __row · __cell |
role=grid · row · gridcell; command-height cells, ten to a row, seven rows tall then it scrolls |
__label · __empty |
"Icon: Users", labels the grid · "No icons match …" |
aria-selected · --_tint |
the chosen icon · the chosen color, set on .qf-icon-picker; the chosen icon is drawn in it |
A live checklist of what a Field's value must meet, such
as a new password's rules, ticked off as the user types (USWDS Validation). Each requirement says in text
whether it is met; one polite live region reports the count after the user pauses, never per keystroke.
The input names the checklist in aria-describedby.
| Axis | Values |
|---|---|
__item[data-state] |
unmet · met; the matching __unmet (circle) or __met (filled check) icon shows, and the visually hidden __state says "not met" or "met" |
data-invalid |
after a failed submit: unmet items take the error ink; pair it with the Field's data-invalid, aria-invalid and an error message |
__title · ul.__list[aria-labelledby] · __text |
"Your password must have", and one item per requirement |
__summary[role="status"] |
the one polite live region: "2 of 4 requirements met", written after a pause and only when the count changes |
input aria-describedby |
the checklist's id (after the error message's when invalid) |
Your password must have
- At least 12 characters, not met
- An uppercase letter (A–Z), not met
- A number (0–9), not met
- A symbol, such as ! or #, not met
Your password must have
- At least 12 characters, not met
- An uppercase letter (A–Z), not met
- A number (0–9), not met
- A symbol, such as ! or #, not met
Your password must have
- At least 12 characters, not met
- An uppercase letter (A–Z), not met
- A number (0–9), met
- A symbol, such as ! or #, not met
Your password must have
- At least 12 characters, met
- An uppercase letter (A–Z), met
- A number (0–9), met
- A symbol, such as ! or #, met
Your password must have
- At least 12 characters, not met
- An uppercase letter (A–Z), not met
- A number (0–9), met
- A symbol, such as ! or #, not met
| Axis | Values |
|---|---|
data-variant |
underline (default) · pill · segmented · vertical |
.qf-tabs__tab[aria-selected] · [aria-disabled] · __badge |
state and count |
The stepper is no longer a Tabs variant: a fixed, ordered process is shown with
StepIndicator (C-47), which marks the current
step with aria-current="step" and says in text which steps are complete.
Native <details>/<summary>. Exclusive open uses the
name attribute.
| Axis | Values |
|---|---|
data-variant |
default · separated · flush |
name |
same name on every item = exclusive |
General
Permissions
History
Separated item
Another item
| Axis | Values |
|---|---|
data-bordered · data-density · data-sticky |
full · bottom / compact / sticky top |
__group · __spacer · __title · .qf-divider[vertical] |
layout parts; overflow collapses tail groups into a Menu |
| Axis | Values |
|---|---|
mode |
pages · cursor (prev/next only) · load-more |
__page[aria-current=page] · __ellipsis · __info · __size |
parts |
| Axis | Values |
|---|---|
.qf-tree__node[aria-expanded][data-depth] |
expand state and indent level |
.qf-tree__node[aria-selected] · __toggle[data-leaf] · __count |
parts |
data-show-lines |
guide lines |
Where the user is in a fixed, ordered process, such as a multi-page application form: one segment per
step, and a "Step 2 of 5" heading naming the current one. Modeled on the USWDS Step indicator. It shows
status only; the pages themselves move the user on. It is the system's one stepper: the Tabs stepper
variant (.qf-stepper) folded into it.
| Axis | Values |
|---|---|
data-variant |
segments (default) · counters (a numbered circle on each segment) |
data-size |
counters only: md 2rem (default) · sm 1.5rem |
data-labels |
visible (default) · hidden (the heading names the step); below 30rem of width the labels hide on their own |
__segment[data-state="complete"] · __segment[aria-current="step"] · __segment |
complete · current · upcoming; each label ends in a visually hidden "completed" or "not completed" (none on the current step) |
__heading › __counter (__current · __total) · __heading-text |
the "Step N of M" heading, at the level the page needs |
- Personal information completed
- Household status completed
- Supporting documents
- Signature not completed
- Review and submit not completed
Step3of 5Supporting documents
- Upload completed
- Classify completed
- Index
- Review not completed
- Done not completed
Step3of 5Index
- Request completed
- Approval
- Fulfilment not completed
- Personal information
- Household status not completed
- Supporting documents not completed
- Signature not completed
- Review and submit not completed
Step1of 5Personal information
- Upload completed
- Classify completed
- Index completed
- Review
Step4of 4Review
- Personal information completed
- Household status
- Supporting documents not completed
- Signature not completed
- Review and submit not completed
Step2of 5Household status
Lets the user read the app in another language (USWDS Language selector). A button naming the current
language opens a Menu of languages, each written in
its own language and script with its lang, and glossed in the page's language; an app with
two languages uses a single link to the other one. UI only: choosing a language here changes the button,
not the page.
| Axis | Values |
|---|---|
| Form | menu (__trigger + __menu) · toggle (__toggle, a link to the other language) |
__trigger › __current[lang] |
a C-01 Button with aria-haspopup="menu"; a visually hidden "Language:" and the current language in its own name |
__menu › .qf-menu__item[role="menuitemradio"][aria-checked] |
one item per language; the current one checked |
.qf-menu__label[lang] · [dir="rtl"] · .qf-menu__description |
the language's own name, its lang (and dir for right-to-left scripts), and its name in the page's language |
__toggle[lang][hreflang] |
the other language's own name, as a link |
| Axis | Values |
|---|---|
data-divided · data-bordered · data-dense · data-interactive |
layout flags |
__item[data-state=selected|dragging] · [aria-disabled] |
row state |
__leading · __body · __primary · __secondary · __trailing · __handle · __header |
parts |
- Today
- jd Approve invoice INV-2026-0412Assigned by Jane Doe · Acme Holdings · due tomorrow High2h
- ak Index scanned batch #204114 pages · scan queue Medium5h
- mr Being draggeddata-state="dragging"
- Locked taskChecked out by another user
- Dense row1.2 MB
- Another dense row88 KB
| Axis | Values |
|---|---|
data-dense · data-striped · data-sticky-header |
flags |
th[aria-sort] · __sort · __resize[data-state=resizing] |
column state |
tr[data-state=selected] ([aria-selected] in a grid) · td[data-align=end] · [data-truncate] · __select · __actions · __footer |
parts |
| Actions | |||||||
|---|---|---|---|---|---|---|---|
| INV-2026-0412.pdf | Invoice | jdJane Doe | Approved | £4,250.00 | 2h ago | ||
| Q3-statement-acme-holdings-final.xlsx | Statement | akAlex Kim | In review | £12,080.10 | Yesterday | ||
| MSA-2026-draft-v4.docx | Contract | mrMaya Rossi | Draft | — | 3 days ago |
| Axis | Values |
|---|---|
data-orientation · data-density · data-elevation |
vertical · horizontal / compact / none · md |
data-clickable · data-state=selected |
interaction |
__media · __header · __heading · __title · __subtitle · __content · __actions · __footer |
slots |
Measures children and collapses the tail into a +N that opens a Popover. The static contract is
a clipped flex row with a trailing counter chip.
| Axis | Values |
|---|---|
data-orientation |
horizontal (default) · vertical |
__panel[data-collapsed] · __handle[data-state=dragging] · __grip |
parts and state |
A Banner in a Toast shell, queued in a fixed region. Preserves the imperative toaster.* API.
Five-second timer bar; dedupe by key.
| Axis | Values |
|---|---|
.qf-toast-region[data-position] |
bottom-right (default) · top-center · bottom-left |
.qf-toast[data-state=exiting] · __timer |
lifecycle |
A timed toast pauses while it is hovered or focused and resumes when the pointer and focus leave. A toast with an action has no timer: it stays until it is dismissed or its action is used.
| Axis | Values |
|---|---|
data-variant |
linear (default) · circular |
data-indeterminate · data-size · data-color |
flag / sm · md · lg / primary · success · warning · error · info |
style="--_value: 42" |
determinate value 0–100 |
| Axis | Values |
|---|---|
data-variant |
empty (default) · no-results · loading · error · no-access · offline · coming-soon |
data-compact · data-bordered |
inline size / dashed frame |
__icon · __title · __description · __actions |
slots |
C-42 – C-45 · Versioning
Versioning components
Save often, version on purpose. Saves keep the work without making a version; closing the editor makes a
Draft checkpoint (2.1, 2.2); Publish version makes the next Published version (3.0) and the previous one
Superseded. The same markup serves the QEditor title bar and panes (Fluent glyphs, see the
Publish version
and
Version history
mockups) and the qaction document screen (Tabler glyphs, below). Behaviour: js/versions.js.
The save indicator beside a document's name: Saving…, Saved, Offline, or Not saved while it retries, with Retry now. A save never makes a version, so the indicator never says one.
| Axis / element | Values |
|---|---|
data-state | saving · saved · offline · error |
data-placement · data-compact | titlebar (glyph takes the bar's ink; automatic inside .qf-overlay__header) / glyph only, words kept for screen readers |
__status (role="status") · __icon · __label · __detail | the live region and its parts |
__retry | Retry now, outside the live region; hidden unless data-state="error" |
The body of the Publish version dialog: the version it will create (2.3 → 3.0), what happens to the previous published version, warnings, and a required comment. Opened from the document screen and from File → Publish version in the editor; both end in POST /api/v2/documents/{id}/publish.
| Axis / element | Values |
|---|---|
__change · __from · __arrow · __to · __caption · __summary | 2.3 → 3.0, read as “From version 2.3 to version 3.0” |
__warnings | .qf-banners: checked out by someone else (error, blocks), under legal hold (warning, publishing is allowed), open in the editor (info); each banner present on open is named in the dialog's aria-describedby after __change, so it is read when the dialog opens |
__comment | a required .qf-field with a multiline control; data-invalid + aria-invalid when empty on submit |
[data-publish-submit] · aria-disabled | the footer's Publish version; disabled with aria-describedby naming the blocking warning |
Every version of a document, newest first, grouped by Published version with its Draft checkpoints beneath. Published rows are solid with a green edge, Superseded rows sunken and muted, Draft rows indented with a dashed edge; each also says its status in a badge. Published only hides the drafts. In the editor's side pane and on the qaction document screen.
| Axis / element | Values |
|---|---|
.qf-version-history[data-filter] | all (default) · published |
__toolbar · [data-version-filter] · __count | the Published only switch and the polite count |
__groups › __group (data-drafts-only) › __toggle · __drafts | one group per major number; the disclosure (aria-expanded, aria-controls) shows its drafts |
.qf-version[data-status] · aria-current · data-version | published · superseded · draft / the version that is open / its label |
.qf-version-history__head · .qf-hold-badge | the title row; under legal hold it carries the On hold badge, a warning .qf-chip with the gavel and its words, also used in a pane's .qf-object__meta |
__head · __label · __status[data-status] · __tag · __meta · __comment · __actions | row parts; actions carry data-version-action = open · compare · restore |
Version history
-
Version 3.0
PublishedLatest publishedApproved for release to the insurer.
-
Draft 3.2
DraftCurrent -
Draft 3.1
Draft
-
-
Version 2.0
SupersededAdds the moisture readings to section 2.
-
Draft 2.3
Draft -
Draft 2.2
Draft -
Draft 2.1
Draft
-
-
Version 1.0
SupersededCreated from the Harbourline site report.
Version history
On hold-
Version 2.0
PublishedLatest published
How long Draft checkpoints are kept after the next Published version, for one classification and document type pair: inherit the rule, a number of days, or keep all. Shows the effective rule and where it comes from (this pair, the classification, or the product default). Records managers edit it; everyone else sees it read only.
| Axis / element | Values | ||
|---|---|---|---|
__choice · __legend · __option · __hint · __days | a radio group; data-retention = inherit · days · keep-all on each radio | ||
__effective (a role="status" box around a | |||
data-inherited-value · data-inherited-source · data-pair-name | what the effective rule reads when the pair inherits | ||
__note · __actions | what is never pruned / Save |
C-30 – C-41 · Office
Office components
The ribbon family and the controls the QEditor editors add (QEditor spec §2.3), and the office variants of
existing components. Office icons come from sprites/office-icons.svg; specimens take the
Document accent unless they say otherwise. See them in context in the
office mockups.
The transparent command button of ribbons, menus bars, panes and status bars (QEditor spec §2.3 C11). sm is the Full small row (1.5rem), md the Compact row (2rem), lg stacked the Full large button.
| Axis / element | Values |
|---|---|
data-variant="command" | on .qf-button |
data-size | sm 24px · md 32px (default) · lg 66px |
data-layout="stacked" | icon over a two-line label (Full large) |
.qf-button__label · .qf-button__chevron | label · menu chevron |
aria-pressed · aria-expanded · :disabled | toggle · open menu · unavailable |
data-priority · data-collapsed | 1–4 · label / hidden (P-20) |
An access-key badge at the bottom centre of its target, shown by the KeyTips primitive (P-16) on Alt+Windows or Ctrl+Option. Badges are aria-hidden; the same keys are on the control as aria-keyshortcuts.
| Axis / element | Values |
|---|---|
.qf-keytip | one or two letters |
data-state="dimmed" | does not match the keys typed so far |
data-placement="start" | aligned to the start of the target |
The floating formatting bar over a selection or above a context menu: role="toolbar", two rows at most, and never the only path to a command (every one is also in the ribbon).
| Axis / element | Values |
|---|---|
.qf-mini-toolbar__row | one or two rows |
data-state="faded" | fades with pointer distance (not under reduced motion) |
data-static | in flow, for documentation |
One DOM for both modes (spec §1.2): every control is written once with data-priority and its label; data-mode picks Compact (the default: one row, labels visually hidden, priorities 3–4 in the ⋯ menu) or Full (three small rows or a large button per group, group labels, launchers). As the width shrinks, both modes shed labels, then move controls into the ⋯ menu by priority (last group first), then fold whole groups into dropdowns; groups never overlap or clip, and horizontal scrolling is only the last resort (P-20). The tab row takes the app accent and the panel stays flat.
| Axis / element | Values |
|---|---|
data-mode | compact (default) · full |
data-state | collapsed (show tabs only) · popup |
__bar · __file · __tabs · __tab · __contextual · __end · __search | the tab row |
__panel · __toolbar · __group · __group-body · __group-label · __launcher | the panel (role=tabpanel → role=toolbar → role=group) |
__stack · __rows · __row · __sep | Full layout of small controls |
__more · __display · __collapsed-group · __placeholder | overflow, display options, folded group, unbuilt tab |
data-ribbon-only | compact · full (a gallery in Full is a value button in Compact) |
Inline in the ribbon as a one-row role="listbox" with ▲ ▼ scroll buttons (out of the tab order) and an expand button; expanded as a role="grid" with labeled row groups and footer commands. Thumbnails are rendered by the engine from the document (spec §3.3).
| Axis / element | Values |
|---|---|
data-item-size | sm · md (default) · lg · wide |
data-columns | 4 · 5 (default) · 6 · 8 |
data-expanded | the popover form |
__viewport · __item · __thumb · __item-label | items |
__scroll-up · __scroll-down · __expand | inline controls |
__section · __section-label · __grid · __row · __footer | expanded parts |
Popover content for font, highlight, fill and outline colors: Automatic, the theme grid (10 × 6 tints and shades), the standard row, More colors…. Each cell is labeled ("Blue, Lighter 40%"); swatches keep their true color in forced colors with a CanvasText border.
| Axis / element | Values |
|---|---|
__grid · __row · __cell | role=grid · row · gridcell, --_swatch is document data |
data-variant="theme" | gap under the first row |
__label · __action · __preview | section titles · Automatic / More colors… · current/new pair |
aria-selected | the current color |
The Insert table size picker (10 × 8): arrows grow the selection, the live label names it, Enter inserts. The label is the grid's aria-describedby.
| Axis / element | Values |
|---|---|
__grid · __row · __cell[data-state="selected"] | the size grid |
__label | "4 × 3 table" |
The zoom slider (a detent marks 100%), transparency and brightness: role="slider" with aria-valuetext. The value is a 0–1 fraction in --_value.
| Axis / element | Values |
|---|---|
__track · __fill · __thumb · __detent | parts |
A complementary landmark (<aside aria-labelledby>) docked at the end (or the start: navigation, thumbnails, the PDF left pane). Opening moves focus to the title; closing returns it. Two end panes share a dock with a tab strip; under 48rem a pane becomes a drawer.
| Axis / element | Values |
|---|---|
data-side | start · end (default) |
data-presentation | docked (default) · card (floating card) · drawer |
__header · __back · __title · __actions · __toolbar · __body · __footer · __section-label | parts |
.qf-pane-dock · __tabs · __tab | two panes in one dock |
A footer region holding one role="toolbar": items are buttons when they open something (page → Go to, words → Word count), text otherwise; values are not live. The end holds the view radio set and the zoom control. It is not the app's page footer, which holds the version and legal links: that is Footer (C-51).
| Axis / element | Values |
|---|---|
__bar · __start · __end | layout |
__item · .qf-button[data-variant="command"][data-size="sm"] | text and actionable items |
__views · __zoom · __sep | view buttons · zoom (C-38) · separator |
data-priority | items that move out first at narrow widths |
Collaborators carry a ring in their presence color (--qf-presence-1…8) and a badge for viewing or editing; the name is always shown beside the avatar or in its tooltip, so hue never identifies a user on its own.
| Axis / element | Values |
|---|---|
data-presence | 1 … 8 |
.qf-avatar__badge | viewing (eye) · editing (pencil) |
The "super tooltip" of ribbon commands: title, shortcut, a one-paragraph description and, on a disabled command, the reason it is unavailable.
| Axis / element | Values |
|---|---|
data-variant="rich" | on .qf-tooltip |
__header · __shortcut · __description · __reason | parts |
The editable combobox of the ribbon and the formula bar: a text input with role="combobox" plus a chevron that opens the list. The roving toolbar yields ←/→ to the caret inside it.
| Axis / element | Values |
|---|---|
data-editable | on .qf-select |
data-width | font · size · zoom · namebox · format |
data-size | xs (ribbon) · sm (default) |
__input · __toggle | parts |
.qf-listbox__option[data-preview] | a font or style previewed in its own face |
Icon-only tabs with tooltips for the Format panes (Fill & Line, Effects, Size & Properties, Picture).
| Axis / element | Values |
|---|---|
data-variant="icon" | on .qf-tabs |
The PDF tool rail on narrow widths (data-orientation="vertical") and the annotation toolbar inside a popover (data-floating). Both are one tab stop with roving arrows (P-15).
| Axis / element | Values |
|---|---|
data-orientation="vertical" | a column of commands |
data-floating | a raised bar over the canvas |
CommentThread
C-41 Office Element pageA comment thread: the anchor quote, the author header (avatar, name, time, actions), the body, nested replies, the composer and the actions. In the pane, in the page margin next to its anchor, or as a popup over the page (PDF sticky notes).
data-statedata-placement__anchor-quote·__header·__who·__author·__time·__meta__body·__mention·__reply·__composer·__actionsHow do we know the seal failed suddenly? Underwriting will ask.
Added the moisture readings in §2.
Reviewed against the policy schedule.
Ask Northwind to split ceiling and walls before we approve.