headless

package
v0.86.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 2, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package headless is the structure and accessibility half of a design system: the tags, the roles, the labelling relationships, the state attributes and the hooks a runtime binds to. It renders no classes and ships no CSS.

The split exists because structure and styling have different lifetimes and different reviewers. Whether a password field's reveal button announces what it will do next, whether a field's error is tied to its input by aria-describedby, whether a pager says which page is current — none of that changes when the palette does, and all of it is testable without rendering a pixel (a11y_test.go, harness_test.go). A class map is then free to be redrawn, or replaced entirely, without putting a single accessibility guarantee back at risk.

A component here is a pure function from its props and a Classes to HTML. The Classes decides what class each named Part carries; a nil Classes renders the same markup with no classes at all, which is what "headless" means and what the goldens pin. Seven things are named in a component's contract, and the harness checks each: its Parts, its runtime hooks (data-hui-*), what a caller may set on its Parts (Attrs, Slots, Binds), its Strings, and for a component that changes in-page state, an Island. See box.go, strings.go and island.go.

The package is SSR-first and hydrates incrementally, the same model as the rest of the framework (core-ui/ARCHITECTURE.md): first paint is the full markup, behaviour is armed by hook on arrival, and a state change is an island RPC on the element that keeps its href or action for a reader with no script. Its dependencies are core-ui/html, core/render and core-ui/interactive (the signal attribute allow-list a Bind is checked against), plus the agents inventory registration every framework subpackage carries.

The behaviour module exists: behavior.go registers it under the name "headless" through the same seam a stylesheet uses (registry.RegisterBehavior), the host serves it at /__gofastr/runtime/headless.js, and the kernel loads it when one of its markers is on the page. The styled layer's adoption has begun: framework/ui's Button family renders through this package dressed with the fui-button class map, and the remaining families follow in their own changes.

Index

Constants

View Source
const BehaviorName = "headless"

BehaviorName is the runtime module the hooks are bound by. The host serves it at /__gofastr/runtime/headless.js and the kernel loads it when one of its markers is on the page.

View Source
const CarouselBehaviorName = "headless-carousel"

CarouselBehaviorName is the runtime module that binds the carousel's data-hui-* hooks: the active slide, the controls, the status sentence, the auto-rotation with its pauses, and the keyboard. It replaces the retired core-ui/runtime carousel module.

View Source
const CollectionsBehaviorName = "headless-collections"

CollectionsBehaviorName is the runtime module that binds the tag input's and the repeater's data-hui-* hooks. It replaces the retired core-ui/runtime taginput and formrepeater modules.

View Source
const ComboboxBehaviorName = "headless-combobox"

ComboboxBehaviorName is the runtime module that binds the combobox's data-hui-* hooks and owns its keyboard contract. It replaces the retired core-ui/runtime combobox module; the RPC debouncing and the signal swap stay the kernel's data-fui-rpc contract.

View Source
const ControlsBehaviorName = "headless-controls"

ControlsBehaviorName is the runtime module that binds the stateful form controls' data-hui-* hooks. The host serves it at /__gofastr/runtime/headless-controls.js and the kernel loads it when one of its markers is on the page; it replaces the retired core-ui/runtime numberinput, slider, rangeslider and animatedcounter modules.

View Source
const DisclosureBehaviorName = "headless-disclosure"

DisclosureBehaviorName is the runtime module that binds the disclosure family's data-hui-* hooks (including the close-on-navigate for non-persistent disclosures). It replaces the retired core-ui/runtime disclosure module wholesale.

View Source
const FeedbackBehaviorName = "headless-feedback"

FeedbackBehaviorName is the runtime module that binds the feedback family's data-hui-* hooks and owns the toast stack runtime the kernel's response-header path dispatches into. It replaces the retired core-ui/runtime copy, toasts and networkretrybanner modules; the kernel's loadModule('headless-feedback') retarget is what keeps NS.toast, _initToasts and the X-Gofastr-Toast dispatch working.

View Source
const MenuBehaviorName = "headless-menu"

MenuBehaviorName is the runtime module that binds the menu's data-hui-* hooks and owns the menu keyboard contract. It replaces the retired core-ui/runtime menu module.

View Source
const MultiSelectBehaviorName = "headless-multiselect"

MultiSelectBehaviorName is the runtime module that binds the multiselect's data-hui-* hooks: the chips strip (rebuilt from the checkboxes' own state after every change), the chip remove buttons and the click-outside close. It replaces the retired core-ui/runtime multiselect module.

View Source
const NavigationBehaviorName = "headless-navigation"

NavigationBehaviorName is the runtime module that binds the page-level controls' data-hui-* hooks: the back-to-top link, the theme (colour-scheme) control group, and the page's keyboard shortcuts. It replaces the retired core-ui/runtime backtotop, themeswitch and shortcut modules.

View Source
const PaneHostBehaviorName = "headless-panehost"

PaneHostBehaviorName is the runtime module that binds the pane host's data-hui-* hooks: the open/close/swap lifecycle, the focus handoff and restore, the responsive drawer (its Tab trap over the kernel's focus selector and the kernel's refcounted scroll lock), the programmatic API and events, and the query deep link. It replaces the retired core-ui/runtime panehost module.

View Source
const RailBehaviorName = "headless-rail"

RailBehaviorName is the runtime module that binds the rail's data-hui-* hooks and owns the shared IntersectionObserver the table of contents arms through. It replaces the retired core-ui/runtime scrollspy module.

View Source
const SidebarBehaviorName = "headless-sidebar"

SidebarBehaviorName is the runtime module that binds the sidebar's data-hui-* hooks: the collapse state (persisting only when a storage key rides the root — server-owned otherwise) and the toggle. The mobile drawer is the widget runtime's. It replaces the retired core-ui/runtime sidebar module.

View Source
const SortableListBehaviorName = "headless-sortablelist"

SortableListBehaviorName is the runtime module that binds the sortable list's data-hui-* hooks: the drag-and-drop and keyboard reorder, the polite announcements (from Strings that travel as attributes), and the commit/rollback/versioned-409 conflict path. It replaces the retired core-ui/runtime sortablelist module.

View Source
const SpecimenGlyph render.HTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" ` +
	`stroke="currentColor" stroke-width="1.5" aria-hidden="true">` +
	`<circle cx="8" cy="8" r="6.25"/><path d="M5.5 8.25 7.25 10l3.25-3.5"/></svg>`

SpecimenGlyph is the icon a fixture draws when a component takes one.

It is a real 16×16 svg, because "<svg/>" is not. An svg with no width, no height and no viewBox has no intrinsic size, so CSS falls back to the replaced-element default of 300×150 — and every fixture in this package once used the short form. A badge meant to be a pill drew 340px wide and an alert's header grew a 140px hole between its title and its text.

Nothing in the markup was wrong. The classes were right, the parts were right, the audit was clean, and the page still looked broken — which is the whole reason a fixture has to be something a caller would plausibly pass, not the shortest string that type-checks.

View Source
const TOCBehaviorName = "headless-toc"

TOCBehaviorName is the runtime module that binds the table of contents' data-hui-* hooks. It replaces the retired core-ui/runtime toc module: the list is server-rendered from explicit items now, so what the module owns is only the active-entry state.

View Source
const TabsBehaviorName = "headless-tabs"

TabsBehaviorName is the runtime module that binds the tab strips' data-hui-* hooks and owns their keyboard contract. It replaces the retired core-ui/runtime tabs module (loaded there only through the prefetch bridge; here it is a registered behaviour on the strip's own marker).

View Source
const TreeBehaviorName = "headless-tree"

TreeBehaviorName is the runtime module that binds the tree's data-hui-* hooks: the roving tabindex, the arrow/Home/End/type-ahead keyboard contract, and the expand/collapse that drives the same toggle button a click drives. It replaces the retired core-ui/runtime tree module.

View Source
const WhenBehaviorName = "headless-when"

WhenBehaviorName is the runtime module that binds ConditionalField's data-hui-when regions, split from the headless module to keep both under the byte budget. The when hooks it owns moved with it.

View Source
const WizardBehaviorName = "headless-wizard"

WizardBehaviorName is the runtime module that binds the step wizard's data-hui-* hooks. No old standalone module existed: the plain POST wizard needed none, and this one owns only the island path's focus restore and announcement.

Variables

This section is empty.

Functions

func Alert

func Alert(p AlertProps, s Classes) render.HTML

Alert renders the message.

func Attrs

func Attrs(pairs map[string]string) html.Attrs

Attrs is a small builder: it drops empty values, so a component can declare every attribute it might set in one place and let the zero value mean "absent" rather than "present and empty".

Boolean attributes are the exception — an empty string IS the value for disabled, required and friends — so those go through Flag.

func BackToTop

func BackToTop(p BackToTopProps, s Classes) render.HTML

BackToTop renders the jump link.

The anchor is the whole contract: its href is the no-script destination, and the module that binds data-hui-back-to-top shows it past the threshold (through data-hui-back-to-top-visible, which no component renders — the module owns it), scrolls to the target, and returns focus to the link after the scroll. A stylesheet hides the link while the visible mark is absent, so the link appears when it is true and never flashes on a page still at its top.

func Badge

func Badge(p BadgeProps, s Classes) render.HTML

Badge renders a badge.

func Breadcrumbs(p BreadcrumbsProps, s Classes) render.HTML

Breadcrumbs renders the trail.

func Button

func Button(p ButtonProps, s Classes) render.HTML

Button renders the control.

func Card

func Card(p CardProps, s Classes, body ...render.HTML) render.HTML

Card renders a card around its body.

func Carousel(p CarouselProps, s Classes) render.HTML

Carousel renders the slider.

func Choice

func Choice(p ChoiceProps, s Classes) render.HTML

Choice renders label > input + span(text). The label wraps the control, so clicking the text toggles it with no for/id pair to keep in sync — and the wrap itself is the accessible target, which is why the control's own small box already passes WCAG 2.5.8.

func Cluster

func Cluster(p ClusterProps, s Classes, children ...render.HTML) render.HTML

Cluster renders a wrapping row.

A row of controls is not a Cluster: Toolbar aligns its children by construction, every child being control-height, and gives the search field the slack and the groups their labels. Cluster is for content that wraps — tags, badges, a byline's parts — where the row is a layout fact and nothing in it is a control.

func Color

func Color(p ColorProps, s Classes) render.HTML

Color renders the colour control as an affix shell: the native picker reduced to a swatch beside a readonly hex readout, so the field reads as one control-height input instead of a bare OS widget. The swatch carries no name and never submits; the hex text is the value and the swatch is a picker bound to it, which is also why the two never swap roles. The hex readout's tabindex stays 0 (it IS the control); the swatch is taken out of the tab order (tabindex -1) so there is exactly one focus target and one submitted value. There is no Required notion here because type=color always has a value; a required field that cannot bite would be a lie.

func Combobox

func Combobox(p ComboboxProps, s Classes) render.HTML

Combobox renders the search input with its listbox.

func ConditionalField

func ConditionalField(p ConditionalFieldProps, s Classes, children ...render.HTML) render.HTML

ConditionalField renders the region — VISIBLE.

It carries no hidden attribute, because hiding it here would make the dependent field reachable only after script had run: a page with script disabled, a reader mode, a crawler and a first paint before script arms would all see a field that never arrived. The module that binds data-hui-when hides and shows it as the watched field changes; the hiding is the platform's own hidden attribute, restated by a stylesheet at a specificity nothing here can beat.

It is a div and adds no semantics: the fields inside arrive with their own labels, and a region name would be read before each one.

func Container

func Container(p ContainerProps, s Classes, children ...render.HTML) render.HTML

Container renders the measure.

func Counter

func Counter(p CounterProps, s Classes) render.HTML

Counter renders the value between its two buttons.

func Describe

func Describe(id, hint, errText string) (describedBy, hintID, errID string)

Describe wires an input to its error and its hint by id, returning the aria-describedby value. This is the whole reason a field is a component and not three elements in a row: the relationship has to be built from the same ids the elements are given, in one place, or it silently rots.

The error comes first, so the correction is read before the rule it violated; both ids ride in one attribute whenever both are set — the hint is the rule the value must obey, and dropping it from the description exactly when it was broken is dropping it when the reader needs it most.

func DetailList

func DetailList(p DetailListProps, s Classes) render.HTML

DetailList renders the record as a <dl>.

The description list is the contract: a <dt> and the <dd> after it are one pair to assistive technology in a way a grid of divs never is, and the pair survives every restyling because the relationship is the element, not the layout. A row wrapper keeps each pair addressable for the class map without breaking that pairing — a div between dt and dd is valid HTML and keeps the pair's reading order intact.

func Disclosure

func Disclosure(p DisclosureProps, s Classes) render.HTML

Disclosure renders the native details disclosure.

func Divider

func Divider(p DividerProps, s Classes) render.HTML

Divider renders the line.

A meaningful one is an <hr>: the element already means "a thematic break", so no role has to be claimed. A decorative one is a div that says nothing, because the alternative — an <hr> with aria-hidden — is a semantic element being told to lie.

func El

func El(tag string, s Classes, p Part, own html.Attrs, children ...render.HTML) render.HTML

El builds one element: the part's class, then the caller's attrs, then children. Attrs the component owns always win over ExtraAttrs, which is why they are passed separately.

func EmptyState

func EmptyState(p EmptyStateProps, s Classes) render.HTML

EmptyState renders the nothing-here.

The root is role="region", because an empty result is a place the reader arrives at and needs to recognise — "this is the empty state, its name is the reason" — and an unnamed div is a place nothing can name. An explicit ID names the heading `<ID>-title` and points the region's aria-labelledby at it, so the name and the heading cannot disagree; without one the region is named by an aria-label equal to the Title and the heading carries no id — a title-derived id is not unique, and two empty panels with one title on a page would collide.

func Field

func Field(p FieldProps, s Classes, build func(FieldControl) render.HTML) render.HTML

Field renders the group. build receives the wiring and returns the control.

func FieldRow

func FieldRow(s Classes, fields ...render.HTML) render.HTML

FieldRow lays fields side by side.

func Fieldset

func Fieldset(p FieldsetProps, s Classes, fields ...render.HTML) render.HTML

Fieldset renders the group.

func FileUpload

func FileUpload(p FileUploadProps, s Classes) render.HTML

FileUpload renders the control.

The input is a real <input type="file">, visually hidden and fully present: focusable, labelled, keyboard-operable, and submitting with the form. The drop zone is its <label>, so clicking anywhere in the zone opens the picker with no script at all.

Dragging is an ENHANCEMENT and never the only route. WCAG 2.5.7 (Dragging Movements, AA since 2.2) says any drag action needs a single-pointer alternative, and the population that cannot drag — tremor, switch access, head pointer, touch with a stylus — is larger than the population that finds dragging convenient. Here the alternative is the same control: the zone is a label, so it is a click target before any script runs.

The chosen files are announced through a polite live region, because picking a file otherwise changes nothing a screen reader notices: the input's value is not read back, and the list of names appears silently.

func Flag

func Flag(a html.Attrs, name string, on bool) html.Attrs

Flag sets a boolean attribute when on.

func Form

func Form(p FormProps, s Classes, fields ...render.HTML) render.HTML

Form renders the form.

The interesting part is what happens after a failed submit. The server re-renders with Errors set; the module that binds data-hui-form-errors then moves focus to the summary, which is role="alert" and tabindex="-1". Without that move, a screen reader user is left at the top of an unchanged-looking page with no indication anything happened — the single most common way an accessible-looking form is not one.

func Gallery(p GalleryProps, s Classes) render.HTML

Gallery renders the image list.

func Grid

func Grid(p GridProps, s Classes, children ...render.HTML) render.HTML

Grid renders the auto-fitting grid.

func Group

func Group(p GroupProps, s Classes, items ...render.HTML) render.HTML

Group renders the fieldset. The legend is a real <legend> inside a real <fieldset>: that pair is the native group semantic, naming every control inside without a single aria attribute.

func Input

func Input(p InputProps, s Classes) render.HTML

func InputGroup

func InputGroup(p InputGroupProps, s Classes, children ...render.HTML) render.HTML

InputGroup renders the joined row.

It is a div by default and deliberately adds no semantics: the controls inside are already labelled, and wrapping two labelled controls in a group with a third name means a screen reader reads the group name before each one. A name is added only when the caller says the group needs one.

func Internal

func Internal(own html.Attrs) html.Attrs

Internal returns own with data-fui-internal set: the attribute an owned style's @scope stops at. A component puts it on each subtree that holds none of the caller's content (a header built from a Title string, a control's input, a dismiss button), and never on an element that holds a slot, or on any ancestor of one: content passed in stays in the owner's reach. The component's root is never marked; an owner may place it. A mark under another mark is inert. own is not modified; nil is fine.

func JSONTree

func JSONTree(p JSONTreeProps, s Classes) render.HTML

JSONTree renders the value as a collapsible tree.

func LightboxViewer

func LightboxViewer(p LightboxViewerProps, s Classes) render.HTML

LightboxViewer renders the open viewer's body.

The three rendered signals — alt on the visually-hidden title, src on the image's src attribute and (for the download anchor) its href, caption on the figcaption — are the lightbox widget contract's own names: the deeplink a trigger carries (src=…&alt=…&caption=…) lands in them, and a viewer that renamed them would render a widget that never updates. The group parameter is not rendered: the behaviour module reads it from the signal store, not from markup.

func Mark

func Mark(a html.Attrs, names ...string) html.Attrs

Mark sets attributes whose PRESENCE is the value: the data-hui-* hooks a runtime binds to, and the HTML attributes that work the same way — hidden, popover, open, inert.

It exists because Attrs drops empty values, which is right for "absent means unset" and catastrophic here: the component renders with every option set and the attribute missing, so it is styled, labelled, announced, and either wired to nothing or visible when it should not be. That shipped five times in the package this one grew from — a popover attribute, a copy hook, a drop list, repeater rows, and hidden on an empty message — every one of them built through Attrs, and the fifth AFTER this helper existed, because the helper was thought of as being for hooks. It is not: it is for any attribute whose empty string is meaningful.

func Menu(p MenuProps, s Classes) render.HTML

Menu renders the dropdown.

func Merge

func Merge(a, b html.Attrs) html.Attrs

Merge folds b into a, b winning. Used to layer owned attrs over caller extras.

func MultiSelect

func MultiSelect(p MultiSelectProps, s Classes) render.HTML

MultiSelect renders the checkbox-group disclosure.

func NotificationBell

func NotificationBell(p NotificationBellProps, s Classes) render.HTML

NotificationBell renders the trigger.

The trigger is an anchor with a real destination — the no-script page goes to the notifications page, the script-enhanced one opens the popover, and the same element is both. The count the anchor's name says is the count the badge shows, so the news and the number cannot disagree.

func NumberInput

func NumberInput(p NumberInputProps, s Classes) render.HTML

NumberInput renders the number field between its steppers.

func OptimisticAction

func OptimisticAction(p OptimisticActionProps, s Classes) render.HTML

OptimisticAction renders the button. The headless module binds it through the kernel's action primitive: endpoint, method and both label parts are the data-hui-action-* hooks below, and a non-2xx rolls everything back with the shake the class map's stylesheet may hang on data-state="error".

func Own

func Own(h render.HTML) render.HTML

Own marks every top-level element of h data-fui-internal. It is for markup a component builds from its own config and hands to another component as slot content: framework/ui's default banner glyph, a form's submit row, a field's control. The receiving component cannot tell that markup from a caller's, so the composer says it here, and a slot whose content is wholly Own'd counts as the component's own (see ownedSlot): the element holding it is marked too.

Top-level text cannot carry an attribute and is left as it is, which also leaves the slot reachable: a composer that wants its text internal wraps it in an element first. Elements already marked are left alone.

func PageHeader(p PageHeaderProps, s Classes) render.HTML

PageHeader renders the page top.

The element is a plain <header>: role="banner" is the top-level page header's to claim, and whether this header is that one is a decision the page makes, not the header — so the component claims no role and the browser's header semantics stand.

func Pagination

func Pagination(p PaginationProps, s Classes) render.HTML

Pagination renders prev / numbered pages / next. The current page carries aria-current="page" and the stylesheet styles the attribute, so state and appearance cannot disagree. Prev and next at the ends are disabled anchors: visible, named, and out of the tab order rather than gone.

The nav landmark itself is PartRoot and carries no class; the list inside it is PartPagination, which is where a caller's Class has always landed.

func PaneHost

func PaneHost(p PaneHostProps, s Classes) render.HTML

PaneHost renders the shell.

func Password

func Password(p PasswordProps, s Classes) render.HTML

Password renders the affix shell: a div carrying the runtime's data-hui-affix hook, with a borderless input and a reveal button inside. The shell owns the one border, so nothing stacks a border on a border.

The reveal button is complete, correct markup — type=button so it never submits, an aria-label — but it is INERT until a runtime module exists to toggle the input's type and the button's own label. Nothing is wired on purpose: no inline script, no dead onclick, and no pretending it works before it does.

func Progress

func Progress(p ProgressProps, s Classes) render.HTML

Progress renders the bar.

func Rail

func Rail(p RailProps, s Classes) render.HTML

Rail renders the sticky in-page navigation.

func RangeSlider

func RangeSlider(p RangeSliderProps, s Classes) render.HTML

RangeSlider renders the two-thumb range pair.

func Rating

func Rating(p RatingProps, s Classes) render.HTML

Rating renders the radio group.

The fieldset/legend and the radio inputs ARE the contract: native arrows move between choices, the form POSTs the chosen value, and every choice is named ("3 out of 5") — which is why no behaviour module exists for it. A second state owner would add nothing the radios do not already guarantee.

The choices render in REVERSE order (Max..1): the sheet's sibling selector cascades the highlight backward from the checked or hovered choice to every earlier one, with no script at all.

func Register

func Register(sp Spec)

Register adds a component to the harness. Called from each component's own file, so the fixture lives beside the thing it describes and moves when it moves.

func Repeater

func Repeater(p RepeaterProps, s Classes) render.HTML

Repeater renders the repeated rows with their add and remove controls.

The rows are ordinary form fields and the controls are named submit buttons, so the no-script page is not a degraded page: add and remove submit the surrounding form with the operation in the request, and the server re-renders. With an Island the same buttons carry the framework's RPC contract beside their submit semantics, the region swaps in place, and the module restores focus to the row's first control (a removal) or the add control (an addition).

func Safe

func Safe(extra html.Attrs, owned ...string) html.Attrs

Safe copies caller-supplied extras, dropping the keys a component owns so no caller can break its structure or its labelling, and the keys refused wherever a caller's attributes come in (see refused). Keys are stored folded, the way the browser reads them: NAME and name are one attribute, and stored as written a caller's NAME sorted ahead of the component's name, so the browser kept the caller's. One key under two spellings is refused rather than left to map order.

func Section

func Section(p SectionProps, s Classes, children ...render.HTML) render.HTML

Section renders the region.

func Select

func Select(p SelectProps, s Classes) render.HTML

Select renders the native dropdown. The chevron is the stylesheet's (appearance: none plus a drawn arrow), so the markup stays a plain select — no wrapper div to align against its neighbours.

func Sidebar(p SidebarProps, s Classes) render.HTML

Sidebar renders the shell: the data-hui-sidebar root the module owns, the drawer trigger, the inline column with the collapse toggle, and the region (see SidebarRegion) inside it.

func SidebarDrawerTrigger

func SidebarDrawerTrigger(p SidebarProps, s Classes) render.HTML

SidebarDrawerTrigger renders the drawer-opening hamburger button on its own, for hosts that place it in their own chrome (the page header) instead of above the sidebar's inline column — pair it with SidebarProps.HideDrawerTrigger so the shell does not draw a second one. Same button and widget contract as the shell's own trigger (data-fui-open names the drawer widget); the styled component's class map carries the variant class the sheet's >= md hiding keys on — the button IS the component's root, so root overrides and binds land on it. Empty DrawerName renders nothing.

func SidebarRegion

func SidebarRegion(p SidebarProps, s Classes) render.HTML

sidebarDrawerTrigger is the one implementation of the hamburger: SidebarRegion renders the navigation content alone — the title, the prepend slot, the nav landmark with the items, the footer — with no data-hui-sidebar shell hooks: a host slotting the region into its own chrome (a drawer body, a pinned panel) calls this directly, and the module never treats it as a sidebar root.

func Skeleton

func Skeleton(p SkeletonProps, s Classes) render.HTML

Skeleton renders the placeholder.

The bars are aria-hidden, every one of them. A skeleton is a picture of content that does not exist yet, and a screen reader reading out eight empty boxes — or worse, announcing each shimmer as it animates — is strictly worse than silence. What it does instead is say "Loading apps" once, politely, and then wait.

This is also why the bars are not <p> or <div> full of nbsp: there is no text to read, so there should be no text.

func Slider

func Slider(p SliderProps, s Classes) render.HTML

Slider renders the labelled range control.

func SortableItems

func SortableItems(p SortableListProps, s Classes) render.HTML

SortableItems renders just the row elements without the <ol> wrapper, the fragment a conflict-recovery endpoint returns to replace a list's contents with the server's own rows. Empty items render an empty fragment: authoritative reconciliation may empty a column.

func SortableList

func SortableList(p SortableListProps, s Classes) render.HTML

SortableList renders the list.

func Spacer

func Spacer(p SpacerProps, s Classes) render.HTML

Spacer renders the space.

It is a <span>, not a div, because a contents list is a paragraph ("Restarts … 263" is one sentence of a list) and a div inside a <p> is a parse error the browser repairs by closing the paragraph — which splits the list into pieces nobody styled. It is aria-hidden and empty: the space is the whole content, and a screen reader user gets the term and the value as neighbours, which is the same fact the leader line draws for the eye.

func Spinner

func Spinner(p SpinnerProps, s Classes) render.HTML

Spinner renders the indicator.

role="status" rather than role="progressbar": a progressbar promises a value, and a spinner has none — that is what makes it a spinner

func Stack

func Stack(p StackProps, s Classes, children ...render.HTML) render.HTML

Stack renders vertical flow.

func StatCard

func StatCard(p StatCardProps, s Classes) render.HTML

StatCard renders the metric.

Label, value and trend are read in that order and the label is first for the same reason a form label precedes its control: the name arrives before the number, so "MRR: 48,200" is one fact instead of a number the reader has to look up.

func StepWizard

func StepWizard(p StepWizardProps, s Classes) render.HTML

StepWizard renders the form, its rail and its controls.

func Steps

func Steps(p StepsProps, s Classes) render.HTML

Steps renders a progress rail — a list of states, not a set of controls. Each step's data-state drives both its marker and the connecting line the stylesheet draws between markers, so the rail cannot disagree with the states; the current step also carries aria-current="step" for AT. Exactly one step may be current: an explicit State "current" on a step other than Current's is refused, because two current steps tell assistive technology the flow is in two places at once.

func Switch

func Switch(p SwitchProps, s Classes) render.HTML

Switch renders a checkbox that looks like a track-and-thumb. The input keeps type=checkbox — it still submits like one — and role= switch states the shape to assistive tech. The track, the thumb and the motion are the stylesheet's, keyed off :checked, so nothing in this markup can fall out of step with the state.

func SystemBanner

func SystemBanner(p SystemBannerProps, s Classes) render.HTML

SystemBanner renders the message.

role="status" rather than alert: a system message is important and must not interrupt. It is on screen at the top of the shell, and a polite region is read at the next pause — announcing itself over whatever the reader was doing would make the top of every page a shout. The offline banner is the one exception, and it is the exception because losing the connection is the one system message worth interrupting for: everything the reader does next will fail until it is back. It carries role="alert" and aria-live="assertive" both, as the framework banner it replaces did, so either attribute alone still says how urgent it is.

func Table

func Table(p TableProps, s Classes) render.HTML

Table renders the list: one wrapper div (PartRoot) holding a focusable scroll region (PartScroll) around the table, and the footer, when there is one, as the region's sibling. The scroll region is the element a class map makes the horizontal scroll surface — the one a wide table on a narrow screen scrolls inside — and it is markup, not styling, because a scroll region that cannot take focus cannot be scrolled by keyboard.

The explicit ARIA roles stay on every element. They look redundant on a displayed <table> and are not: a cards collapse sets display:block on the table's elements, and a table element displayed as a block loses its implicit table semantics in Chromium and WebKit. The roles are what keep a collapsed table a table for assistive technology.

aria-sort is the only sort indicator rendered — ascending and descending on the active column, none on every other sortable one — and the stylesheet draws from the attribute, so state and appearance cannot disagree.

func TableOfContents

func TableOfContents(p TableOfContentsProps, s Classes) render.HTML

TableOfContents renders the contents navigation.

func Tabs

func Tabs(p TabsProps, s Classes) render.HTML

Tabs renders the tab strip.

func TabsMaxPanels

func TabsMaxPanels() int

TabsMaxPanels exposes the ceiling to the styled layer, whose generated CSS covers exactly this many indices.

func Tag

func Tag(p TagProps, s Classes) render.HTML

Tag renders a chip, optionally dismissible.

func TagInput

func TagInput(p TagInputProps, s Classes) render.HTML

TagInput renders the chips, the draft input and the add control.

The chip list is a real list (role="list" of items) rather than a strip of hidden inputs the runtime converts on arrival: the values are visible on the first paint, each with its own named remove control, and the hidden inputs beside them are what the form submits. Enter and comma commit the draft; Backspace on an empty draft removes the last chip; the add control commits it for a reader whose keyboard has no handy Enter (a tablet's on-screen one) — all bound by the module, none of it required for the values already committed to be real.

func Textarea

func Textarea(p TextareaProps, s Classes) render.HTML

Textarea renders the multiline control. The value is the content, not an attribute: setting both is what makes some browsers show the stale attribute after a reset.

func Timeline

func Timeline(p TimelineProps, s Classes) render.HTML

Timeline renders the events as an ordered list.

Ordered, because the order is the content: these things happened in this sequence, and <ol> is what says so — a screen reader announces the count and the position, so "3 of 7" locates you in the history without seeing the line down the left.

The dots and the connecting line are aria-hidden. They are a picture of the ordering that the list already states, and announcing them would mean hearing "bullet" before every entry.

func Toast

func Toast(p ToastProps, s Classes) render.HTML

Toast renders one notification row.

func ToastStack

func ToastStack(p ToastStackProps, s Classes) render.HTML

ToastStack renders the region the toasts live in.

The stack carries the framework's data-fui-toast-stack name beside its own: the kernel's response-header toast path and the runtime's auto-mount look for the framework's name, and the module that owns the component lifecycle looks for this package's. One region, two contracts, because the kernel's half is infrastructure this package does not own.

func ToggleAction

func ToggleAction(p ToggleActionProps, s Classes) render.HTML

ToggleAction renders the button. The headless module binds it through the kernel's action primitive, ships nothing itself but the initial state below, and mirrors committed onto aria-pressed from then on.

func Toolbar

func Toolbar(p ToolbarProps, s Classes, children ...render.HTML) render.HTML

Toolbar renders a row of controls that aligns by construction: every child is control-height, so nothing needs aligning to anything else. Build children from ToolbarGroup, ToolbarSpacer and ToolbarSearch.

func ToolbarGroup

func ToolbarGroup(s Classes, label string, children ...render.HTML) render.HTML

ToolbarGroup clusters related controls under a visible label. An empty label omits the label span; the cluster remains.

func ToolbarSearch

func ToolbarSearch(p ToolbarSearchProps, s Classes, child render.HTML) render.HTML

ToolbarSearch wraps the search field, the one child allowed to take the row's slack, in the GET form that submits it.

func ToolbarSpacer

func ToolbarSpacer(s Classes) render.HTML

ToolbarSpacer pushes everything after it to the far end of the row.

func Tree

func Tree(p TreeProps, s Classes) render.HTML

Tree renders the treeview.

func ValidationSummary

func ValidationSummary(p ValidationSummaryProps, s Classes) render.HTML

ValidationSummary renders the summary that goes above a form.

This is the single highest-value accessibility component in a form, and the reasoning is worth stating in full.

When a form fails validation, a sighted user sees red appear near the fields. A screen reader user, unless told, hears nothing: focus is wherever it was, the page looks the same to the accessibility tree except for text that changed somewhere below. So the summary:

  • is role="alert", which interrupts — this DID just happen, it is the one case where interrupting is correct;
  • is tabindex="-1", so the server can send focus to it after a failed submit. Not focusable-by-tab, focusable-by-script: it must never become a tab stop for someone filling in the form;
  • lists each error as a LINK to the field, because the value of the summary is getting to the field, not reading the list. The link moves focus to the control itself, so the next thing the user types goes in the right box.

Rendering it with no errors renders nothing: an empty "there is a problem" box that announces itself is a lie that interrupts.

Types

type Action

type Action = html.Attrs

Action is the wiring a component admits through its request seam: the same type Button's Action prop takes, named here so a form's Request reads as what it is. Every key is checked for what it deserves at render; see formRequestAttrs.

type AlertProps

type AlertProps struct {
	// Tone names the kind of message: the class map turns it into colour
	// through the root's "<part>--<tone>" variant. It is passed
	// through, not interpreted; the tone word is what carries it to a
	// reader.
	Tone string
	// ToneWord is the tone in words — "Error", "Warning", "Success".
	// It is rendered for assistive tech and hidden visually, and it is
	// the reason this component satisfies WCAG 1.4.1: colour may not
	// be the only thing carrying meaning, and a red box is exactly
	// that to anyone who cannot see the red.
	//
	// Empty means the Title already says which kind it is — "Deploy
	// failed" needs no "Error:" in front of it — so the word is
	// skipped rather than duplicated.
	ToneWord string
	// Title is the headline. Required: an alert with no headline is a
	// coloured paragraph.
	Title string
	// Text is the detail, in prose.
	Text string
	// Body is the detail as markup — a list, a code sample, nested
	// content that is not an action. It renders after Text; prose
	// belongs in Text, and actions in Actions.
	Body render.HTML
	// Icon is decorative. The tone word carries the meaning.
	Icon render.HTML
	// Actions are the controls: retry, view logs, dismiss.
	Actions render.HTML
	// Live says whether this interrupts. See Live.
	Live Live
	// Focus moves focus here when the page loads.
	//
	// It is how a message that is ALREADY THERE gets announced. A live
	// region announces changes; content present when the document
	// loads is not a change, and role="alert" at load time is reported
	// inconsistently across screen readers. After a form posts and the
	// server redirects — the shape of every action in a
	// server-rendered app — the confirmation is present at load, so
	// the only reliable way to say it is to put the reader on it.
	Focus bool
	// DismissHref makes the alert dismissable with a link that keeps a
	// real href, so dismissing needs no script and survives the page
	// being reloaded. The label names what is being dismissed, because
	// "Dismiss" three times in a row tells a screen reader user
	// nothing.
	DismissHref  string
	DismissLabel string
	// Island is where the dismiss goes with script: dismissing is an
	// in-page state change, so the × carries the RPC contract beside
	// its href. Required when DismissHref is set; ignored otherwise.
	Island Island

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part. Nothing here is
	// fillable — Actions already takes the page's own controls, and
	// everything else an alert draws is what a screen reader is given
	// to tell one alert from another.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

AlertProps is a message about something that happened, or is true.

type BackToTopProps

type BackToTopProps struct {
	// Href is where the link goes: a same-origin anchor, usually the
	// top of the page or the main content's id. Required — a jump
	// control with no destination is a button pretending to be one,
	// and the no-script page would carry a dead link.
	Href string
	// Target, when set, is the element id the module reads the scroll
	// sentinel from and scrolls to. An id, not a selector: the module
	// resolves it, and an id here cannot become a query the way a
	// selector string can.
	Target string
	// Label is the accessible name. Empty takes Strings.BackToTop;
	// the glyph alone names nothing.
	Label string
	// Icon is the glyph. The default arrow is the structure's own; a
	// caller's SVG replaces it.
	Icon render.HTML
	// Threshold is the scroll offset in CSS pixels past which the
	// module shows the link. Zero takes the module's default;
	// negative is refused.
	Threshold int
	// Smooth asks for smooth scrolling. The module downgrades to an
	// instant jump under reduced motion; the preference is honoured,
	// not overridden.
	Smooth bool

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root, the icon and the label.
	// Strings are not read — the name is the caller's Label.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

BackToTopProps configures the page's way back to its top.

type BadgeProps

type BadgeProps struct {
	// Label is the visible text, and the whole of what a screen reader
	// hears. Required.
	Label string
	// Icon renders before the label, and is decorative: the label is
	// the accessible name, so an icon that repeated it would be read
	// twice. Anything the icon means that the label does not say
	// belongs in the label.
	Icon render.HTML
	ID   string

	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root and the icon.
	Parts Parts
}

BadgeProps is a badge: a small status chip, not a fill. A badge has no tone of its own and says none: its label IS its meaning ("running", "3 unread", "beta"), and the tone a class map variant paints it is decoration for the word already there. An Alert prefixes its tone because its title may not say it; a badge whose colour means something its label does not say has the wrong label.

type BadgeTone

type BadgeTone string

BadgeTone selects a badge's colour. It is class-map vocabulary: the structure does not care, the class map looks it up.

type Bind

type Bind struct {
	// Signal is the signal's name. Required.
	Signal string
	// Mode is "text" (the default: the part's text is the value),
	// "html" (the part's HTML is the value, the trusted path an island
	// fragment uses) or "attr" (one attribute is the value).
	Mode string
	// Attr is the attribute for mode "attr", and must be one the
	// framework lets a signal write: any aria-*, or its allow-list.
	// Empty otherwise.
	Attr string
}

Bind keeps a part in step with one of the host framework's client signals: its runtime rewrites the part's text, its HTML, or one attribute whenever the signal changes. It is the third thing a caller may set on a part, beside slots and attrs, because a binding is neither: an attribute may not carry a data-fui-* key, on purpose, so the only way a part can follow a signal is to say so here, where it is typed, reviewable, lands on exactly one element, and cannot reach an attribute the runtime would execute.

type Binds

type Binds map[Part]Bind

Binds maps parts to the signal each follows.

type Box

type Box struct {
	Classes Classes
	Slots   Slots
	Over    PartAttrs
	Binds   Binds
	// contains filtered or unexported fields
}

Box carries the three layers through a component's render. A zero Box with only a Classes behaves exactly as El always did, which is why adopting it is a per-component change and not a rewrite.

func Boxed

func Boxed(s Classes, slots Slots, over PartAttrs) Box

Boxed is the constructor a component calls with whatever its props carry.

func (Box) El

func (b Box) El(tag string, p Part, own html.Attrs, children ...render.HTML) render.HTML

El renders one element with the part's class, the caller's attrs for that part, then the component's own attrs — in that order, so the component wins.

func (Box) Fill

func (b Box) Fill(p Part, def render.HTML) render.HTML

Fill returns the caller's content for a part, or the component's own when there is none.

func (Box) FillableOn

func (b Box) FillableOn(parts ...Part) Box

FillableOn names the parts whose content a caller may replace — the same parts the spec lists as Fillable — so a text or html Bind can be refused on every other part at render, where the mistake is a panic with a reason. A Bind that rewrites content is a Slot with a timer: allowed where Slots are, and nowhere else.

func (Box) Filled

func (b Box) Filled(p Part) bool

Filled reports whether a slot was supplied. A component uses it when the presence of content changes the structure — a footer that is not rendered at all rather than rendered empty.

type Breadcrumb struct {
	// Text is the step's visible label. Required.
	Text string
	// Href is the step's destination. Empty on the last step (the
	// current page names itself, it does not link to itself); a value
	// on the last step links it unless Current is also set.
	Href string
	// Current marks the step as the current page whatever its Href:
	// rendered as text with aria-current="page" rather than a link,
	// for a page that appears in its own trail with a link.
	Current bool
}

Breadcrumb is one step in the trail.

type BreadcrumbsProps struct {
	// Label names the navigation landmark. Empty takes
	// Strings.BreadcrumbsLabel.
	Label string
	// Items are the steps, shallowest first. Required and non-empty:
	// a trail with no steps is a landmark that says nothing.
	Items []Breadcrumb

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root and the list. No part is fillable: the
	// trail is the contract.
	Parts   Parts
	Strings *Strings
}

BreadcrumbsProps configures the trail.

type ButtonProps

type ButtonProps struct {
	// Label is the visible text. An icon-only button leaves it empty
	// and sets AriaLabel; a button with neither has no accessible name
	// and is refused.
	Label     string
	AriaLabel string
	Icon      render.HTML
	// Suffix renders after the label — a keyboard hint, a count. It is
	// the caller's markup, so whether it is announced is the caller's
	// decision: a keyboard hint hides its glyphs and supplies words, a
	// badge announces itself.
	Suffix render.HTML
	// Variant and Size are class-map vocabulary, passed through so the class map
	// can look up "<part>--<variant>". The structure does not care.
	Variant string
	Size    string
	// Disabled is a real state here. A disabled anchor is not a thing
	// in HTML, so Href + Disabled drops the href and says so with
	// aria-disabled rather than leaving a live link that looks dead.
	Disabled bool
	Type     string
	Href     string
	// External, with Href, opens the link in a new tab with
	// rel="noopener noreferrer" and owns target and rel: a caller's
	// spelling of either — folded or not — cannot clobber the noopener
	// contract.
	External bool
	// PopoverTarget names a popover this button opens. It is also what
	// gives that popover its invoker, which is how the runtime places
	// the panel beside this button.
	PopoverTarget string
	// HasPopup, when set, is the aria-haspopup value ("menu", "dialog").
	HasPopup string

	// Action is what the button DOES when the host framework's runtime
	// is on the page: the data-fui-rpc attributes of one of its
	// actions, as its Attrs() returns them, one of its local signal
	// mutations (set, increment, toggle), or one of the wiring keys a
	// page can put on any clickable — opening a widget or pane, firing
	// a toast, writing the URL, deep-linking an open, prefetching a
	// runtime module. It is a seam of its own rather than a use of
	// ExtraAttrs, because ExtraAttrs is for what a page knows and a
	// component cannot — a test id, a title — and Safe drops every
	// data-fui-* key from it so a decoration can never become a
	// request. An action is not decoration. Naming it makes it
	// reviewable: a button that fires a request says so in its props,
	// in one place, and the type refuses anything that is not a
	// request or a wiring key.
	//
	// The request family (data-fui-rpc*, data-fui-confirm, the signal
	// mutations) needs a button: an anchor carrying one is refused at
	// render, because a link navigates and a button acts. The wiring
	// keys may ride either tag.
	Action html.Attrs

	ID         string
	ExtraAttrs html.Attrs
	// Parts is the caller's reach into this component's named parts:
	// attributes on the root (a class appends, never replaces) and on
	// the icon. The root is not fillable; the icon is caller markup
	// already.
	Parts Parts
}

ButtonProps is a button, or an anchor that looks like one.

type CardProps

type CardProps struct {
	// Title renders as a heading. Empty omits it, along with the whole
	// header when Desc is empty too — an empty header is a stripe of
	// padding that looks like a mistake.
	Title string
	// TitleTag is the heading level, "h3" by default. A card does not
	// know how deep in the outline it sits, so a page that nests cards
	// under an h2 says so here rather than letting every card claim
	// the same level and leave the document with no structure to
	// navigate by.
	TitleTag string
	Desc     string
	// Href makes the whole card one focusable link — the surface is
	// the affordance. The contents render inside a single inner part,
	// so the anchor wraps exactly what the card showed as a div. A
	// href the anchor policy refuses is refused at render, naming the
	// prop, the package's posture for every configured href
	// (Alert.DismissHref, Form.Action, Table.Path): a rejected href is
	// the developer's mistake, not a link to render dead.
	Href   string
	Footer render.HTML

	ID         string
	ExtraAttrs html.Attrs

	// Parts. The header is the one fillable part, and the rule it
	// comes from is worth stating: a slot exists only where the
	// component COMPOSES something a prop cannot express. A card's
	// header is built from a title string and a description string,
	// so a header that needs a control in it has no prop to arrive
	// through. The body and the footer already take caller content, so
	// a slot there would be a second way to do one thing — which is
	// worse than none, because half the call sites will use each.
	//
	// Attrs are the opposite: they apply to every part, on every
	// component, because adding an attribute cannot break a structure.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

CardProps configures a card.

A card is the one component here with almost no accessibility surface, and saying so is the point of the type: the heading level is the only decision in it that a screen reader can be hurt by, and it is a decision the card cannot make alone.

type CarouselProps

type CarouselProps struct {
	// Label names the region. Required.
	Label string
	// Slides, in order. Required, at least one.
	Slides []CarouselSlide
	// Loop makes Next on the last wrap to the first (and vice versa).
	// Default: the ends disable.
	Loop bool
	// AutoRotateMS, when > 0, advances every N ms. Negative refused.
	AutoRotateMS int
	// VisiblePerView shows N slides side-by-side (default 1; 0 or
	// negative refused — a viewport of nothing is not a viewport).
	VisiblePerView int
	// NoDots hides the dot anchors.
	NoDots bool
	// NoArrows hides the prev/next controls.
	NoArrows bool

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root, the track, the slides and the
	// controls.
	Parts   Parts
	Strings *Strings
}

CarouselProps configures the carousel.

type CarouselSlide

type CarouselSlide struct {
	// Content is the slide body. Required.
	Content render.HTML
	// Label is the slide's accessible label. Defaults to
	// Strings.CarouselSlide ("Slide <n> of <total>") at render.
	Label string
	// ID is the slide's stable id. Empty takes "<trackID>-slide-<n>".
	ID string
}

CarouselSlide is one entry.

type Case

type Case struct {
	Name string
	Why  string
	HTML render.HTML
}

Case is one rendering of a component, with a reason for existing. The reason is not documentation: a case with nothing to show is a case that will be updated to match whatever the code does next.

type ChoiceProps

type ChoiceProps struct {
	// Type is "checkbox" or "radio".
	Type string
	// Name groups the choice. For a radio it IS the group; items that
	// must act as one group share it, which is why the group helpers
	// stamp it on every item.
	Name string
	// Value is what the choice submits when checked. Required for
	// Radio: without distinct values a group cannot tell its options
	// apart. A checkbox may leave it empty and submit "on", the HTML
	// default.
	Value string
	// Label is the visible text beside the control. Required: an
	// unlabelled choice is a bug, not a variant.
	Label string
	// Hint is secondary text under the label, for the consequence of
	// the choice rather than its meaning.
	Hint     string
	Checked  bool
	Disabled bool

	ID string
	// Extra attrs land on the input, the control that submits.
	Extra html.Attrs
}

ChoiceProps configures one checkbox or radio.

type Classes

type Classes map[Part]string

Classes maps parts to class names. Nil is valid and renders unstyled.

func (Classes) Class

func (s Classes) Class(p Part) string

Class returns the class for a part, or "" when the class map has none.

func (Classes) Variant

func (s Classes) Variant(p Part, variant string) string

Variant returns the class a class map uses for a named variant of a part, looked up as "<part>--<variant>". Empty when unstyled or unknown.

type ClusterProps

type ClusterProps struct {
	Gap string
	// Align is cross-axis: "center" by default in the class map, because a
	// row of controls of different heights should line up on their
	// middles.
	Align string
	// Justify is main-axis: "start", "between", "end".
	Justify string
	// NoWrap keeps the row on one line. Use it sparingly: a row that
	// cannot wrap is a row that overflows on a phone, and horizontal
	// page scroll is the failure this whole layer exists to prevent.
	NoWrap bool
	Tag    string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root.
	Parts Parts
}

ClusterProps is horizontal flow that wraps: a row of buttons, a row of tags, a toolbar.

type ColorProps

type ColorProps struct {
	Name string
	// Value is the colour as text, usually but not always #rrggbb.
	Value    string
	Disabled bool
	Invalid  bool

	// ID and extra attrs land on the hex text input, the control that
	// submits.
	ID          string
	Extra       html.Attrs
	DescribedBy string

	// Parts: attrs and binds on the shell, the hex input and the
	// swatch. Strings carry the swatch's name.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

ColorProps configures a colour control.

type Column

type Column struct {
	// Key identifies the column: the value under SortParam in the
	// sort href, and the key a Row's Cells are matched by. Required.
	// Anything a query can encode is a legal Key — that is the point
	// of building the href through net/url.
	Key string
	// Header is the visible column header text. May be empty: an
	// actions or icon column. A header-less column that cannot be
	// sorted is hidden from assistive tech; one that can is named
	// from Strings.TableSortBy, because an anchor with no text is a
	// control with no name.
	Header string
	// Sortable makes the header a sort control: an anchor that
	// re-submits the screen's query with this column's Key and the
	// next direction.
	Sortable bool
	// HeaderAttrs and CellAttrs add attributes to this column's <th>
	// and to every <td> under it, through the same sanitiser as
	// ExtraAttrs (Safe): data-fui-* keys, style, id and class are
	// dropped, names are stored folded, and one attribute under two
	// spellings is refused.
	HeaderAttrs html.Attrs
	CellAttrs   html.Attrs

	// Variant is class-map vocabulary for the whole column: looked
	// up as "<part>--<variant>" on this column's <th> (PartHeader)
	// and each of its <td> (PartCell), so a class map can align a
	// column's text or shade its column without the structure
	// caring. Empty means none.
	Variant string
}

Column describes one table column.

type ComboboxOption

type ComboboxOption struct {
	// ID is the option's stable id. Empty takes "<listboxID>-opt-<n>".
	ID string
	// Value is what a pick writes into the input. Empty takes Label.
	Value string
	// Label is the option's visible text. Required.
	Label string
	// Meta is secondary text beside the label (a path, a kind).
	Meta string
	// Href turns the option into an anchor: picking it navigates.
	// Unsafe schemes drop the navigation affordance entirely (the
	// pick still fills the input).
	Href string
	// Disabled removes the option from keyboard navigation.
	Disabled bool
}

ComboboxOption is one row in the listbox.

type ComboboxProps

type ComboboxProps struct {
	// ID is the input's element id. Required; the listbox takes
	// "<ID>-listbox".
	ID string
	// Name is the form-submit name on the input. Required.
	Name string
	// Label names the input. Required.
	Label string

	// Placeholder for the input.
	Placeholder string

	// Island is the typed in-page results contract: the endpoint that
	// re-renders the listbox and the signal the region is bound to.
	// Optional — a static Options list needs no island; the no-script
	// path is the same-origin GET form below either way.
	Island *Island

	// NoScriptAction is the form's action URL, the no-script
	// destination: same-origin, a GET that submits the query. Required
	// when Island is set (a reader without script must still reach the
	// results); refused when it is "#".
	NoScriptAction string

	// Options is a static list the module filters client-side. Takes
	// precedence over the Island (no round-trip fires).
	Options []ComboboxOption

	// DebounceMS bounds the input debounce. Zero takes 250; negative
	// is refused.
	DebounceMS int

	ExtraAttrs html.Attrs

	// Parts: attrs on the root, the input and the listbox. The
	// listbox's content is the Options' — not fillable.
	Parts   Parts
	Strings *Strings
}

ComboboxProps configures the combobox.

type ConditionalFieldProps

type ConditionalFieldProps struct {
	// When is the NAME of the watched field. Required: a region that
	// watches nothing is always shown, which is a div.
	//
	// The scope the runtime reads the name in: the region's own form
	// first — two forms can each carry a "plan" control and the region
	// follows the one it belongs to — and, when the region has no
	// form or its form holds no control of that name, the document,
	// preferring controls no form owns (a page-level switch) and
	// otherwise the first in document order.
	When string
	// Value is the watched field's value that shows the region.
	// Required: shown on every value is the same as always shown.
	// The empty string is refused, so "show when unchecked" — a
	// checkbox whose unchecked value is "" — is not expressible here;
	// watch a select or a radio pair whose values are both stated
	// instead.
	Value string

	ID         string
	ExtraAttrs html.Attrs
}

ConditionalFieldProps is a region shown when another field has a given value.

type ContainerProps

type ContainerProps struct {
	// Size names the measure — "sm", "md", "lg", "full".
	Size string
	Tag  string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root.
	Parts Parts
}

ContainerProps is the page's measure: a maximum width and the gutters that keep content off the edge of the screen.

type CounterProps

type CounterProps struct {
	// Signal is the client signal the count lives in. Required: the
	// buttons' whole contract is that they change a value the page
	// already holds, and the display's contract is that the same
	// signal writes it.
	Signal string
	// Name, when set, renders the value as a named number input the
	// surrounding form submits — the no-script path. The input's
	// value follows the signal the same way the span does, so the
	// two variants are one control.
	Name string
	// Value is the count's first value, seeded into the markup.
	Value int
	// Step is the increment size. Default 1; must be positive.
	Step int
	// Label names the group for assistive technology. Empty takes
	// Strings.CounterLabel: three controls with no group name is a
	// label, a minus, a number and a plus to nobody, so the default
	// names them.
	Label string
	// AnimateFrom, when set, is the value the count animates from on
	// arrival; Value is where it lands, and Value stays the SSR text.
	// Nil means no animation. The hooks it renders are bound by the
	// headless-controls module, which respects reduced motion and
	// never owns the number.
	AnimateFrom *int
	// DurationMS bounds the animation. Zero takes the module's
	// default; negative is refused.
	DurationMS int

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root and the value. Strings
	// carry the group's name and the two buttons'.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

CounterProps configures a client-side counter.

type DetailListProps

type DetailListProps struct {
	// Rows are the pairs, in reading order. At least one.
	Rows []DetailRow

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the list, its rows and their terms
	// and values.
	Parts Parts
}

DetailListProps is a record read as label/value pairs.

type DetailRow

type DetailRow struct {
	// Label is the term. Required: a value with no term is a fact
	// nobody can look up.
	Label string
	// Value is the value, and it may be markup — a status badge, a
	// link, an empty-value dash. Required: render the empty-value
	// dash for a missing value, so the absence is deliberate.
	Value render.HTML
}

DetailRow is one labelled value: the term in a <dt>, the value in the <dd> that follows it.

type DisclosureProps

type DisclosureProps struct {
	// Summary is the always-visible controller. Required and
	// non-blank: a details whose summary says nothing is a button with
	// no name.
	Summary render.HTML
	// Content is the revealed panel.
	Content render.HTML
	// Open renders the details expanded.
	Open bool
	// Trap opts the open disclosure into focus containment: Tab walks
	// inside it and comes back, the drawer posture. The containment is
	// the widget runtime's own (the kernel's focus selector), armed by
	// the module while the disclosure is open.
	Trap bool
	// Name, when set, is the details element's name attribute: the
	// browser groups disclosures that share it and opens one at a time
	// (the native accordion) — opening one closes the others with no
	// script at all. Control bytes are refused: the value is a group
	// key the browser matches verbatim.
	Name string
	// PersistKey, when set, keeps the open state across client-side
	// navigation AND restores it on arrival from the session store,
	// namespaced and component-encoded so two disclosures never share
	// one key. Empty means the ordinary dialect: closed by a
	// navigation, whatever the reader left open.
	PersistKey string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root, the summary and the panel. The summary
	// is fillable — its content is the caller's — and so is the panel.
	Parts Parts
}

DisclosureProps configures one disclosure.

type DividerProps

type DividerProps struct {
	// Label puts text in the break ("or"). A labelled divider is
	// always meaningful, so it is never decorative.
	Label string
	// Vertical draws it along the block axis, for a divider inside a
	// row.
	Vertical bool
	// Decorative says this line groups nothing — it is a flourish, and
	// is hidden from assistive tech. Without it a screen reader
	// announces "separator" at every line on the page.
	Decorative bool

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root, the lines and the label.
	Parts Parts
}

DividerProps is a line between things.

type EmptyStateProps

type EmptyStateProps struct {
	// Title is the heading. Required: it is also the region's name.
	Title string
	// Level is the heading level, 3 by default. An empty state nests
	// inside a section; one mounted as the whole page under the h1
	// says 2 here.
	Level int
	// Description is the supporting line: what would be here, or what
	// to do about it.
	Description string
	// Action is the way out — "New app", "Clear the filter". A dead
	// end with no action is a page the reader can only leave with the
	// back button.
	Action render.HTML

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part the state draws.
	Parts Parts
}

EmptyStateProps is the page a list with nothing in it shows instead of the list.

type Event

type Event struct {
	// Title is what happened. Required.
	Title string
	// Detail is the supporting line.
	Detail string
	// When is the human-readable time ("3 days ago", "18:22").
	When string
	// Machine is the machine-readable timestamp for <time datetime>,
	// RFC 3339. Without it "3 days ago" is a string no assistive tech,
	// translation layer or scraper can resolve to a moment.
	Machine string
	// Tone lets the class map colour the marker — "success", "danger".
	Tone string
	// Meta is the secondary line beside the title — an actor, a
	// relative time ("by dom", "2h ago") — in the header row, read
	// after the title it qualifies. When is the timestamp contract;
	// Meta is a caption with no machine form.
	Meta string
	// Body is extra markup under the detail: a log excerpt, actions.
	Body render.HTML
}

Event is one thing that happened.

type FieldControl

type FieldControl struct {
	// ID is the field's For: the id the label points at.
	ID string
	// DescribedBy is the error's and the hint's ids, error first, for
	// the control's aria-describedby. Empty when the field has
	// neither — unless ReserveError held an empty error node in place,
	// whose id rides here too.
	DescribedBy string
	// Invalid is true when the field has an Error, so a control never
	// has to be told twice.
	Invalid bool
	// Required mirrors the field's own flag.
	Required bool
}

FieldControl is what a field tells its control about itself. The control is built from it rather than beside it, which is the whole point: the id scheme, the description wiring and the invalid state all originate in one place and reach the input by construction.

Passing a pre-built control instead is how a hint ends up rendered, given an id, and never referenced — visible on screen and absent to a screen reader. That was true of every field in this system until Field started handing these down.

type FieldError

type FieldError struct {
	// For is the id of the control at fault. With it the message
	// becomes a link that moves focus to the field; without it the
	// message is text, which is the right fallback and a worse
	// experience.
	For string
	// Message is what is wrong, in words the person can act on.
	// "Invalid" is not one of them.
	Message string
}

FieldError is one thing that went wrong.

type FieldProps

type FieldProps struct {
	// Label is required. An input without a label is not a variant, it
	// is a defect: nothing announces it and nothing clicks it into
	// focus.
	Label string
	// For is the control's id. Without it the label is decorative — it
	// neither names the control for assistive tech nor enlarges its hit
	// area — so a Field with a Label and no For is refused.
	For string
	// Hint is help text: the rule the value must obey. It stays
	// rendered — and stays in the description — when Error is set: the
	// hint is the rule, the error is the violation, and dropping the
	// rule exactly when it was broken is dropping it when the reader
	// needs it most. The error is drawn first and read first, so the
	// correction arrives before the reminder.
	//
	// The cost is paid in pixels and syllables: an errored row is
	// taller, and its description is longer, than an error-only one.
	// That is the deliberate trade of this primitive; a caller who
	// wants the hint gone on error says so by not setting it.
	Hint string
	// Error is announced and drawn before the hint.
	Error string
	// ReserveError keeps an error paragraph rendered — empty and wired
	// into the control's aria-describedby — when Error itself is
	// empty. It exists for a script that fills the node without
	// re-rendering the field (a live editor applying edits as the
	// operator types). An empty described-by target announces nothing
	// until it is filled, which is the point: the wiring ships, the
	// words arrive when they are true.
	//
	// The node's id is the contract a filling script looks it up by:
	// the control's id with "-error" appended, the same id that rides
	// aria-describedby. It carries no hook of its own — a data-hui-*
	// attribute is something this package's runtime module binds, and
	// no module has behaviour for an empty paragraph.
	//
	// A caller that fills the node must also set aria-invalid on the
	// control it describes; this component cannot know the script's
	// verdict. The server-rendered path should pass Error instead —
	// a reserved node is a scaffold, not an answer.
	ReserveError bool
	// Required marks the label and is mirrored onto the control by the
	// caller (the control owns its own required attribute).
	Required bool

	// Parts is the caller's reach into the field's named parts:
	// attributes on the root (a class appends, never replaces). No
	// part is fillable — every element a field draws is half of a
	// relationship built from the control's id, and replacement
	// content would break the half it replaced.
	Parts Parts

	ID         string
	ExtraAttrs html.Attrs
}

FieldProps is the label / control / hint / error group.

type FieldsetProps

type FieldsetProps struct {
	// Legend is the group's name, rendered as a real <legend> inside
	// a real <fieldset>: that pair is the native group semantic,
	// naming every control inside without a single aria attribute.
	//
	// Empty renders the group as a plain div: an unlabelled fieldset
	// is a landmark that names nothing, and a group with no name to
	// give is a row of fields, not a section.
	Legend string
	// Description is the supporting line under the legend. It is
	// wired to the group by aria-describedby beside any group error,
	// read with the question it explains.
	Description string
	// Error is the error that belongs to the group as a whole — the
	// question was answered wrong somewhere in it, not in any one
	// field. It is wired to the group by aria-describedby, so the
	// fields' own errors and the group's never compete for the same
	// relationship.
	Error string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the group and every part it draws.
	Parts Parts
}

FieldsetProps is a titled group of fields: the section of a form that belongs together ("Notifications", "Access").

type FileUploadProps

type FileUploadProps struct {
	// Name is the field name. Required.
	Name string
	// ID is the input's id. Required: the zone is a <label for=…>, and
	// without the pair the biggest click target on the control does
	// nothing.
	ID string
	// Label is the instruction inside the zone. Required.
	Label string
	// CTA is the part of the instruction that reads as the action —
	// "choose a file". It is NOT a button: a button inside a label
	// swallows the label's click, so the whole zone stops opening the
	// picker. It is styled text, and the real control is the input.
	CTA string
	// Hint is the accepted types and size, tied to the input by
	// aria-describedby so it is read with the field rather than being
	// small print beside it.
	Hint string
	// Accept is the accept attribute; Multiple allows several files.
	Accept   string
	Multiple bool
	Required bool
	Disabled bool
	Invalid  bool
	// DescribedBy is an extra id to reference, from a Field.
	DescribedBy string

	// Parts: attrs and binds on the root, the zone, the input, the
	// list and the status. Strings carry the sentences the
	// runtime says when files are chosen.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings

	ExtraAttrs html.Attrs
}

FileUploadProps is a file picker with a drop target.

type FormProps

type FormProps struct {
	// Action and Method are the submission target. Method defaults to
	// post.
	Action string
	Method string
	// Label names the form for assistive tech when it is one of
	// several on a page. A single form on a page needs no name; two
	// unnamed ones are two identical entries in a landmark list.
	Label string
	// Errors renders above the fields. When non-empty the form asks
	// the runtime to move focus there on load, which is what makes a
	// failed submit noticed at all — see ValidationSummary.
	Errors render.HTML
	// Actions are the submit and cancel controls.
	Actions render.HTML
	// Multipart sets the encoding a file upload needs. Without it the
	// browser submits file inputs as names with no contents, which
	// looks like a server bug and is not one.
	Multipart bool
	// NoValidate turns off the browser's own validation bubbles, for a
	// form that validates on the server and reports through Errors.
	// The bubbles are not a substitute: they show one message at a
	// time, vanish on blur, and cannot be styled or read back.
	NoValidate bool

	// Island is where the form's answer is rendered again: when set,
	// the form carries the RPC contract beside its action and the
	// arrival pass focuses the summary. The HTTP convention the
	// runtime's RPC lands in the signal: a validation failure is
	// answered 200 with the region's HTML — the errors ARE the
	// answer, the island swaps them in, and the summary takes focus.
	// A non-2xx is a transport or server error, which the runtime
	// delivers as {ok:false, status, text} in the signal, never as
	// markup: an island form that answers 422 to a failed validation
	// renders nothing at all. Nil is right for a page that IS the
	// form — sign-in, the auth flow the architecture keeps native —
	// where the plain POST to the page is the whole design.
	Island Island

	// Request is what the form DOES when the host framework's runtime
	// is on the page: its data-fui-rpc contract — the endpoint the
	// submit posts to, a method that may differ from the native one
	// (a form that natively POSTs for no-script may PUT over RPC),
	// the success effects (a signal to land in, a page to navigate
	// to, a reset, a widget to open or close) — plus the mount hook a
	// generated form carries (data-action-mount, the compiled action
	// that populates its relation selects). It is a seam of its own
	// rather than a use of ExtraAttrs for the same reason Button's
	// Action is: Safe drops every data-fui-* and data-action-* key, so
	// wiring through extras would render a plain form that posts
	// natively. A key outside the request vocabulary panics at render,
	// naming the key and the seam.
	//
	// Island and Request are two ways of saying "the submit is an
	// RPC"; carrying both on one form is refused rather than resolved
	// by precedence.
	Request Action

	// Parts is the caller's reach into the form's named parts:
	// attributes on the root and on the body and actions rows (a
	// class appends, never replaces). Nothing is fillable — the
	// fields and the actions are the caller's own children.
	Parts Parts

	ID         string
	ExtraAttrs html.Attrs
}

FormProps is a form and the things around it.

type GalleryItem

type GalleryItem struct {
	// Src is the full-resolution image URL. Required; refused when
	// unsafe (a javascript: URL in an image is a payload, not a
	// picture) — it degrades to nothing the day the caller hands one
	// in, so the refusal is loud.
	Src string
	// Thumb is the thumbnail URL. Defaults to Src.
	Thumb string
	// Alt is the image's description. Required: an image with no
	// description is decoration lying about being content.
	Alt string
	// Caption is optional descriptive text under the image.
	Caption string
	// Width / Height for the thumbnail (CLS-safe). Default 200×150.
	Width  int
	Height int
}

GalleryItem is one entry.

type GalleryLightbox

type GalleryLightbox struct {
	// Name is the lightbox to open: the data-fui-open value, the Name
	// of a mounted framework/ui.Lightbox. Required when the wiring is
	// set; a zero GalleryLightbox renders plain links.
	Name string
	// Group is the data-fui-lightbox-group value: the id the lightbox's
	// prev/next nav walks across this gallery's items. Empty derives
	// "<Name>-gallery".
	Group string
}

GalleryLightbox names the lightbox a gallery's items open. It is a typed prop rather than per-item extra attrs because the data-fui-* keys it renders are the widget runtime's open contract, which the extra-attrs surface refuses on purpose — a caller's decoration can never become a request, but a lightbox trigger is a first-class intent, so it is named here where it is reviewable.

type GalleryProps

type GalleryProps struct {
	// Items are the entries, in order. Required non-empty.
	Items []GalleryItem
	// Label names the list. Required: an unnamed image list is a
	// landmark a screen reader cannot jump to.
	Label string
	// HrefFn, when set, returns a per-item destination. Empty for an
	// item makes that item's link the full image, so every tile stays
	// a working link.
	HrefFn func(i int, it GalleryItem) string
	// ExtraAttrsPerItem adds attributes to item N's anchor (a lightbox
	// group id, a deeplink): index → attrs.
	ExtraAttrsPerItem map[int]html.Attrs
	// Lightbox, when set, wires every item's anchor to a framework
	// lightbox: the click opens the named overlay instead of
	// navigating, through the widget runtime's open contract. The
	// no-script path stays the full-image href the anchor carries.
	Lightbox GalleryLightbox

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the list, items, figures, links, images,
	// captions.
	Parts Parts
}

GalleryProps configures the gallery.

type GridProps

type GridProps struct {
	// Min is the narrowest a column may get before the grid drops one,
	// as a name from the scale ("sm", "md", "lg"), not a length.
	//
	// A length is what the framework's Grid takes, and it silently did
	// nothing for every value: the component wrote a data attribute
	// and no stylesheet ever read it. A named step cannot rot that way
	// — the class map either has a rule for the name or the name is a typo
	// that shows up the first time anyone looks.
	Min string
	Gap string
	Tag string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root.
	Parts Parts
}

GridProps is an auto-fitting grid.

type GroupProps

type GroupProps struct {
	// Legend is the group's shared label. Required: a set of choices
	// with no question above them is as broken as an unlabelled input.
	Legend string
	// Required marks the legend with data-required, the same state
	// attribute a Field puts on its label, so a class map can draw
	// the mark a sighted reader looks for. It is a cue, not a
	// constraint: what the browser and the parser enforce is the
	// required attribute on the leaves, which the caller sets there.
	Required bool

	ID    string
	Extra html.Attrs
}

GroupProps configures a fieldset of choices under one legend. The items arrive already rendered (each wrapped by its own styled component) because the wrapper owns the per-item style handle.

type InputGroupProps

type InputGroupProps struct {
	// Label names the group when it holds more than one focusable
	// thing and no single label covers them. It becomes a group role
	// with that name; without it the group is a plain div, which is
	// correct for the common case of one labelled input plus a button
	// that already says what it does.
	Label string

	ID         string
	ExtraAttrs html.Attrs
}

InputGroupProps joins controls into one visual control: an input with a button, a select with a field.

type InputProps

type InputProps struct {
	Type string
	// DescribedBy is the aria-describedby a Field hands down. It is
	// the whole reason a hint or an error reaches assistive tech: the
	// text is on screen either way, but without this attribute a
	// screen reader user hears the label and nothing else — no
	// format hint, and no reason the control is red.
	DescribedBy string
	Name        string
	Value       string
	Placeholder string
	Required    bool
	Disabled    bool
	// Invalid states aria-invalid for assistive tech; the class map
	// colours the border off the same attribute.
	Invalid bool
	// AriaLabel names the control where a visible label cannot go — a
	// search box in a toolbar, a field inside a table's own controls.
	// Everywhere else the label belongs to a Field, on screen where
	// everyone can read it. A placeholder is not a label: it goes away
	// on the first keystroke, its contrast is deliberately low, and
	// not every screen reader treats it as a name.
	AriaLabel string

	ID    string
	Extra html.Attrs
	// Owned are the type-specific attrs — min, max and step, and only
	// those — applied after the common set and after Extra so a caller
	// cannot widen a numeric input's bounds through extra attrs behind
	// the config's back. Any other key is refused: this is a seam for
	// bounds, not a second ExtraAttrs. Empty values are skipped,
	// leaving Extra's say if it has one.
	Owned html.Attrs
}

InputProps is the single-line control every Input flavour renders: one void input, the same state attrs assembled the same way. The flavours differ only in Type and the typed attributes they add (min/max/step), which is why none of them can drift in structure from a plain Input.

type Island

type Island struct {
	// Endpoint is the RPC path the region's next rendering comes
	// from: same-origin, starting with "/".
	Endpoint string
	// Signal is the data-fui-signal the region is bound to; the
	// response replaces it.
	Signal string
}

Island is where a state change goes: the endpoint that renders the region again, and the signal that region is bound to.

It is the sibling of Button's Action, and it exists for the same reason: the host framework attaches a request to markup by splicing attributes into the rendered string, which works on anything and therefore says nothing. Here the request is a prop on the component that fires it, so a table that turns its own pages says so in its props, in one place, and the attributes cannot be forged through ExtraAttrs — Safe drops every data-fui-* key on purpose.

The framework's first hard rule is that an in-page state change is never a route: no link that merely navigates for a new page of rows, a new sort, a filter. The component renders the RPC contract (see attrs, in box.go) on the SAME element that keeps the href or action for no script, so the progressive shape is one element — an anchor that is the page without script and the island update with it.

type JSONTreeProps

type JSONTreeProps struct {
	// Value is the data to render. Required non-nil; values that do
	// not marshal to JSON are refused (a channel, a func, a cycle).
	Value any
	// OpenDepth is the recursion depth that renders open by default.
	// 0 means only the root is open; -1 means everything is open.
	OpenDepth int
	// MaxStringLen truncates long strings with the truncation mark.
	// 0 means no limit.
	MaxStringLen int

	ID         string
	ExtraAttrs html.Attrs

	// Strings overrides the words (object, array, null, empty,
	// truncation). Nil takes the defaults.
	Strings *Strings

	// Parts: attrs on the root, nodes, summaries, lists, keys.
	Parts Parts
}

JSONTreeProps configures the tree.

type Kit

type Kit struct {
	// Classes is this component's own classes.
	Classes Classes
	// contains filtered or unexported fields
}

Kit is what a fixture is handed: this component's class map, and a way to reach any other component's.

It exists because a fixture composes. A Form fixture needs a Button and an Input inside it, and before this existed there was only one class map in scope — the Form's — so every fixture either passed the parent's class map to the child, which dresses an <input> in .ds-form and leaves it otherwise naked, or gave up and wrote the child as a raw HTML string, which no part check, no golden and no audit can see. Thirty-one components did one or the other.

The lookup is a function rather than a map because the class maps live in the styled layer, which imports this one. Inverting that would put class names in the headless half, and the whole split is that they are not there.

func NewKit

func NewKit(own Classes, of func(component, variant string) Classes) Kit

NewKit builds a Kit with a resolver. The package that owns the class maps calls this to render the fixtures dressed; the contract suite passes nil and gets an unstyled system.

func (Kit) For

func (k Kit) For(component string) Classes

For is the class map a child component should wear. The name is the component's, exactly as it is registered.

func (Kit) Variant

func (k Kit) Variant(component, variant string) Classes

Variant is For, for a component whose class map depends on a tone or kind: an alert is danger or info, a badge neutral or warning. The catalogue drew every alert in the same blue until this existed, because one component mapped to one class map and a tone could not be asked for.

type LightboxViewerProps

type LightboxViewerProps struct {
	// Name is the lightbox's identity (required): the value its hooks
	// carry, and the source of the title span's id (<Name>-title),
	// which the surrounding modal's aria-labelledby points at.
	Name string
	// Label is the accessible name of the open viewer. Empty means
	// Strings.LightboxViewerLabel.
	Label string
	// Nav renders the prev/next buttons.
	Nav bool
	// Caption renders a <figcaption> bound to the viewer's caption
	// signal.
	Caption bool
	// Download renders an anchor that saves the image being viewed,
	// its href bound to the viewer's src signal.
	Download bool
	// PrevIcon, NextIcon and DownloadIcon are the buttons' glyphs.
	// Decorative; the accessible names come from Strings.
	PrevIcon     render.HTML
	NextIcon     render.HTML
	DownloadIcon render.HTML
	// Wiring renders the framework module's data-fui-lightbox*
	// attributes IN PLACE OF the hooks. See LightboxWiring.
	Wiring LightboxWiring

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on every part. Nothing here is fillable — the
	// caption and the image are signal-bound, and the buttons are the
	// nav contract; a slot would fight the binding it replaced.
	Parts Parts
	// Strings are the strings this component says. Nil means the
	// English defaults; a layer above sets them from the request's
	// language.
	Strings *Strings
}

LightboxViewerProps configure the viewer.

type LightboxWiring

type LightboxWiring struct {
	// Viewer is the data-fui-lightbox value: the lightbox widget's
	// name, carried by the root and the nav buttons, and (as a bare
	// marker) by the image the module's pinch-zoom owns.
	Viewer string
	// Nav renders data-fui-lightbox-nav="true" beside the hook that
	// states the same fact: the opt-in that makes prev/next and the
	// arrow keys step the gallery group.
	Nav bool
}

LightboxWiring is the framework binder's spelling of the viewer's own facts. The data-hui-* hooks are this package's contract with any runtime a host chooses, and they render exactly when Wiring is zero: a host shipping its own viewer module against the hooks leaves Wiring zero and none of the framework's attributes render. A set Wiring renders the data-fui-lightbox* family instead — for framework/ui's lightbox module, which binds data-fui-* hooks only — and suppresses the hui twins: the two vocabularies name the same facts, and a viewer that rendered both would invite a host module to double-bind the gallery the framework module steps.

type Live

type Live string

Live says how — and whether — an alert interrupts.

This is the decision the component exists to force, because getting it wrong is not a small mistake in either direction.

  • LiveOff is the default and the right answer for anything the server rendered as part of the page. An alert that was already on screen when the page loaded has not "happened"; announcing it as an event means a screen reader reads page furniture over the top of whatever the user was doing, on every single page load.
  • LivePolite queues the message until the user pauses. It is for something that arrived after load and can wait: a background job finished, a value saved.
  • LiveAssertive interrupts immediately. It is for something the user must hear before they do anything else: the deploy failed, the form did not submit, the connection dropped. Nothing routine belongs here — an assertive region that fires often is one a user learns to resent and cannot turn off.

role="alert" already implies assertive and role="status" already implies polite, so only the role is written. Stating both is how a message ends up announced twice.

const (
	LiveOff       Live = ""
	LivePolite    Live = "polite"
	LiveAssertive Live = "assertive"
)
type MenuAction struct {
	// Path is the form's action URL.
	Path string
	// Method defaults to POST.
	Method string
	// Fields are the hidden inputs, first use: the CSRF token.
	Fields map[string]string
	// Unsafe acknowledges the no-Fields case deliberately.
	Unsafe bool
}

MenuAction is the row's form-POST shape.

CSRF contract: the framework never mints or verifies tokens — the caller's form middleware owns that. But a state-changing POST with no hidden inputs is almost certainly a forgotten token, so the default refuses it: an Action with no Fields panics unless Unsafe explicitly acknowledges the endpoint carries its own protection.

type MenuItem struct {
	// Label is the row's visible text. Required unless Separator.
	Label string

	// Href turns the row into an <a> link. Mutually exclusive with the
	// custom action attrs; if both are supplied, Href wins.
	Href string

	// RPC and RPCMethod wire the row to a server-side handler via the
	// kernel's data-fui-rpc contract. Use for "Delete this row" items.
	RPC, RPCMethod string

	// Confirm asks the reader to confirm before the RPC fires; the
	// runtime honours it on RPC dispatch and on any form submit.
	Confirm string

	// Icon is rendered before the Label. Inline HTML.
	Icon render.HTML

	// Danger tints destructive rows; a visual hint only.
	Danger bool

	// Disabled removes the row from keyboard navigation and pointer
	// interaction while keeping it in the panel.
	Disabled bool

	// Separator renders a horizontal divider instead of a row.
	Separator bool

	// ID becomes the rendered row's id attribute. Uniqueness is
	// caller-owned, like any HTML id — the menu refuses the duplicates
	// it can see.
	ID string

	// Radio, when non-empty, renders the row as a radio option:
	// role="menuitemradio" plus aria-checked (see Checked) and the
	// group attr the module arbitrates client-side. Exactly one row of
	// a group should carry Checked. Mutually exclusive with Children —
	// both set is refused at render.
	Radio string

	// Checked sets aria-checked on a Radio row.
	Checked bool

	// Action renders the row as a form submission instead of a link or
	// RPC, for hosts whose command rows hit PRG endpoints. Nil (the
	// zero value) renders nothing. Mutually exclusive with Href, RPC,
	// Radio and Children: incoherent combos are refused at render.
	Action *MenuAction

	// Children nests a submenu behind this row. The row renders as a
	// disclosure summary; setting Href, RPC, Action or Radio on it is
	// refused at render.
	Children []MenuItem

	// ExtraAttrs forwards additional attributes onto the row. Keys the
	// row owns are dropped, as everywhere in this package.
	ExtraAttrs html.Attrs
}

MenuItem is one row: an actionable item (Label required, Href / RPC / Action as supplied) or a separator. The framework owns the role attributes; callers only describe semantics.

type MenuProps struct {
	// ID pairs the trigger with the panel. Empty derives a stable id
	// from the menu's content.
	ID string
	// Label is the trigger's visible text. Mutually exclusive with
	// TriggerHTML and TriggerElement.
	Label string
	// TriggerHTML overrides Label with custom inline HTML.
	TriggerHTML render.HTML
	// TriggerElement replaces the framework summary with a
	// caller-owned interactive element: the menu renders a
	// summary-less disclosure beside it and the module makes the
	// element the controller. Use for host-styled triggers — an
	// interactive element inside a summary is axe nested-interactive.
	TriggerElement render.HTML
	// Items is the menu's contents. Required; an empty menu panics —
	// it signals a bug, not a runtime state.
	Items []MenuItem
	// Position names the panel's anchoring variant (bottom-start, …);
	// the class map resolves it. Empty takes bottom-start.
	Position string
	// LazyPanel ships the panel's rows inside an inert template the
	// module mounts on first open, for page-scoped consumers that must
	// not see closed-menu rows in the live DOM.
	LazyPanel bool

	ExtraAttrs html.Attrs

	// Parts: attrs on the root. Rows own their guarantees.
	Parts Parts
}

MenuProps configures a dropdown menu.

type MultiSelectOption

type MultiSelectOption struct {
	// Value is the form-submit value. Required.
	Value string
	// Label is the option's visible text. Required.
	Label string
	// Selected checks the option on first paint.
	Selected bool
	// Disabled greys the option out and keeps it unsubmitting.
	Disabled bool
}

MultiSelectOption is one checkbox option.

type MultiSelectProps

type MultiSelectProps struct {
	// Name is the form-field name every checkbox shares; the form
	// receives the repeated key for each checked option. Required.
	Name string
	// Label is the group's accessible name and the disclosure's
	// summary. Required.
	Label string
	// Options are the choices, in order. Required and non-empty.
	Options []MultiSelectOption
	// Open renders the disclosure expanded.
	Open bool
	// Placeholder is what the chips strip says when nothing is
	// picked. Empty takes Strings.MultiSelectPlaceholder.
	Placeholder string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root, the chips strip, the summary, the
	// panel, the group and the rows. No part is fillable: the
	// checkbox group is the contract.
	Parts   Parts
	Strings *Strings
}

MultiSelectProps configures one multiselect.

type NotificationBellProps

type NotificationBellProps struct {
	// Href is where the link goes: the notifications page,
	// same-origin. Required — a bell that rings to nowhere is a dead
	// link on the no-script page, and "#" is not a destination.
	Href string
	// Label is the trigger's visible name. Required.
	Label string
	// UnreadCount is the count the badge shows and the accessible
	// name says. Negative is refused; zero hides the badge (nothing
	// unread is no news) unless a Bind raises it.
	UnreadCount int
	// UnreadBind, when set, keeps the count following a client
	// signal: the badge the server rendered at zero appears when the
	// signal changes it.
	UnreadBind *Bind
	// Opens, when set, is the widget name the anchor opens with
	// script: the same element is the no-script link (Href) and the
	// widget's trigger, rendered as the kernel's data-fui-open and a
	// bottom-anchored popover. A name carrying control bytes or
	// whitespace is refused — it is a widget key, not free text.
	Opens string
	// Icon is the glyph. The default bell is the structure's own; a
	// caller's SVG replaces it.
	Icon render.HTML

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root, the icon, the badge and the
	// count. Strings carry the spoken count.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

NotificationBellProps configures the unread-count trigger.

type NumberInputProps

type NumberInputProps struct {
	// Name is the form-field name. Required.
	Name string
	// Label is the visible label. Required: it names the control and
	// the two buttons (through NumberDecrement / NumberIncrement).
	Label string
	// Value is the initial value as text, so a decimal step's value
	// travels unchanged. Empty means no value. A value that is not a
	// number, or violates a supplied bound, is refused.
	Value string
	// Min and Max bound the value. Nil leaves the bound to the
	// server; a Min above a Max is refused.
	Min *int
	Max *int
	// Step is the stepper granularity. Default 1; positive only.
	Step int
	// Required and Disabled are the input's own states; Disabled also
	// takes the buttons out.
	Required bool
	Disabled bool
	// Help is the rule the value obeys; Error is the violation. Both
	// reach the input's description, the error first.
	Help  string
	Error string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part. Strings carry the two
	// buttons' names.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

NumberInputProps configures a number field with explicit −/+ buttons.

type OptimisticActionProps

type OptimisticActionProps struct {
	// Endpoint is the URL the click fires against. Required, and
	// same-origin (it must start with "/").
	Endpoint string
	// Method defaults to POST and may be PUT, PATCH or DELETE.
	Method string
	// IdleLabel is the text at rest. Required.
	IdleLabel string
	// SuccessLabel is the text painted the moment the button is
	// clicked. Required — and it must say something the idle label
	// does not, because the label flip IS the state change; colour
	// alone is never allowed to carry it.
	SuccessLabel string
	// IdleIcon and DoneIcon are decoration beside each label. Each
	// span carries its own so the flip swaps glyph and word together.
	IdleIcon render.HTML
	DoneIcon render.HTML
	// Variant and Size are class-map vocabulary, passed through so the
	// class map can look up "<part>--<variant>". The structure does not
	// care.
	Variant string
	Size    string
	// Disabled is the state at render time. The runtime manages it
	// from the first click: pending disables, settlement re-enables.
	Disabled bool
	// FailedText is what the status span announces when the server
	// refuses. Defaults to "Could not save. Try again."
	FailedText string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs only. Nothing is fillable — the two labels are
	// the state, and a slot that replaced one could make the button
	// say it did something it did not.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

OptimisticActionProps is a button that commits once: the success label is painted on click, the endpoint fired, and a non-2xx rolls everything back with a shake.

type Option

type Option struct {
	// Value is what the option submits; Label is what it shows. They
	// are separate so the submitted key stays stable while the visible
	// text is edited.
	Value string
	Label string
}

Option is one entry in a Select's list.

type PageHeaderProps

type PageHeaderProps struct {
	// Title is the page's heading. Required: the header exists to
	// name the page, and a header with no heading is padding.
	Title string
	// Level is the heading level, 1 by default. The page's own header
	// is the h1; a header for a sub-page inside a page says 2 here
	// rather than leaving the outline to guess.
	Level int
	// Eyebrow is a short kicker above the title ("Customers"). It is
	// aria-hidden: it repeats what the title or the navigation
	// already says, in fewer words, and hearing both is hearing the
	// page's name twice.
	Eyebrow string
	// Subtitle is the supporting line under the title.
	Subtitle string
	// Badge is status content beside the heading, outside its accessible name.
	Badge render.HTML
	// Actions are the page-level controls — New, Import, Filter — in
	// the trailing slot. They apply to the page, not to an item in
	// it; an action about one row belongs on that row.
	Actions render.HTML

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part the header draws.
	Parts Parts
}

PageHeaderProps is the top of a page: a heading, the words that qualify it, and the actions that apply to the whole page.

type PaginationProps

type PaginationProps struct {
	// AriaLabel names the nav landmark for AT. Required: pagination
	// announced as "pagination" is how AT users find it at all.
	AriaLabel string
	// Page is the current page, 1-based. Must be within 1..Pages.
	Page int
	// Pages is the total number of pages. At least 1.
	Pages int

	// Path is the screen's own path: each page href is it plus the
	// carried query, the page parameter replaced. Empty means the
	// current document — a relative "?query" href. When set it must
	// be same-origin and carry no query or fragment of its own: the
	// carry belongs in Query, where it survives the page turn, and a
	// Path query is silently replaced — a value lost with no error is
	// the defect this component exists to make structural.
	Path string
	// Query is the query the screen's URL already carries — the
	// search, the filters, the sort — and survives a page turn
	// beside the page number.
	Query url.Values
	// PageParam names the page query parameter. It defaults to "p".
	PageParam string

	// Window is the number of pages shown each side of the current
	// one, the first and last always shown. Default 1; a Window
	// large enough shows every page.
	Window int
	// OmitPrevNext drops the Previous and Next anchors entirely.
	OmitPrevNext bool

	// PrevLabel and NextLabel default to "Previous" and "Next".
	PrevLabel string
	NextLabel string

	// Island is where a page change goes when the pager sits inside
	// a region rather than being the page: the page anchors then
	// carry the RPC contract beside their hrefs — the page without
	// script, the region update with it, the URL written after the
	// swap. Optional, the Table posture: the URL is the truth for a
	// list, so a list screen's page anchors are plain navigations
	// the client router intercepts. An Island that looks wired and
	// is not is refused, whichever shape the pager renders.
	Island Island

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root, the list and the gaps.
	// Strings carry the two end links.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

PaginationProps configures a pagination bar.

type PaneHostProps

type PaneHostProps struct {
	// Primary is the always-visible pane. Required.
	Primary render.HTML
	// Secondary is the first optional side pane.
	Secondary render.HTML
	// Tertiary is the second optional side pane.
	Tertiary render.HTML
	// SecondaryOpen / TertiaryOpen set the SSR initial open state.
	SecondaryOpen bool
	TertiaryOpen  bool
	// SecondaryLabel / TertiaryLabel label each side pane's region.
	// Empty takes "Secondary" / "Tertiary".
	SecondaryLabel string
	TertiaryLabel  string
	// DeepLinkParam names the query parameter that carries pane state
	// for refresh/share/Back parity. Empty leaves the URL alone. A
	// non-token value (control bytes, markup, whitespace) is refused.
	DeepLinkParam string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root and each pane.
	Parts Parts
}

PaneHostProps configures the host.

type Part

type Part string

Part names an element inside a component. A class map styles parts; the structure names them. Adding a part is a change to both layers, which is the point: a class map cannot invent a hook the markup does not offer, and the markup cannot quietly drop one a class map is using.

const (
	PartActionIdle Part = "action-idle"
	PartActionDone Part = "action-done"
)

Action parts. The idle and done labels are parts of their own because a class map may want to weigh one differently from the other — a committed toggle that reads heavier than its idle sibling. The status span is PartVisuallyHidden because it must be read and must not be seen, the same rule as every other off-screen sentence in this package.

const (
	PartBreadcrumbList      Part = "breadcrumb-list"
	PartBreadcrumbItem      Part = "breadcrumb-item"
	PartBreadcrumbLink      Part = "breadcrumb-link"
	PartBreadcrumbSeparator Part = "breadcrumb-separator"
)

Breadcrumb parts.

const (
	PartCarouselStage Part = "carousel-stage"
	PartCarouselTrack Part = "carousel-track"
	PartCarouselSlide Part = "carousel-slide"
	PartCarouselDot   Part = "carousel-dot"
	PartCarouselPrev  Part = "carousel-prev"
	PartCarouselNext  Part = "carousel-next"
)

Carousel parts.

const (
	PartTagInputList  Part = "tag-input-list"
	PartTagInputTag   Part = "tag-input-tag"
	PartTagInputZone  Part = "tag-input-zone"
	PartTagInputField Part = "tag-input-field"
	PartTagInputAdd   Part = "tag-input-add"
)

TagInput parts.

const (
	PartRepeaterItems  Part = "repeater-items"
	PartRepeaterItem   Part = "repeater-item"
	PartRepeaterFields Part = "repeater-fields"
	PartRepeaterAdd    Part = "repeater-add"
)

Repeater parts.

const (
	PartComboboxForm    Part = "combobox-form"
	PartComboboxInput   Part = "combobox-input"
	PartComboboxListbox Part = "combobox-listbox"
	PartComboboxOption  Part = "combobox-option"
	PartComboboxStatus  Part = "combobox-status"
)

Combobox parts.

const (
	PartOption      Part = "option"
	PartAffixButton Part = "affix-button"
	PartAffixSwatch Part = "affix-swatch"
)

Control parts. The single-line controls and the affix shells are their own root; the input inside an affix shell is PartControl, and the reveal button and swatch are named so a class map can style them without the markup having to carry a class for them to be found by.

const (
	PartNumberDecrement Part = "number-decrement"
	PartNumberIncrement Part = "number-increment"
)

NumberInput parts. The input is the control the label points at; the two buttons are the component's own.

const (
	PartSliderOutput Part = "slider-output"
	PartSliderEdges  Part = "slider-edges"
	PartSliderEdge   Part = "slider-edge"
)

Slider parts.

const (
	PartRangeLow    Part = "range-low"
	PartRangeHigh   Part = "range-high"
	PartRangeOutput Part = "range-output"
	PartRangeTrack  Part = "range-track"
)

RangeSlider parts. The two inputs are the component's thumbs; the track is the positioning context the sheet overlays them on.

const (
	PartCounterDecrement Part = "counter-decrement"
	PartCounterValue     Part = "counter-value"
	PartCounterIncrement Part = "counter-increment"
)

Counter parts.

const (
	PartSummary Part = "summary"
	PartPanel   Part = "panel"
)

Disclosure parts. The summary and the panel are shared with Menu, which composes this anatomy for its dropdown panels.

const (
	PartToneWord Part = "tone-word"
	PartDismiss  Part = "dismiss"
)

Alert parts.

const (
	PartLegend     Part = "legend"
	PartGroupDesc  Part = "group-desc"
	PartFields     Part = "fields"
	PartGroupError Part = "group-error"
)

Fieldset parts.

const (
	PartFormBody    Part = "form-body"
	PartFormActions Part = "form-actions"
)

Form parts.

const (
	PartRoot    Part = "root"
	PartLabel   Part = "label"
	PartControl Part = "control"
	PartHint    Part = "hint"
	PartError   Part = "error"
	PartIcon    Part = "icon"
	PartText    Part = "text"
	PartFooter  Part = "footer"
	PartHeader  Part = "header"
	PartTitle   Part = "title"
	PartDesc    Part = "desc"
	PartBody    Part = "body"
	PartMarker  Part = "marker"
	PartActions Part = "actions"
	PartStatus  Part = "status"
	// PartVisuallyHidden is text that must be read and must not be
	// seen. It exists as a shared part because more than one component
	// needs it and every one of them must hide it the same way —
	// clipped, never display: none, which would take it out of the
	// accessibility tree along with the view.
	PartVisuallyHidden Part = "visually-hidden"
)

The shared vocabulary. Component-specific parts live beside their component.

const (
	PartJSONColon Part = "json-colon"
	PartJSONType  Part = "json-type"
	PartJSONCount Part = "json-count"
	PartJSONStr   Part = "json-str"
	PartJSONNum   Part = "json-num"
	PartJSONBool  Part = "json-bool"
	PartJSONNull  Part = "json-null"
	PartJSONEmpty Part = "json-empty"
)

JSONTreeParts: the primitive has no interactive parts beyond the details the browser owns, so the anatomy is textual (the value, the node, the summary, the list, the key) — and, under the value, one part per JSON scalar kind, so a class map can colour a string without colouring a number. Every one renders the same span the shared PartText did; only the name a class map can target differs.

const (
	PartSectionHead Part = "section-head"
	PartSectionBody Part = "section-body"
	PartSectionBrow Part = "section-eyebrow"
	PartDividerLine Part = "divider-line"
)

Layout parts.

const (
	PartFigure   Part = "figure"
	PartImage    Part = "image"
	PartCaption  Part = "caption"
	PartToolbar  Part = "toolbar"
	PartPrev     Part = "prev"
	PartNext     Part = "next"
	PartDownload Part = "download"
)

Lightbox viewer parts.

const (
	PartMenuCaret   Part = "menu-caret"
	PartMenuItem    Part = "menu-item"
	PartMenuSubmenu Part = "menu-submenu"
	PartMenuTrigger Part = "menu-trigger"
	PartMenuToggle  Part = "menu-toggle"
	PartMenuForm    Part = "menu-form"
)

Menu parts. The summary is the trigger on the summary path; the trigger-element path renders a wrapper (menu-trigger) beside a summary-less details (menu-toggle) that the module pairs by id.

const (
	PartMultiSelectChips Part = "multiselect-chips"
	PartMultiSelectGroup Part = "multiselect-group"
	PartMultiSelectRow   Part = "multiselect-row"
)

MultiSelect parts. The summary and the panel are the Disclosure's shared parts; the row is the label that wraps its checkbox.

const (
	PartBadgeDismiss   Part = "badge-dismiss"
	PartToolbarGroup   Part = "toolbar-group"
	PartToolbarLabel   Part = "toolbar-label"
	PartToolbarSpacer  Part = "toolbar-spacer"
	PartToolbarSearch  Part = "toolbar-search"
	PartPagination     Part = "pagination"
	PartPaginationLink Part = "pagination-link"
	PartPaginationGap  Part = "pagination-gap"
	PartStep           Part = "step"
	PartStepRow        Part = "step-row"
	PartStepText       Part = "step-text"
	PartStepHint       Part = "step-hint"
)

Badge / Tag / Toolbar / Pagination / Steps parts. PartRoot is each component's outermost element; Badge and Tag share every part they have, because a tag is a badge with a dismiss control and the two must not be allowed to drift apart.

const (
	PartPageEyebrow  Part = "page-eyebrow"
	PartPageText     Part = "page-text"
	PartPageSubtitle Part = "page-subtitle"
	PartPageActions  Part = "page-actions"
	PartPageTitleRow Part = "page-title-row"
)

PageHeader parts.

const (
	PartEmptyTitle Part = "empty-title"
	PartEmptyDesc  Part = "empty-desc"
	PartEmptyAct   Part = "empty-action"
)

EmptyState parts.

const (
	PartStatValue Part = "stat-value"
	PartStatTrend Part = "stat-trend"
)

StatCard parts.

const (
	PartDetailRow   Part = "detail-row"
	PartDetailTerm  Part = "detail-term"
	PartDetailValue Part = "detail-value"
)

DetailList parts.

const (
	PartRailList    Part = "rail-list"
	PartRailItem    Part = "rail-item"
	PartRailLink    Part = "rail-link"
	PartRailEyebrow Part = "rail-eyebrow"
	PartRailCount   Part = "rail-count"
)

Rail parts. The label is a plain label, not a heading: the rail is a complementary landmark already named by its aria-label, and a heading here would inject a stray, out-of-order entry into the page outline.

const (
	PartSidebarGroup       Part = "sidebar-group"
	PartSidebarGroupList   Part = "sidebar-group-list"
	PartSidebarGroupToggle Part = "sidebar-group-toggle"
	PartSidebarToggle      Part = "sidebar-toggle"
	PartSidebarDrawer      Part = "sidebar-drawer"
	PartSidebarInline      Part = "sidebar-inline"
	PartSidebarItem        Part = "sidebar-item"
	PartSidebarNav         Part = "sidebar-nav"
	PartSidebarPrepend     Part = "sidebar-prepend"
)

Sidebar parts.

const (
	PartSortableItem Part = "sortable-item"
	PartSortableGrip Part = "sortable-grip"
)

SortableList parts.

const (
	PartSpinnerRing Part = "spinner-ring"
	PartSpinnerDots Part = "spinner-dots"
	PartSpinnerDot  Part = "spinner-dot"
	PartSpinnerGrid Part = "spinner-grid"
	PartSpinnerCell Part = "spinner-cell"
	PartSkeleton    Part = "skeleton-line"
)

Spinner and Skeleton parts.

const (
	PartCardHeader Part = "card-header"
	PartCardBody   Part = "card-body"
	PartCardInner  Part = "card-inner"
)

Card parts.

const (
	PartTable  Part = "table"
	PartScroll Part = "scroll"
	PartHead   Part = "head"
	PartRow    Part = "row"
	PartSort   Part = "sort"
	PartCell   Part = "cell"
	PartEmpty  Part = "empty"
)

Table parts. PartCaption, PartHeader and PartBody are the shared names. PartRoot is the wrapper div, PartScroll the focusable region the table horizontally scrolls inside, and PartTable the table itself.

const (
	PartTab       Part = "tab"
	PartTabPanel  Part = "tabpanel"
	PartTabsNav   Part = "tabs-nav"
	PartTabsPanel Part = "tabs-panels"
)

Tabs parts.

const (
	PartTOCList Part = "toc-list"
	PartTOCItem Part = "toc-item"
	PartTOCLink Part = "toc-link"
)

TableOfContents parts.

const (
	PartTreeItem   Part = "tree-item"
	PartTreeRow    Part = "tree-row"
	PartTreeToggle Part = "tree-toggle"
	PartTreeGroup  Part = "tree-group"
)

Tree parts. The row holds the toggle and the label; the group is the child list a branch reveals.

const (
	PartDropZone  Part = "drop-zone"
	PartDropCTA   Part = "drop-cta"
	PartDropHint  Part = "drop-hint"
	PartDropInput Part = "drop-input"
	PartDropList  Part = "drop-list"
)

FileUpload parts.

const (
	PartErrorList Part = "error-list"
	PartErrorItem Part = "error-item"
	PartErrorLink Part = "error-link"
)

ValidationSummary parts.

const (
	PartTimelineItem Part = "timeline-item"
	PartTimelineMark Part = "timeline-mark"
	PartTimelineTime Part = "timeline-time"
	PartTimelineHead Part = "timeline-head"
	PartTimelineMeta Part = "timeline-meta"
	PartTimelineBody Part = "timeline-body"
)

Timeline parts.

const (
	PartFieldRow Part = "field-row"
)

Field parts.

const (
	PartOptionChoice Part = "option-choice"
)

Rating parts: each choice is one radio (PartControl) wrapped by its label (PartOption), the glyph inside the label aria-hidden.

const (
	PartPane Part = "pane"
)

PaneHost parts. A slot's modifier (pane--primary, pane--secondary, pane--tertiary) resolves from the same class map through Classes.Variant(PartPane, slot), the way every variant-taking part in this package resolves.

const (
	PartProgressValue Part = "progress-value"
)

Progress parts. The bar is the element; the description is the human sentence beside it.

const (
	PartSteps Part = "steps"
)

StepWizard parts.

const (
	PartToastToneWord Part = "toast-tone-word"
)

Toast parts.

type PartAttrs

type PartAttrs map[Part]html.Attrs

PartAttrs add attributes to named parts, subject to the rules above.

type Parts

type Parts struct {
	// Attrs add attributes to named parts. An attribute the component
	// owns is never beaten, and the keys refused on every way in are
	// refused here too (see refused).
	Attrs PartAttrs
	// Slots replace named parts' content. Only the parts a component
	// lists as fillable do anything; the rest are ignored rather than
	// silently half-applied.
	Slots Slots
	// Binds keep named parts in step with client signals: an
	// attribute anywhere the allow-lists allow, and text or html only
	// on a fillable part, where a Bind may replace what a Slot may.
	Binds Binds
}

Parts is what a caller may set on a component's named parts: extra attributes, replacement content for the parts the spec lists as fillable, and a binding to a client signal. It is one value rather than three loose fields so the harness can find it: a component either offers its parts or does not, and which parts a caller can reach is a question with one answer per component.

func (Parts) Box

func (s Parts) Box(classes Classes, fillable ...Part) Box

Box builds the render box that applies a caller's Parts. The fillable names are the parts whose content a caller may replace — the spec's Fillable list — and a text or html Bind on any other part is refused at render: it would replace content the component guarantees, which is the same reason a Slot is offered only there.

type PasswordProps

type PasswordProps struct {
	Name        string
	Placeholder string
	Required    bool
	Disabled    bool
	Invalid     bool

	// ID lands on the inner input, not the shell: the shell is
	// styling, and a Field label's for= must point at the control.
	ID string
	// Extra attrs land on the inner input too (autocomplete,
	// inputmode): they are attributes of the control, and putting them
	// on the shell would swallow them.
	Extra       html.Attrs
	DescribedBy string

	// Parts: attrs and binds on the shell, the input and the
	// reveal button. Strings carry the reveal button's two
	// names).
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

PasswordProps configures a password control.

type ProgressProps

type ProgressProps struct {
	// Value is the current progress. 0 to Max renders a determinate
	// bar; a negative value renders an indeterminate one. A value
	// past Max is clamped to Max at render; NaN and the infinities
	// render indeterminate, because a number that is not a number
	// cannot label progress.
	Value float64
	// Max is the ceiling. 0 (and any non-finite or negative value)
	// takes 100.
	Max float64
	// Label names the bar for assistive tech. Required: a progress
	// bar with no name is a stripe a screen reader cannot identify.
	Label string
	// LabelVisible renders the label as text above the bar (wired to
	// the element through aria-labelledby) instead of an aria-label.
	LabelVisible bool
	// Description is an optional human sentence beside the bar ("73
	// of 100", "Uploading…"). Scrubbed, never refused: it is data a
	// request can carry.
	Description string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root and the value. No part is fillable.
	Parts Parts
}

ProgressProps configures one bar.

type RailItem

type RailItem struct {
	// Anchor is the fragment id of the section this entry links to,
	// without the leading #. Required: an entry that links nowhere is
	// not an entry.
	Anchor string
	// Text is the visible link label. Required.
	Text string
	// Eyebrow is the leading chip beside the label (a number, a
	// glyph); empty hides it.
	Eyebrow string
	// Count is the trailing chip (a document count); empty hides it.
	Count string
}

RailItem is one entry in the rail.

type RailProps

type RailProps struct {
	// Label names the landmark and heads the list. Required: a
	// complementary landmark with no name is a region a screen reader
	// cannot jump to by name.
	Label string
	// Items are the entries, in display order. Required and non-empty.
	Items []RailItem
	// ObserveSelector is the CSS selector of the region whose sections
	// the module watches to mark the active entry. Empty renders a
	// purely static rail: the links work, nothing is marked.
	ObserveSelector string
	// TargetSelector narrows which elements inside the observed region
	// count as sections. Empty takes the module's default (the
	// h2/h3/h4 elements that carry an id).
	TargetSelector string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root and the entries. No part is fillable:
	// every element the rail draws carries its own guarantee.
	Parts Parts
}

RailProps configures a rail.

type RangeSliderProps

type RangeSliderProps struct {
	// Name is the base form-field name. Two inputs ship, Name+"-min"
	// and Name+"-max", so the server receives the pair without
	// parsing a composite string.
	Name string
	// Label is the group's name, read by assistive technology and
	// built into each thumb's name (RangeLow / RangeHigh).
	Label string
	// Min, Max, Step as for Slider.
	Min  int
	Max  int
	Step int
	// ValueLow and ValueHigh are the thumbs' values. Both zero means
	// Min and Max. Values outside the bounds clamp to them, and a
	// crossed pair is ordered low, high — the same repair the module
	// makes of a dragged pair.
	ValueLow  int
	ValueHigh int
	// ShowValue renders the pair's one output sentence, from
	// RangeValue; the module keeps it in step using the same
	// sentence, which travels with the output.
	ShowValue bool
	// Disabled disables both thumbs.
	Disabled bool

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part. Strings carry the two
	// thumbs' names and the output sentence.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

RangeSliderProps configures the two-thumb range pair.

type RatingProps

type RatingProps struct {
	// Name is the form-field name. Required; the group submits the
	// chosen value 1..Max under it.
	Name string
	// Label names the group for assistive technology. Required.
	Label string
	// Max is the ceiling. Default 5; below 1 is refused.
	Max int
	// Value is the current choice, 0 (none) to Max.
	Value int
	// Icon is the glyph cloned into every choice. The default is a
	// star; a caller's icon reaches every choice the same way.
	Icon render.HTML
	// Disabled disables every radio.
	Disabled bool

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part. Strings carry each
	// choice's name (RatingChoice).
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

RatingProps configures a rating as a radio group.

type RepeaterItem

type RepeaterItem struct {
	Fields []render.HTML
}

RepeaterItem is one repeated row: the fields are the caller's, the remove control is the component's.

type RepeaterProps

type RepeaterProps struct {
	// Name is the group's name. Required; the submit controls' names
	// derive from it when the caller does not give their own.
	Name string
	// Label names the group for assistive technology and names the
	// items region. Optional: a repeater beside its own heading may
	// not need one, though two unnamed repeaters on a page are two
	// identical entries in a landmarks list.
	Label string
	// Items are the rendered rows, in order.
	Items []RepeaterItem
	// MinItems is the floor below which removal is refused (the
	// remove controls render disabled). MaxItems is the ceiling above
	// which addition is refused. Zero MaxItems is unlimited.
	MinItems int
	MaxItems int
	// AddLabel and RemoveLabel override the add control's visible
	// text and the remove controls'; the remove control's accessible
	// name always carries its 1-based position.
	AddLabel    string
	RemoveLabel string
	// AddName and AddValue name the add control as a submit control
	// (a FormRepeater's "<Name>_add=1"); RemoveName names the remove
	// controls, whose value is the row's 0-based index. Empty names
	// render plain buttons the surrounding form does not carry.
	AddName    string
	AddValue   string
	RemoveName string
	// Action is the same-origin URL the add and remove operations go
	// to when the result swaps in place; it is required together with
	// Island and useless without it — a plain form repeater needs no
	// Action, because the surrounding form's own action is the
	// no-script destination and the submit controls ride it.
	Action string
	// Island is where an add or remove result lands. Required when
	// Action is set, refused half-wired like every Island.
	Island Island
	// Status is the sentence the live region carries — the server's
	// own words about the operation that just happened, re-rendered
	// with the region after an island swap.
	Status string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part. Strings carry the add
	// control's text and the remove controls' names.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

RepeaterProps configures a repeated field group.

type Row

type Row struct {
	// ID lands on the <tr> as id=, for a keyed swap: successive
	// renders of a live table differ only on the rows that changed.
	ID string
	// Cells maps a column Key to the rendered cell HTML. A column
	// with no entry renders an empty cell; a key naming no column is
	// refused at render, because a silent drop hides a typo.
	Cells map[string]render.HTML
}

Row is one table row. The shape is ui.DataTable's, so a caller moves between the two without repacking.

type SectionProps

type SectionProps struct {
	// Title is the heading. It is also what makes this a landmark: a
	// section with a name is navigable, a section without one is noise
	// in the landmark list, so Section renders a plain div until it
	// has a title to be named by.
	Title string
	// Eyebrow is a short kicker above the title ("01 / Overview"). It
	// is aria-hidden: it decorates a heading the reader has already
	// heard, and reading both is hearing the section's name twice. A
	// section named by Label alone renders it too — the caller's
	// heading rides in the body, and the kicker decorates it from
	// above.
	Eyebrow string
	// Level is the heading level, 2 by default. It is a real decision
	// and not a style: heading levels are how a screen reader user
	// moves through a page, and a section under an <h1> that renders
	// an <h3> leaves a hole in the outline.
	Level int
	// Description is supporting text under the heading.
	Description string
	// DescriptionHTML is supporting text that carries markup — code,
	// links. When set it takes precedence over Description.
	DescriptionHTML render.HTML
	// Label names the region when there is no Title to name it by.
	// A named section without a heading is still a landmark; an
	// unnamed one renders as a plain div. With both set, Title wins:
	// the heading names the region and Label is not read.
	Label string
	// Actions sit opposite the heading — a button, a filter.
	Actions render.HTML
	Gap     string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part the region draws.
	Parts Parts
}

SectionProps is a titled region of a page.

type SelectProps

type SelectProps struct {
	Name string
	// DescribedBy is the aria-describedby a Field hands down. It is
	// the whole reason a hint or an error reaches assistive tech: the
	// text is on screen either way, but without this attribute a
	// screen reader user hears the label and nothing else — no
	// format hint, and no reason the control is red.
	DescribedBy string
	// Options is the list of choices.
	Options []Option
	// Selected marks the option whose Value it matches. Empty selects
	// the placeholder (or the first option when there is none).
	Selected string
	// Placeholder renders a disabled first option with an empty value.
	// That empty value is also what makes Required enforceable: the
	// browser reads it as unfilled.
	Placeholder string
	Required    bool
	Disabled    bool
	Invalid     bool

	ID    string
	Extra html.Attrs
}

SelectProps configures a dropdown.

type SidebarItem

type SidebarItem struct {
	// Label is the entry's text. Required.
	Label string
	// Href is the link's destination. Required for leaves; refused on
	// group parents (a group parent is a disclosure, not a link).
	Href string
	// Icon is optional inline HTML before the label.
	Icon render.HTML
	// Active marks the current page's link.
	Active bool
	// Open opens a group at SSR. Inert on leaves.
	Open bool
	// MatchPrefix, when non-empty, is emitted as the leaf link's
	// data-fui-match-prefix value: the runtime's active-link module
	// re-derives the item's current-state on sub-paths after a client
	// navigation, using this value (not the href) as the section
	// prefix. The caller resolves the active state for first paint;
	// this is the same rule, handed to the client.
	MatchPrefix string
	// Children make this entry a group. Mutually exclusive with Href.
	Children []SidebarItem
}

SidebarItem is one nav entry: a link, or a group with children.

type SidebarProps

type SidebarProps struct {
	// NavLabel names the navigation landmark. Required.
	NavLabel string
	// Title is the optional heading above the list.
	Title string
	// Items, in order. Required non-empty.
	Items []SidebarItem
	// Variant names the layout posture: "persistent" (always shown),
	// "collapsible" (toggle collapses to a rail), "off-canvas"
	// (drawer-only), "auto-hide" (a rail at rest that reveals on hover
	// or focus — pure presentation, the sheet's own). Empty takes
	// persistent.
	Variant string
	// Collapse names the collapse behaviour for the collapsible
	// variant: "none" (server-owned state), "auto" (the module owns
	// and restores it). Empty takes none.
	Collapse string
	// ServerCollapsed ships the server-owned collapsed state for the
	// collapsible variant (data-collapsed on the root, aria-expanded
	// and the state-matched label on the toggle). Nil means the state
	// is not the server's to say here: with Collapse "auto" the module
	// restores it after hydration, and no data-collapsed ships.
	ServerCollapsed *bool
	// DrawerName names the widget the mobile drawer opens; empty
	// renders no drawer trigger (the caller mounts the drawer).
	DrawerName string
	// DrawerLabel names the drawer trigger button. Empty leaves the
	// trigger unnamed — a caller rendering the trigger passes the
	// words, this package says none of its own.
	DrawerLabel string
	// CollapseStorageKey names the storage the collapse state lives
	// in when Collapse is auto. Empty means server-owned: the module
	// never writes storage without it.
	CollapseStorageKey string
	// HideDrawerTrigger suppresses the drawer trigger button (a host
	// that opens the drawer from its own chrome).
	HideDrawerTrigger bool
	// CollapseLabel / ExpandLabel are the collapse toggle's two
	// accessible names, resolved by the caller. Both always ride the
	// button as data-hui-sidebar-collapse-label / -expand-label: the
	// module re-says them after a client-side toggle and carries no
	// sentence of its own. Empty falls back to NavLabel for the
	// button's initial aria-label.
	CollapseLabel string
	ExpandLabel   string
	// GroupMarkup selects the dialect for groups: "" or "details"
	// (native details + the disclosure module's persist key) or
	// "button" (aria-expanded + aria-controls + hidden, which the
	// sidebar module's group-toggle mirror owns).
	GroupMarkup string
	// GroupIDPrefix derives each group's id (the button dialect's
	// aria-controls target) and per-group persist key (the details
	// dialect), as <prefix>-g<N>. Empty takes DrawerName + "-inline",
	// or "sidebar-inline" when DrawerName is empty too.
	GroupIDPrefix string
	// Prepend / Footer are optional slots above and below the list.
	Prepend render.HTML
	Footer  render.HTML

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the shell, the controls, the region's parts and
	// every item part.
	Parts Parts
}

SidebarProps configures the sidebar.

type SkeletonProps

type SkeletonProps struct {
	// Label is what is loading, announced once through a live region
	// — "Loading apps". Required for the same reason Spinner's is.
	Label string
	// Lines is how many bars to draw. Zero means one.
	Lines int
	// Shape is a class map hint: "text", "title", "block", "circle".
	Shape string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root and the bars.
	Parts Parts
}

SkeletonProps is the placeholder shown while content loads.

type SliderProps

type SliderProps struct {
	// Name is the form-field name. Required.
	Name string
	// Label is the visible label. Required.
	Label string
	// Min and Max bound the range. Both zero means 0..100; a Min
	// above or equal to a Max is refused.
	Min int
	Max int
	// Step is the granularity. Default 1; positive only.
	Step int
	// Value is the initial value. Outside the range clamps to the
	// nearest bound, the way the browser treats a dragged thumb; a
	// value between steps renders as given and snaps on first
	// interaction — the module mirrors whatever the thumb says, so
	// there is no disagreement to refuse.
	Value int
	// ShowValue renders the output beside the label and the hook the
	// module mirrors the live value through.
	ShowValue bool
	// ShowEdgeLabels renders Min and Max under the track.
	ShowEdgeLabels bool
	// Disabled disables the control.
	Disabled bool

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

SliderProps configures a labelled range control.

type Slots

type Slots map[Part]render.HTML

Slots hold caller content for named parts. A slot REPLACES what the component would have put there, which is what makes it useful and what makes it dangerous: a component offers a slot only where its own content carries no guarantee, and the harness renders every declared slot filled with something hostile to prove it.

type SortDir

type SortDir string

SortDir is the direction of a sort.

const (
	SortAsc  SortDir = "asc"
	SortDesc SortDir = "desc"
)

type SortableItem

type SortableItem struct {
	// Key is the stable identifier the server applies the new order
	// by. Required. It is data the database hands the page, not
	// configuration: a space, a quote, `#` or markup renders escaped
	// in its attribute and control bytes are scrubbed — only the
	// empty key refuses.
	Key string
	// Label is the row's visible text and the drag name. Required.
	Label string
	// Content, when set, replaces the label as the row's body. Use
	// for richer rows; the grip and the announcements still use
	// Label.
	Content render.HTML
}

SortableItem is one row.

type SortableListProps

type SortableListProps struct {
	// Items are the rows in initial order. May be empty: an empty
	// column renders a valid sortable <ol> with no rows and stays a
	// drop target.
	Items []SortableItem
	// Label is the list's accessible name. Required.
	Label string
	// RPCPath, when set, is POSTed after every successful reorder:
	// order=<comma-separated keys>, plus container=<id> when
	// Container is set, version=<token> when Version is set, and
	// moved=<key> on a cross-container drop. The server confirms with
	// 2xx or rejects (the DOM reverts).
	RPCPath string
	// Group is the board id shared by linked lists (kanban): lists
	// with the same non-empty Group accept cross-container drag and
	// keyboard moves between them, including into an empty one.
	Group string
	// Container is the per-column id sent as the container field so
	// the server can route the write without inferring the column
	// from the key set. Distinct from Group because a board has one
	// Group and N containers.
	Container string
	// Version is an optional optimistic-concurrency token appended to
	// every commit. When set, a 409 fires the conflict path instead
	// of a blanket rollback.
	Version string
	// ConflictRPC, alongside Version, is GET-fetched on a 409: the
	// response body (fresh rows) replaces the list's contents,
	// server-rendered reconciliation. Without it a 409 falls back to
	// rollback.
	ConflictRPC string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root and the rows. No part is fillable:
	// the rows are the caller's data.
	Parts   Parts
	Strings *Strings
}

SortableListProps configures one list.

type SpacerProps

type SpacerProps struct {
	// Grow is how much of the row's slack this spacer takes against
	// its growing siblings. 1 through 4 — 0 is refused because a
	// spacer that cannot grow is a typo rather than a choice (the
	// typed Spacer defaults it to 1), and above 4 is refused because
	// the stylesheet wires exactly those four factors: a number the
	// sheet does not carry would be a factor that renders as 1.
	//
	// It travels as data-hui-grow rather than a style, because an
	// inline style is a rule the CSP drops and a class per factor is
	// a class the class map has to enumerate from a number it cannot see.
	Grow int
	// Min and Max bound the space, as names from the gap scale rather
	// than lengths: a spacer with a floor keeps a contents list legible
	// when the value is missing, and one with a ceiling keeps two
	// distant words from being pushed to the edges of the screen.
	Min string
	Max string
	// Leader draws a dotted line across the space — the leader between
	// a term and its value in a contents list, which is what ties the
	// two together once the row is wider than the pair.
	Leader bool
	// Rule draws a hairline rule across the space, for when the line
	// should read as a divider-in-waiting rather than a tie. A leader
	// and a rule in the same space are two answers to one question,
	// and are refused together.
	Rule bool

	ID         string
	ExtraAttrs html.Attrs
}

SpacerProps is the flexible space in a row: between a label and a value in a contents list, between the safe action and the destructive one.

type Spec

type Spec struct {
	// Name is the exported function's name, exactly. The coverage
	// gate matches on it.
	Name string
	// Anatomy lists the parts this component draws. A class map styles these
	// and only these; a part listed here and never rendered is a
	// class in the stylesheet with nothing to land on.
	Anatomy []Part
	// Fillable are the parts a caller may replace through Slots. The
	// empty set is the correct answer for most components: a slot is
	// offered where the component's own content carries no guarantee.
	Fillable []Part
	// Hooks are the data-hui-* attributes this component publishes for
	// the runtime. Naming them here is what lets a test prove the
	// runtime is not bound to an attribute nothing renders.
	Hooks []string
	// WithParts renders the component with a caller's Parts applied.
	// A component that offers its parts must provide it: it is how the
	// harness proves that filling a slot or adding an attribute cannot
	// break the contract, and a part nothing tests is a part that will.
	WithParts func(s Classes, parts Parts) render.HTML
	// Cases renders the component at a given Classes. A nil Classes means
	// unstyled, which is what the contract is asserted against.
	Cases func(k Kit) []Case
}

Spec describes one component and how to render it.

func SpecOf

func SpecOf(name string) (Spec, bool)

SpecOf returns one spec by component name.

func Specs

func Specs() []Spec

Specs returns every registered spec, in name order so that anything built from them — a test's output, a gallery page — is stable.

type SpinnerProps

type SpinnerProps struct {
	// Label says what is being waited for — "Loading apps". Required,
	// and it is not decoration: a spinner with no label is a moving
	// shape that tells a screen reader user nothing at all, and the
	// one thing they need to know is that waiting is the correct thing
	// to be doing right now.
	Label string
	// Announce puts the label in a live region, so it is read when the
	// spinner appears. Off by default: a spinner rendered WITH the
	// page has not "happened" — the same rule Alert follows — and
	// announcing it on load talks over the page.
	//
	// Turn it on for a spinner that replaces content after an action.
	Announce bool
	// Size is a class-map hint ("sm", "lg").
	Size string
	// Variant is the animated shape, a class-map hint: "" draws a
	// bordered ring, "dots" three pulsing dots, "grid" a rippling
	// square of nine. Every shape is aria-hidden — the shape is a
	// picture of waiting and the label is what waiting is FOR.
	Variant string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root and the shape.
	Parts Parts
}

SpinnerProps is the "working" indicator.

type StackProps

type StackProps struct {
	// Gap is a name from the scale, passed through to the class map. It is
	// not a length, because a system with arbitrary gaps has no
	// rhythm — and the one the framework ships proves the point: its
	// Grid takes a free-form Min that nothing ever read.
	Gap string
	// Align is cross-axis alignment: "start", "center", "end". Empty
	// stretches, which is what a stack of cards wants.
	Align string
	// Justify is main-axis distribution: "start", "center", "end",
	// "between". Empty packs to the start, which is what a page's
	// blocks do.
	Justify string
	// Tag overrides the element. A list of things should be a list;
	// this is how a Stack becomes one without a second component.
	Tag string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root.
	Parts Parts
}

StackProps is vertical flow: one thing after another, with one gap.

type StatCardProps

type StatCardProps struct {
	// Label says what is counted. Required.
	Label string
	// Value is the number, formatted by the caller. Required: a stat
	// card with no value is a label in a box.
	Value string
	// Trend is the movement ("+12% vs. last week"). Optional.
	Trend string
	// Direction names which way the trend reads — "up", "down" or
	// "flat". The trend's colour is the class map's to draw from it;
	// the direction must also be in the Trend's own words for a
	// reader who cannot see the colour, which is why Direction is a
	// variant hint and never the only carrier of meaning.
	Direction string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part the card draws. The value
	// is the part a dashboard binds to a signal.
	Parts Parts
}

StatCardProps is one metric held up for the page: a labelled value, and how it is moving.

type Step

type Step struct {
	// Label is the step's name. Required.
	Label string
	// Hint is the supporting line under the label.
	Hint string
	// Href makes the step a link — a completed step the reader can go
	// back to. Every href goes through the anchor policy; one the
	// policy refuses is the developer's mistake, said at render like
	// every configured href in this package.
	Href string
	// Marker overrides the glyph in the marker circle — a zero-padded
	// number ("01"), a caller's check icon. It is aria-hidden either
	// way: the marker is a picture of the step's state, and the label
	// beside it is what a reader hears.
	Marker render.HTML
	// State overrides the state derived from Current: "done",
	// "current" or "todo". Empty derives. An explicit state is how a
	// rail says a later step finished while an earlier one is still
	// open.
	State string
}

Step is one entry on a Steps rail.

type StepWizardProps

type StepWizardProps struct {
	// Steps is the flow. At least one.
	Steps []WizardStep
	// Current is the 0-based step in progress. Outside the list is
	// refused.
	Current int
	// Action and Method are the form's own. Method defaults to POST;
	// anything but GET or POST is refused.
	Action string
	Method string
	// HiddenFields carry state between steps.
	HiddenFields []render.HTML
	// Errors is the rendered validation summary for a failed submit.
	// When set, ID is required and the form asks the runtime to move
	// focus to the summary on arrival.
	Errors render.HTML

	// Island, when set, makes the submit a region update: the same
	// form, the same controls, the answer swapped into the signal.
	// Partial is refused like every Island.
	Island Island

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part. Strings carry the three
	// controls, the rail's name and each dot's.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

StepWizardProps configures the wizard.

type StepsProps

type StepsProps struct {
	// Steps are the entries in order. At least one.
	Steps []Step
	// Current is the 1-based step in progress: the steps before it are
	// done, it is current, the rest are todo. 0 means nothing has
	// started yet. Must not exceed len(Steps).
	Current int

	ID         string
	ExtraAttrs html.Attrs
}

StepsProps configures a step indicator.

type Strings

type Strings struct {

	// DismissTitled names a dismiss control whose target follows it:
	// an Alert's, a SystemBanner's. The title follows the colon and a
	// space; a format taking it as %s.
	DismissTitled string
	// RemoveLabelled names a remove control by what it removes: a
	// Tag's chip. A format taking the value's name as %s.
	RemoveLabelled string

	// ActionFailed is what a failed mutation announces when the caller
	// supplied nothing better. A sentence, not a word: it says the
	// save did not happen and that trying again is allowed.
	ActionFailed string

	// PickColor names the colour swatch, which carries no visible
	// label. A format taking the field's name as %s.
	PickColor string

	// ShowPassword and HidePassword name the reveal button in each of
	// its two states. Imperative, not rendered visually; they also
	// travel as data so the runtime can swap them.
	ShowPassword string
	HidePassword string
	// RevealShow is the reveal button's visible text, and RevealHide
	// is what the runtime swaps it to. One word each; RevealHide is
	// never rendered by the server.
	RevealShow string
	RevealHide string

	// Previous and Next label the pager's ends, which are disabled
	// anchors rather than absent ones. One word each; they carry no
	// arrows, so a translation may add its own.
	Previous string
	Next     string

	// ToneInfo, ToneSuccess, ToneWarning and ToneDanger are the tone
	// said before a title — a SystemBanner's, and an Alert's when the
	// caller passes one as ToneWord — because the title says WHAT
	// happened and the
	// tone is the only thing saying how serious it is. One word each,
	// the one a reader understands rather than the stylesheet's name
	// for the colour ("Error", not "Danger"); the colon and space that
	// follow are added in the assembly.
	ToneInfo    string
	ToneSuccess string
	ToneWarning string
	ToneDanger  string

	// FileSelected says one file was chosen, with {name} where the
	// file's name goes. The runtime writes the name in when it knows
	// it; the server never renders this string with a value in it,
	// because before the reader picks there is no value to say.
	FileSelected string
	// FilesSelected says several files were chosen, with {n} for the
	// count and {names} for the joined list. Substituted by the runtime
	// like FileSelected's {name}, for the same reason: neither the
	// count nor the names exist until the reader has picked.
	FilesSelected string

	// LightboxViewerLabel is the accessible name of the open image
	// viewer: what a screen reader says when the viewer takes focus.
	// A short noun phrase; the image's own alt travels beside it.
	LightboxViewerLabel string
	// LightboxPrevious and LightboxNext name the viewer's nav buttons.
	// They name the action on this surface ("Previous image"), not the
	// pager's bare word: a reader inside a viewer needs to know what
	// stepping moves.
	LightboxPrevious string
	LightboxNext     string
	// LightboxDownload names the anchor that saves the image being
	// viewed.
	LightboxDownload string

	// TableSortBy names a sort control whose column carries no
	// visible header text: an actions or icon column that can still
	// be sorted. A format taking the column's Key as {column}; the
	// token is written in at render, when the anchor is built —
	// unlike the {n} the runtime substitutes, the key is known on
	// the server.
	TableSortBy string
	// TableSortedBy says what a changed table is now sorted by, with
	// {column} where the column's header goes and {direction} where
	// the direction word goes. Rendered into data-hui-table-announcement
	// at render and copied into the status after a swap, so a
	// translated page announces in its own language.
	TableSortedBy string
	// SortAscending and SortDescending are the direction words
	// TableSortedBy carries. One lowercase word each: they sit inside
	// a sentence, after a comma.
	SortAscending  string
	SortDescending string

	// CounterLabel names the counter group, which carries no visible
	// label of its own. One noun.
	CounterLabel string
	// CounterDecrement and CounterIncrement name the counter's two
	// buttons. One word each; the counter's value is the context.
	CounterDecrement string
	CounterIncrement string

	// BackToTop names the back-to-top anchor, whose glyph carries no
	// words. A short imperative.
	BackToTop string

	// NumberDecrement and NumberIncrement name the stepper's two
	// buttons, which need the field's name to tell them from any
	// other stepper's. Formats taking the label as %s.
	NumberDecrement string
	NumberIncrement string

	// RangeLow and RangeHigh name the two thumbs of a range pair —
	// "Minimum %s" and "Maximum %s", the label standing in for the
	// thing bounded. RangeValue is the pair's one output sentence,
	// "%s to %s", low first.
	RangeLow   string
	RangeHigh  string
	RangeValue string

	// RatingChoice names one rating radio: "%d out of %d", the chosen
	// value and the ceiling.
	RatingChoice string

	// TagInputAdd names the control that commits the draft, a format
	// taking the field's label as %s. TagInputAdded and
	// TagInputRemoved are what the status region says after a chip
	// operation, with {name} where the chip's text goes — the runtime
	// writes it when the operation has happened.
	TagInputAdd     string
	TagInputAdded   string
	TagInputRemoved string

	// RepeaterAdd names the add control. RepeaterRemove names one
	// remove control, a format taking the item's 1-based position as
	// %d, because twelve controls all called Remove tell a screen
	// reader user nothing.
	RepeaterAdd    string
	RepeaterRemove string

	// NotificationCount is the bell's accessible name, "%d unread
	// notifications" — the count the anchor carries, said in words.
	NotificationCount string

	// StepBack, StepNext and StepSubmit are the wizard's three
	// controls; Next is the one weighted action until the last step
	// makes it Submit. StepOf is the rail's name, "Step %d of %d",
	// and StepName is one dot's, "Step %d: %s" with the step's
	// heading as %s.
	StepBack   string
	StepNext   string
	StepSubmit string
	StepOf     string
	StepName   string

	// TableOfContentsLabel names the contents navigation, which is a
	// landmark a screen reader jumps to by name. A short prepositional
	// phrase naming what the list is of.
	TableOfContentsLabel string

	// ComboboxLoading is what the status region says while results
	// are being fetched. One word or a short phrase.
	ComboboxLoading string
	// ComboboxNoResults is what the status region says when a query
	// matched nothing.
	ComboboxNoResults string
	// ComboboxResultCount announces how many results a query found,
	// "{n}" where the count goes — the module writes the count in when
	// the results arrive, so the sentence travels as an attribute and
	// the placeholder is a name, not a fmt verb.
	ComboboxResultCount string
	// ComboboxResultsLabel names the listbox: "<Label> results" is
	// assembled at render, so the word here is the noun.
	ComboboxResultsLabel string

	// CarouselSlide names one slide and the total, "Slide {n} of
	// {total}" — {n} and {total} are filled at render (the module
	// re-says it into the status after each step).
	CarouselSlide string
	// CarouselGoTo names a dot control, "Go to slide {n}".
	CarouselGoTo string

	// JSONObject names an object node in a JSON tree. One word.
	JSONObject string
	// JSONArray names an array node in a JSON tree. One word.
	JSONArray string
	// JSONNull is the null literal. One word.
	JSONNull string
	// JSONTrue / JSONFalse are the boolean literals. One word each.
	JSONTrue  string
	JSONFalse string
	// JSONEmptyObject / JSONEmptyArray are what an empty collection
	// renders: the literal braces/brackets. One token each.
	JSONEmptyObject string
	JSONEmptyArray  string
	// JSONTruncated is the mark a truncated string ends with.
	JSONTruncated string

	// ThereIsAProblem heads the list a failed submit focuses. A short
	// sentence; it names the fact, the list names each field.
	ThereIsAProblem string

	// BreadcrumbsLabel names the trail's navigation landmark, which a
	// screen reader jumps to by name. One word.
	BreadcrumbsLabel string

	// SortableItemRole is the roledescription every row carries: it
	// tells a screen reader what kind of thing the row is before any
	// key is pressed. Two words.
	SortableItemRole string
	// SortableDragLabel names one row by what it offers: a format
	// taking the row's visible label as %s, applied at render.
	SortableDragLabel string
	// SortableGrabbed is said when a row is picked up, with {label}
	// where the row's name goes. Substituted by the runtime when the
	// grab happens, so the sentence travels as an attribute.
	SortableGrabbed string
	// SortablePosition says where the grabbed row now is, with
	// {position} and {list} (the list's own name) written in by the
	// runtime after each move.
	SortablePosition string
	// SortableMoved says the row changed lists, with {list} and
	// {position}: the kanban crossing, announced after it lands.
	SortableMoved string
	// SortableSaved confirms a commit the server accepted.
	SortableSaved string
	// SortableReverted says a failed commit put the rows back.
	SortableReverted string
	// SortableCancelled says Esc put the grabbed row back where it
	// started, uncommitted.
	SortableCancelled string
	// SortableConflictReverted says a 409 was reconciled by putting
	// the rows back.
	SortableConflictReverted string
	// SortableConflictRefreshed says a 409 was reconciled by
	// replacing the list with the server's own rows.
	SortableConflictRefreshed string

	// MultiSelectPlaceholder is what the chips strip says when
	// nothing is picked. A short phrase; the chips replace it the
	// moment one is.
	MultiSelectPlaceholder string
	// MultiSelectRemoveLabel names a chip's remove control, with
	// {label} where the picked option's name goes — the runtime
	// writes it in when it builds the chip.
	MultiSelectRemoveLabel string
}

Strings are the strings the components say. Every field defaults to the English the components rendered before this type existed, so a nil or partially-set value is safe; DefaultWords returns them all and ProbeWords returns one probe token per field.

Fields holding a %s or %d are format strings, applied with fmt.Sprintf at the site that owns the numbers. {n} is substituted by the runtime, not the server: those strings travel as data-* attributes and the count is written in when it is known.

func DefaultStrings

func DefaultStrings() *Strings

DefaultStrings returns a fresh copy of the English defaults, every field set. Fresh so a caller cannot mutate the package's copy through it.

func ProbeStrings

func ProbeStrings() *Strings

ProbeStrings returns a Strings whose every field is its own name in angle brackets — formats as the name plus their placeholders, so `<RemoveLabelled env=prod>` renders where "Remove env=prod" would. A render against it shows exactly which words came through the table; a real English word in one is a word that bypassed it.

func (*Strings) Resolve

func (s *Strings) Resolve() *Strings

Resolve is how a component reaches its strings: the caller's when a layer above resolved them from the request, the harness's probe when a test is looking, and the English defaults otherwise. A caller's Strings may be partial: every empty field falls back to its English default, so a Strings that sets one string does not silently unname the reveal button. Safe on a nil receiver, which is what an unset prop is.

type SwitchProps

type SwitchProps struct {
	// Name is the key the switch submits under when on.
	Name string
	// Value overrides what an on switch submits. Empty submits "on",
	// the HTML default, which is right for a lone "email me" toggle.
	Value string
	// Label is the visible text beside the track. Required, same rule
	// as every choice.
	Label    string
	Checked  bool
	Disabled bool

	ID string
	// Extra attrs land on the input, the control that submits.
	Extra html.Attrs
}

SwitchProps configures a toggle.

type SystemBannerProps

type SystemBannerProps struct {
	// ID is the message's identity, not just the element's: the
	// runtime remembers it so the same message is not shown twice.
	// Required.
	ID string
	// Tone is "info" (the default), "warning", "danger" or
	// "success". The class map looks it up as root--<tone>, and the word
	// a screen reader hears is derived from it, so a tone nobody
	// spelled is refused rather than silently rendered untinted.
	Tone string
	// Title is the headline. Required: "Connection lost" is the
	// message; the detail is detail.
	Title string
	// Text is the detail, in prose.
	Text string
	// Action is the one control the message offers — "Retry now",
	// "View the deploy". One, because a system banner is a bar
	// across the top of the page, not a form.
	Action render.HTML
	// Dismiss renders the dismiss button. Nil means yes: a system
	// message the reader cannot send away is furniture that outstays
	// its news, and the cases that want it gone — the offline
	// banner, whose ending is the reconnect — say so explicitly:
	// Dismiss on an Offline banner is refused at render.
	Dismiss *bool
	// DismissLabel names the dismiss control. Defaults to
	// "Dismiss: <Title>", because three banners each called
	// "Dismiss" say which nothing.
	DismissLabel string
	// Shown renders the banner visible. The default is hidden: the
	// banner ships hidden and something shows it.
	Shown bool
	// Icon is decoration beside the title, hidden from assistive
	// technology (the tone word in words carries the severity).
	Icon render.HTML
	// Offline marks this banner as the built-in connection message.
	// The root carries data-hui-system-offline for the module that
	// binds it to show when the framework reports the connection lost
	// and hide on reconnect — so it always ships hidden, and Shown on
	// an Offline banner is refused: that module owns it.
	Offline bool

	ExtraAttrs html.Attrs

	// Parts: attrs only. Nothing here is fillable — Action
	// already takes the page's own control, and everything else a
	// banner draws is what a screen reader is given to tell one
	// message from another.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

SystemBannerProps is a message about the system rather than the page: offline, a deploy in progress, maintenance, a new version.

The difference from an Alert is who owns it. An alert is part of a page's answer; a system banner is placed once at the top of the shell, above every page, and what it says is true of the system the reader is standing in. That is also why it ships hidden: the slot is always there, and a message arrives in it — the runtime shows the offline one when the framework reports the connection lost, and anything else is a server render or an island swap into the slot.

type TOCItem

type TOCItem struct {
	// ID is the fragment id of the heading this entry links to,
	// without the leading #. Required.
	ID string
	// Label is the entry's visible text. Required.
	Label string
	// Level is the heading's level, 1 to 6; 0 takes 2. It names the
	// item's variant (h2, h3, …) so a class map can indent by depth.
	Level int
}

TOCItem is one entry.

type Tab

type Tab struct {
	// Label is the tab's visible text. Required.
	Label string
	// Panel is the tab's panel content.
	Panel render.HTML
	// Href overrides the fragment href (a same-origin deep link).
	Href string
	// Disabled removes the tab from the keyboard rotation.
	Disabled bool
}

Tab is one tab: its label and its panel's content.

type TableOfContentsProps

type TableOfContentsProps struct {
	// Label names the landmark. Empty takes
	// Strings.TableOfContentsLabel.
	Label string
	// Items are the entries, in document order. Required and
	// non-empty: an empty contents landmark is a name with nothing
	// under it, and the no-script contract is the rendered list.
	Items []TOCItem
	// TargetSelector is the CSS selector of the content region whose
	// headings the module watches to mark the active entry. Empty
	// renders a purely static list: every link works, nothing is
	// marked.
	TargetSelector string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root and the entries. No part is fillable:
	// the list is the contract.
	Parts   Parts
	Strings *Strings
}

TableOfContentsProps configures the contents navigation.

type TableProps

type TableProps struct {
	// Columns is the column definitions. Required.
	Columns []Column
	// Rows is the rendered rows.
	Rows []Row
	// Caption is the table's caption: text, escaped. A table is named
	// by its caption and by nothing else — there is no AriaLabel —
	// and a caller who wants the table named gives one.
	Caption string

	// SortBy is the active sort column's Key. Empty means no column
	// is sorted.
	SortBy string
	// SortDir is the active sort's direction. Empty means ascending,
	// the direction an inactive column's anchor sorts by.
	SortDir SortDir

	// Path is the screen's own path: each sort href is it plus the
	// carried query, the sort parameters replaced. Empty means the
	// current document — a relative "?query" href. When set it must
	// be same-origin: a sort anchor pointing off-origin is a mistake
	// refused at render.
	Path string
	// Query is the query the screen's URL already carries — the
	// search, the filters, the page — and survives a sort beside the
	// sort keys.
	Query url.Values
	// SortParam and DirParam name the sort key and direction
	// parameters. They default to "sort" and "dir".
	SortParam string
	DirParam  string

	// Island is where a sort goes when the table is embedded in a
	// region rather than being the page: the sort anchors then carry
	// the RPC contract beside their hrefs — the page without script,
	// the region update with it, the URL written after the swap.
	// Optional, the Form posture: the URL is the truth for a list,
	// and a list screen's sort anchors are plain navigations the
	// client router intercepts when script is present. An Island
	// that looks wired and is not is refused, whichever shape the
	// table renders.
	Island Island

	// Empty renders in the body's one row when there are no rows, in
	// a cell spanning every column. Nil renders an empty <tbody>;
	// the head stays either way — an empty result still has named
	// columns, and the sort controls stay usable.
	Empty render.HTML
	// Summary is a sentence about the result window the caller owns,
	// e.g. "Showing 8 of 10". Appended to the sort sentence the
	// announcement carries, with a space, when given: the primitive
	// knows the sort, and only the caller knows the window.
	Summary string
	// Footer renders after the scroll region, as its sibling inside
	// the root, never inside the table, so a pager's nav landmark
	// never nests in a table. Nil renders nothing — the scroll region
	// is the root's only child.
	Footer render.HTML

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on every part. Nothing here is fillable — the
	// cells are the caller's own content, and a slot that replaced a
	// header would drop the sort control inside it.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

TableProps configures a table.

type TabsProps

type TabsProps struct {
	// Name is the signal the selection lives in. Required: the strip's
	// whole contract is that clicking a tab writes the signal and the
	// wrapper mirrors it.
	Name string
	// Tabs, in order. Required, at least one.
	Tabs []Tab
	// Active is the selected tab's index. Out-of-range takes 0.
	Active int
	// VacateHidden ships hidden panels EMPTY with their content in an
	// adjacent JSON stash the module restores on first show, so
	// page-scoped locators cannot match hidden text.
	VacateHidden bool
	// StateAttrs mirrors data-state="active"/"inactive" onto every tab
	// after client-side switches, the attribute Radix-style ports pin
	// their locators to.
	StateAttrs bool
	// ID is the prefix the tab and panel ids derive from ("<ID>-tab-<n>"
	// and "<ID>-panel-<n>"). Empty takes Name; a page with two strips
	// on one signal-distinct name can still disambiguate ids with it.
	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root, the tabs and the panels.
	Parts Parts
}

TabsProps configures a tab strip.

type TagInputProps

type TagInputProps struct {
	// Name is the form-field name. Required: every committed value
	// submits under it, as the standard repeated-key pattern.
	Name string
	// Label is the visible label. Required.
	Label string
	// Values are the committed tags, rendered as chips and as hidden
	// inputs — the list on screen and the values in the form are the
	// same set, which is the whole point of rendering the chips at
	// all: what a reader sees remove is what a submit stops carrying.
	Values []string
	// Placeholder is the draft input's placeholder. A placeholder is
	// not a name; the label is.
	Placeholder string
	// MaxLength caps one tag's length, in characters. Zero is no cap;
	// negative is refused. The cap applies to the rendered values
	// too, so a server value longer than the cap cannot submit
	// unchanged while a typed one is refused.
	MaxLength int
	// Help is the rule the values obey.
	Help string
	// Disabled takes the chips' remove controls, the draft input and
	// the add control out.
	Disabled bool

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part. Strings carry the add
	// control's name, the remove controls' names, and the two
	// sentences the status says after a chip operation.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

TagInputProps configures a free-form tag field.

type TagProps

type TagProps struct {
	// Label is the visible text. Required.
	Label string
	// Icon renders before the label.
	Icon render.HTML
	// DismissHref adds the dismiss control, an × that keeps a real
	// href: removing a filter is the server's decision, and the href
	// is the dismiss that needs no script.
	DismissHref string
	// Island is where the dismiss goes with script: removing a filter
	// is an in-page state change, so the × carries the RPC contract
	// beside its href — the page without script, the region update
	// with it, and the URL written after the swap. Required when
	// DismissHref is set; ignored otherwise.
	Island Island
	// DismissAriaLabel names the × for screen readers. Defaults to
	// "Remove <Label>".
	DismissAriaLabel string
	// Href makes the whole chip an anchor — a filter link — with the
	// label and any dismiss inside it. The same anchor policy as
	// every href this package writes; one chip, one label, whatever
	// shape it renders.
	Href string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root and the dismiss control.
	// Strings carry the dismiss control's name.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

TagProps is a tag: a badge-shaped chip standing in for an active filter, removable in place.

type TextareaProps

type TextareaProps struct {
	Name string
	// DescribedBy is the aria-describedby a Field hands down. It is
	// the whole reason a hint or an error reaches assistive tech: the
	// text is on screen either way, but without this attribute a
	// screen reader user hears the label and nothing else — no
	// format hint, and no reason the control is red.
	DescribedBy string
	Value       string
	// Placeholder is the prompt shown when empty.
	Placeholder string
	// Rows sets the rows attribute. Zero omits it and lets the
	// stylesheet size the box, so an unspecified height stays the
	// system's decision rather than the caller's guess.
	Rows     int
	Required bool
	Disabled bool
	Invalid  bool
	// Autogrow opts the control into the core runtime's auto-resize
	// (textarea.js: every input event resets the height to the
	// scrollHeight, so the field always shows all its content). It is
	// the one data-fui-* attribute a component here renders, and it
	// is a prop rather than an extra because every seam a caller can
	// reach — Safe, the part-attrs sanitiser — refuses the prefix
	// precisely so decoration cannot become a request; autogrow is a
	// behaviour of the control itself, not a decoration, and the
	// styled layer's TextArea has no other way to say it.
	Autogrow bool

	ID    string
	Extra html.Attrs
}

TextareaProps configures a multiline control.

type TimelineProps

type TimelineProps struct {
	// Label names the list for assistive tech — "Deploy history".
	Label  string
	Events []Event

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the list and every part it draws.
	Parts Parts
}

TimelineProps is a sequence of events.

type ToastProps

type ToastProps struct {
	// Tone is one of info, success, warning, danger. The class map
	// colours the row from it; the tone word carries it to a reader.
	Tone string
	// Icon is the decorative glyph, hidden from readers: the tone
	// word is what tells them the kind of news. Empty renders no icon.
	Icon render.HTML
	// Title is the headline; Body is the detail. One of them is
	// required — a toast that says nothing is decoration.
	Title string
	Body  string
	// DismissHref makes the toast dismissable with a link that keeps
	// a real href, and requires a complete Island: dismissing an
	// in-page region is an in-page state change, never a route.
	DismissHref  string
	DismissLabel string
	// TTLMS is how long the module keeps the toast before it goes, on
	// the runtime path. Zero means it stays until dismissed. Negative
	// is refused.
	TTLMS int
	// Live is how the toast interrupts. The default is polite: a
	// toast is an arrival, and arrivals wait their turn; assertive is
	// for the failures a reader must hear before doing anything else.
	Live Live

	// Island is where the dismiss goes with script. Required when
	// DismissHref is set; ignored otherwise.
	Island Island

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on every part. Strings carry the tone
	// word and the dismiss control's name.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

ToastProps configures one toast.

type ToastStackProps

type ToastStackProps struct {
	// Label names the stack for assistive technology. Required: the
	// stack is a landmark a reader can jump to, and an unnamed one is
	// a hole.
	Label string
	// Toasts are the rendered rows. The runtime path inserts into the
	// stack; the server-rendered rows are already home.
	Toasts []render.HTML
	// Max is how many rows the module keeps before dropping the
	// oldest. Default 4; zero takes the default; negative is refused.
	Max int

	ID         string
	ExtraAttrs html.Attrs
	Parts      Parts
	// Strings carry the dismiss label template a runtime-inserted
	// toast names itself by ("%s" where the toast's title goes).
	Strings *Strings
}

ToastStackProps configures the one stack a layout mounts.

type ToggleActionProps

type ToggleActionProps struct {
	// Endpoint is the URL hit when toggling idle → committed.
	// Required, and same-origin.
	Endpoint string
	// Method defaults to POST and may be PUT, PATCH or DELETE. It
	// applies to both the commit and the untoggle request.
	Method string
	// IdleLabel is the text in the un-committed state. Required.
	IdleLabel string
	// CommittedLabel is the text while committed. Required, and it
	// must differ from IdleLabel: the flip is the signal.
	CommittedLabel string
	// IdleIcon and DoneIcon are decoration beside each label.
	IdleIcon render.HTML
	DoneIcon render.HTML
	// Committed is the state the server knows: render it true when
	// the action is already active, so first paint matches server
	// state instead of flashing through the wrong label.
	Committed bool
	// Group, when set, joins this button to a client-side mutex:
	// committing any button with the same key reverts the
	// previously-committed sibling, with no second request.
	Group string
	// AllowUntoggle lets a click on a committed button revert it to
	// idle. Without it (and without UntoggleEndpoint) the button is
	// sticky once committed, like OptimisticAction.
	AllowUntoggle bool
	// UntoggleEndpoint is the URL hit when reverting committed →
	// idle. Setting it implies AllowUntoggle; when empty the revert
	// flips locally with no request.
	UntoggleEndpoint string
	// Variant and Size are class-map vocabulary.
	Variant string
	Size    string
	// Disabled is the state at render time.
	Disabled bool
	// FailedText is what the status span announces on a failure.
	FailedText string

	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs only, for the same reason as
	// OptimisticActionProps.
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

ToggleActionProps is the three-state cousin: idle → pending → committed, and back, with the initial state shipped from the server.

type ToolbarProps

type ToolbarProps struct {
	ID         string
	ExtraAttrs html.Attrs
}

ToolbarProps configures a toolbar. The toolbar carries no role of its own: role="toolbar" promises arrow-key roving that this script-free package cannot implement, and an unfulfilled promise is worse than none. A caller who ships the keyboard handling can add it through ExtraAttrs.

type ToolbarSearchProps

type ToolbarSearchProps struct {
	// Island is required: a search refilters the list the toolbar
	// sits on, which is an in-page state change — never a route.
	Island Island
}

ToolbarSearchProps is the toolbar's search field: the one child allowed to take the row's slack, and the GET form that makes a search an island update rather than a navigation. The form carries no action, so without script it submits to the page it is on — which is the search's own URL — and no push-state, because the canonical URL is the server's to name in X-Gofastr-Push-State once it knows what matched.

type TreeNode

type TreeNode struct {
	// ID is unique within the tree and becomes the treeitem's element
	// id. Required, and a bare id: letters, digits, `-`, `_` — a
	// space, quote, `#` or markup refuses at render. Unlike
	// SortableItem.Key this is not data: the id pairs with
	// href="#<id>" fragments and signal names, so slugify stored
	// values before they reach the tree.
	ID string
	// Label is the row's visible text. Required.
	Label string
	// Href, when set, makes the row's label a link. A branch with
	// Href keeps its toggle; a dangerous href degrades to a plain
	// label rather than a clickable vector.
	Href string
	// Children are statically-known descendants. Empty for leaves.
	Children []TreeNode
	// LazyPath, when set and Children is empty, makes this a branch
	// whose children load on first expand: the toggle carries the
	// kernel's rpc wiring against LazyPath and the child group is
	// bound to the signal the response swaps in. Children wins when
	// both are set — the children are already there.
	LazyPath string
	// Expanded forces the branch open on first paint.
	Expanded bool
	// Selected sets aria-selected="true" on the treeitem.
	Selected bool
}

TreeNode is one entry in the tree.

type TreeProps

type TreeProps struct {
	// Label is the aria-label on the role="tree" wrapper. Required.
	Label string
	// Nodes are the root-level entries. Required and non-empty: a
	// tree with nothing in it is a landmark that says nothing.
	Nodes []TreeNode
	// LazySignalPrefix names the signal namespace the lazy branches
	// bind their child groups to (each lazy branch's group carries
	// data-fui-signal="<prefix>-<node-id>"). Required when any node
	// uses LazyPath, ignored otherwise.
	LazySignalPrefix string

	// ID is the tree wrapper's element id. Required.
	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs on the root and the rows. No part is fillable: the
	// tree is the contract.
	Parts Parts
}

type ValidationSummaryProps

type ValidationSummaryProps struct {
	// Title heads the summary. Defaults to "There is a problem".
	Title string
	// Level is the title's heading level, 2 by default.
	Level  int
	Errors []FieldError
	// ID names the summary's root. Required, like the control ids the
	// errors link to: the title's id is derived from it, and two
	// summaries on one page without ids would share one title id —
	// breaking both labels and both announcements.
	ID         string
	ExtraAttrs html.Attrs

	// Parts: attrs and binds on the root, the title, the list and
	// its items. Strings carry the title a failed submit
	// focuses).
	Parts Parts
	// Strings are the strings this component says. Nil means the English
	// defaults; a layer above sets them from the request's language.
	Strings *Strings
}

ValidationSummaryProps is the list of everything wrong with a form.

type WizardStep

type WizardStep struct {
	// Heading is the step's name. Required: the rail's dots and the
	// step's own heading say it, and a step with no name is a
	// nameless place in a flow.
	Heading string
	// Description is the supporting line under the heading.
	Description string
	// Fields are the step's form fields.
	Fields []render.HTML
}

WizardStep is one step of the flow.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL