@momoi-labs/kiso 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,8 @@ behavioral reference where one exists.
12
12
  - [Card](card.md): Groups related content and actions with visual separation.
13
13
  - [Checkbox](checkbox.md): Toggles an option in a list or selects multiple values.
14
14
  - [ChipInput](chip-input.md): Collects several structured values in one field, each with its own options.
15
+ - [Form](form.md): Composes fields and explicit submission actions in a native form.
16
+ - [FormActions](form-actions.md): Groups form actions and optional feedback, with inline or sticky placement.
15
17
  - [FormField](form-field.md): Composes Label, a form control, HelperText, and ValidationMessage with consistent ID and ARIA wiring.
16
18
  - [HelperText](helper-text.md): Provides persistent, non-error context for a form control.
17
19
  - [IconButton](icon-button.md): Triggers a compact icon-only action with a required accessible name.
@@ -24,11 +26,17 @@ behavioral reference where one exists.
24
26
  - [Switch](switch.md): Changes one immediately applied boolean setting.
25
27
  - [Textarea](textarea.md): Collects multi-line free-form text.
26
28
  - [ThemeSelector](theme-selector.md): Chooses between following the system colour scheme, forcing light, or forcing dark.
29
+ - [AccentSelector](accent-selector.md): Chooses which accent the interface uses: violet, terracotta, teal, cobalt, or nocturne.
27
30
  - [Tooltip](tooltip.md): Adds nonessential pointer or keyboard context as progressive enhancement.
28
31
  - [ValidationMessage](validation-message.md): Explains a field-level validation error and how to fix it.
29
32
 
30
33
  ## Data
31
34
 
35
+ - [Chart](chart.md): Framed lines and stacked areas with synchronized inspection and exact values.
36
+ - [ChartLegend](chart-legend.md): Tabular series summaries.
37
+ - [Meter / Progress](meter.md): Measurements against limits and task completion.
38
+ - [BarGauge](bar-gauge.md): Labelled bars on a shared scale.
39
+
32
40
  - [CommandPalette](command-palette.md): Searches and runs global actions or navigation from a keyboard-first overlay.
33
41
  - [Dot](dot.md): Adds a decorative status mark beside readable text.
34
42
  - [DropdownMenu](dropdown-menu.md): Presents contextual actions anchored to a specific object or trigger.
@@ -38,10 +46,16 @@ behavioral reference where one exists.
38
46
  - [Search](search.md): Filters visible content such as a list or table.
39
47
  - [Sparkline](sparkline.md): Draws the shape of one metric series at cell size.
40
48
  - [Stat](stat.md): Presents a named metric with optional change and context.
49
+ - [StepBar](step-bar.md): Summarises a run as one segment per step, coloured by state.
50
+ - [StepList](step-list.md): Lists a run's steps with a state, a label, a timing, and an output.
41
51
  - [Table / DataTable](table.md): Presents structured records with optional sorting, selection, filtering, and pagination.
42
52
 
43
53
  ## Navigation and structure
44
54
 
55
+ - [Disclosure](disclosure.md): Native collapsible sections.
56
+ - [TimeRangeControl](time-range-control.md): Presets and exact UTC collection windows.
57
+ - [DashboardGrid](dashboard-grid.md): Responsive twelve-column panel layout.
58
+
45
59
  - [AppShell / ApplicationShell](app-shell.md): Provides low-level columns or the complete shared application frame (sidebar console or top-bar surface).
46
60
  - [BrandMark](brand-mark.md): Decorative letter or icon beside a product name.
47
61
  - [Breadcrumb](breadcrumb.md): Shows the current location within a hierarchy.
@@ -63,6 +77,8 @@ behavioral reference where one exists.
63
77
 
64
78
  ## Required compositions
65
79
 
80
+ - [Form](form.md) with explicit submission includes fields and [FormActions](form-actions.md).
81
+
66
82
  - [FormField](form-field.md) composes [Label](label.md) + [Input](input.md) (or another form control) + [HelperText](helper-text.md) + [ValidationMessage](validation-message.md).
67
83
  - [ApplicationShell](app-shell.md) composes [Sidebar](sidebar.md) + [Navigation](navigation.md) + [Header](header.md) + page content, or a top-bar-only frame with brand in [Header](header.md).
68
84
  - [Header](header.md) composes [Link](link.md) + [IconButton](icon-button.md) + optional [DropdownMenu](dropdown-menu.md).
@@ -0,0 +1,136 @@
1
+ # AccentSelector
2
+
3
+ ## Purpose
4
+
5
+ AccentSelector chooses which accent the interface uses: violet, terracotta,
6
+ teal, cobalt, or nocturne. It is the only sanctioned control for that choice. The theme
7
+ (light, dark, system) is a separate axis with its own control,
8
+ [ThemeSelector](theme-selector.md); the two compose.
9
+
10
+ ## The five values
11
+
12
+ | Value | Meaning | `data-accent` on `<html>` |
13
+ | --- | --- | --- |
14
+ | `violet` (default) | The violet ink and fill on the warm neutrals. | Either no attribute or `data-accent="violet"`. |
15
+ | `terracotta` | Same roles, hue turned to terracotta. Neutrals unchanged. | `data-accent="terracotta"` |
16
+ | `teal` | Same roles, hue turned to teal. Neutrals unchanged. | `data-accent="teal"` |
17
+ | `cobalt` | Same roles, hue turned to cobalt. Neutrals unchanged. | `data-accent="cobalt"` |
18
+ | `nocturne` | The marketing site's palette: cool slate neutrals under the violet ink. | `data-accent="nocturne"` |
19
+
20
+ The attribute may also sit on a container, in which case only that subtree
21
+ takes the accent. A nested `data-accent="violet"` resets to the default. See
22
+ [Accents](../tokens.md#accents) for what the accent does and does not change.
23
+
24
+ An application that offers fewer accents passes the subset it supports; the
25
+ control does not have to show all five.
26
+
27
+ ## Persistence
28
+
29
+ - An explicit choice persists under the key `kiso-accent`, with the value
30
+ `violet`, `terracotta`, `teal`, `cobalt`, or `nocturne`.
31
+ - Read the stored value in a blocking inline script in `<head>`, before first
32
+ paint, and apply it as `data-accent`. Anything later flashes.
33
+ - Storage may be unavailable. Wrap reads and writes so a failure degrades to
34
+ `violet` rather than throwing.
35
+ - An application with a fixed accent does not render the control at all; it
36
+ sets the attribute once, in the document.
37
+
38
+ ## Anatomy
39
+
40
+ 1. **Row label**: the word "Accent", on the left.
41
+ 2. **Pills**: one per value, on the right, as a group. Each pill is a dot in
42
+ that accent's `primary` fill followed by the accent's name.
43
+ 3. **Preview**: below the row, one panel that shows the *selected* accent
44
+ applied: a sidebar strip with a current-item marker, a heading, a link, a
45
+ primary and a secondary button, and a selected row.
46
+
47
+ | Value | Pill | Accessible name |
48
+ | --- | --- | --- |
49
+ | `violet` | violet dot, "Violet" | "Violet" |
50
+ | `terracotta` | terracotta dot, "Terracotta" | "Terracotta" |
51
+ | `teal` | teal dot, "Teal" | "Teal" |
52
+ | `cobalt` | cobalt dot, "Cobalt" | "Cobalt" |
53
+ | `nocturne` | nocturne dot, "Nocturne" | "Nocturne" |
54
+
55
+ The pill names the accent, so no hover is needed to tell which is which. The
56
+ preview answers the choice: it is decorative and hidden from assistive
57
+ technology, because the names already carry the meaning. Each pill and the
58
+ preview carry their own `data-accent`, so their colours resolve under that
59
+ accent with no inline values.
60
+
61
+ The preview may be omitted where the row is inline chrome, such as a header
62
+ toolbar. In a settings card it is always present.
63
+
64
+ ## Layout
65
+
66
+ A configuration row directly below the Theme row in the same card, with the
67
+ preview spanning the row's width beneath it. See
68
+ [Settings](../patterns/settings.md#theme).
69
+
70
+ ```text
71
+ Theme [ ▣ ][ ☀ ][ ☾ ]
72
+ Accent (● Violet)(● Terracotta)(● Teal)(● Cobalt)(● Nocturne)
73
+ ┌──┬──────────────────────────────────┐
74
+ │ │ ▬▬▬▬▬▬ │
75
+ │▬ │ ▬▬▬ │
76
+ │ │ [primary] [secondary] │
77
+ │ │ ░░░░░░░░░░░░░░░░ selected │
78
+ └──┴──────────────────────────────────┘
79
+ ```
80
+
81
+ Narrower than 360px (a sidebar footer), the label stacks over the pills and
82
+ the pills form a two-column grid, so no name wraps mid-word.
83
+
84
+ ## Tokens
85
+
86
+ Pill: height `--size-control-xs`, radius `--radius-full`, border
87
+ `--color-border`, fill `--color-card`, text `--color-muted-foreground` at
88
+ `--type-size-label`, dot `--spacing-md` square in `--color-primary`.
89
+
90
+ Selected pill: border `--color-primary`, fill `--color-accent-surface`, text
91
+ `--color-foreground` at `--type-weight-medium`. The pill is outlined and
92
+ tinted, not filled with `--color-primary`: the fill is already on the dot,
93
+ and a solid pill would compete with the page's actual primary action.
94
+
95
+ Preview: 120px tall, border `--color-border`, radius `--radius-lg`, canvas
96
+ `--color-background`, strip `--color-sidebar`, marker and link `--color-link`,
97
+ buttons `--color-primary` and `--color-card` with `--color-border-strong`,
98
+ row `--color-selected`. Transitions on `--motion-duration-fast` /
99
+ `--motion-easing-standard`.
100
+
101
+ ## States
102
+
103
+ | State | Behavior |
104
+ | --- | --- |
105
+ | default | The offered options, exactly one selected; the preview shows it. |
106
+ | hover | Unselected pill takes `--color-accent-surface` and `--color-foreground`. |
107
+ | focus | Visible ring using `--color-ring`. Never remove it. |
108
+ | selected | Outlined and tinted pill as above, plus the accessible selected state. The preview repaints at once. |
109
+ | disabled | Not a state. The accent is always changeable where the control is shown. |
110
+
111
+ There is no loading state. The change is local and instant; do not wait on a
112
+ server round-trip to repaint, and do not show a Toast for it.
113
+
114
+ ## Accessibility
115
+
116
+ - A group of mutually exclusive options with exactly one selected, each named
117
+ by its visible text.
118
+ - The preview is `aria-hidden`; it repeats what the pill already says.
119
+ - Announce the selection, not the resulting colours.
120
+ - Every accent passes the same [AA gate](../tokens.md#aa-gate) as the default,
121
+ so the control does not need to warn about contrast.
122
+
123
+ ## When NOT to use
124
+
125
+ - A free colour picker. Kiso has five accents; a product colour that is not
126
+ one of them is a token proposal, not an option.
127
+ - A way to mark status or severity. Red, amber, and green are not accents for
128
+ that reason: a primary button in the danger hue reads as destructive.
129
+ - Per-user branding inside a single product. One accent per product, or per
130
+ product area, chosen by the product.
131
+
132
+ ## Related
133
+
134
+ - [tokens](../tokens.md#accents): what an accent changes and how it is built.
135
+ - [ThemeSelector](theme-selector.md): the other appearance row.
136
+ - [Settings](../patterns/settings.md#theme): where the row lives.
@@ -0,0 +1,29 @@
1
+ # BarGauge
2
+
3
+ ## Purpose and anatomy
4
+
5
+ Compare labelled measurements on horizontal bars with a value column.
6
+ Each row composes [Meter](meter.md). All rows share a positive finite `max`
7
+ and start at zero. Give the group a descriptive `label`, including its unit.
8
+ Use [Chart](chart.md) when the task requires history.
9
+
10
+ ## Data, states, and behavior
11
+
12
+ Rows have a stable key, label, and numeric or null value. `formatValue` applies
13
+ the same unit and precision to every known value. Preserve row order across
14
+ refreshes so a reader can compare changes. Bars clip at their bounds while
15
+ text retains the actual number. A missing row value reads "Not collected";
16
+ zero draws an empty track. An empty group reads "No measurements."
17
+
18
+ ## Accessibility
19
+
20
+ The group has an accessible name; each measured row has Meter semantics and
21
+ its own name and value text. Labels and numbers remain visible. There is no
22
+ hover requirement, animation, or keyboard interaction. Wrap long labels and
23
+ keep values aligned. Color does not encode the identity of a row.
24
+
25
+ ## Tokens and implementation
26
+
27
+ Use Meter's track and fill tokens, `--spacing-md` between rows, and
28
+ `--spacing-sm` within each row. No separate charting or Radix primitive is
29
+ required. The old `.bars` vertical mini-bars are unrelated and stay compatible.
@@ -188,3 +188,9 @@ No Radix Card primitive. Structure follows shadcn
188
188
 
189
189
  Ignore shadcn's invitation to hard-code spacing utilities. Card spacing
190
190
  consumes `--spacing-md`, `--spacing-lg`, or `--spacing-xl` only.
191
+
192
+ ## Form submission
193
+
194
+ Use [FormActions](form-actions.md) for form submission controls and feedback.
195
+ It can replace CardFooter at the bottom of a form inside a Card. Do not nest
196
+ the two footers. CardFooter remains a general content-and-actions container.
@@ -0,0 +1,42 @@
1
+ # ChartLegend
2
+
3
+ ## Purpose and anatomy
4
+
5
+ A native summary table for a [Chart](chart.md), available without hovering.
6
+ Each row has the series' numbered label and stroke sample, then min, max,
7
+ arithmetic average, and current. The visible caption names the metric and unit.
8
+
9
+ ## Data and states
10
+
11
+ Accept the same `data` and `series` as the chart. Keep ordering and slots
12
+ identical. Min, max, and average use finite measurements only. Current is the
13
+ measurement at the final timestamp. Missing statistics read "Not collected";
14
+ zero remains zero. An empty window retains the named series and headers.
15
+
16
+ `formatValue` controls all summary cells. Use one unit and precision across
17
+ the table. The chart's exact-values table preserves unrounded source data.
18
+ The legend alone is a summary, not a replacement for the full sample table.
19
+
20
+ ## Accessibility and behavior
21
+
22
+ Use native table, caption, column headers, and row headers. Numeric cells align
23
+ to the end with tabular figures. By default rows are informational. With `onHighlightSeriesChange`, series
24
+ labels become buttons with pressed state from `highlightSeries`. Selection
25
+ emphasizes a series without hiding values. The table scrolls horizontally inside its frame at narrow
26
+ widths. Numbered labels, highlighting, and the exact sample table supplement color.
27
+
28
+ ## Tokens and implementation
29
+
30
+ Text uses `--color-foreground` and `--color-muted-foreground`. Borders use
31
+ `--color-border`; spacing uses `--spacing-xs` and `--spacing-sm`. Series
32
+ swatches consume the same five semantic chart roles as Chart. There is no
33
+ Radix primitive; use a native table.
34
+
35
+ ## Display forms
36
+
37
+ `variant="table"` is the default. `inline` wraps series labels and current
38
+ values into a short row. `sidebar` places them in a column beside the plot,
39
+ falling below it on narrow screens. Both use native description lists.
40
+ `activeTimestamp` selects the sidebar sample, otherwise it shows the last
41
+ sample. `formatTime` formats its timestamp. A missing measurement stays
42
+ "Not collected". Sidebar changes use a polite live region.
@@ -0,0 +1,120 @@
1
+ # Chart
2
+
3
+ ## Purpose
4
+
5
+ Read several metric series across a collection window. pg-probe needs to
6
+ correlate CPU, memory, disk, and database activity at the same instant.
7
+ [Issue #93](https://github.com/momoi-labs/blueprint/issues/93) records that need.
8
+ Use [Sparkline](sparkline.md) for a small, single-series trend.
9
+
10
+ ## Anatomy and forms
11
+
12
+ A named figure contains axes, horizontal grid lines, a plot, an inspection
13
+ readout, [ChartLegend](chart-legend.md), and a disclosure of exact samples.
14
+ `line` is the default. `stacked-area` shows nonnegative contributions to a
15
+ total. No gradients, curves that overshoot observations, or animation.
16
+
17
+ One to five series share a unit and timestamp axis. Put the unit in the figure
18
+ label, such as "CPU by state (%)". Give each series a unique key and label.
19
+ An optional slot from 1 to 5 fixes its color and numbered label across
20
+ panels. Slots must be unique within a chart. Keep ascending slot order in stacks. Never encode health with a slot.
21
+
22
+ In standard layout, `height` defaults to 200 pixels. Width follows the parent. Axes and their labels
23
+ remain visible. `min` and `max` establish comparable scales across panels;
24
+ otherwise lines use their extent and stacks start at zero. Stacks reject
25
+ negative values. Use lines for signed data.
26
+
27
+ ## Data and gaps
28
+
29
+ `data` contains `{ timestamp, values }`, where timestamps are epoch
30
+ milliseconds in strictly increasing order. Supply every expected collection
31
+ timestamp, including slots the collector missed. Values are keyed by series.
32
+ A null, absent, or nonfinite measurement is unavailable, never zero. The
33
+ component does not infer a sampling interval or interpolate missing samples.
34
+
35
+ Lines break at gaps. A stack breaks across all series when any contribution
36
+ is missing, because its total is unknown. The readout and tables retain known
37
+ contributions at that timestamp. An isolated sample draws a point.
38
+
39
+ The summary ignores missing samples for min, max, and arithmetic mean. Current
40
+ means the final timestamp, including an unavailable value. It never silently
41
+ substitutes an older sample. Statistics describe the supplied window only.
42
+
43
+ ## Interaction and accessibility
44
+
45
+ The figure has a visible caption and an accessible name. Tab focuses the plot;
46
+ Left and Right inspect adjacent samples through Recharts' accessibility layer.
47
+ Pointer hover and touch inspect the same readout. The floating readout or sidebar names the
48
+ selected timestamp and every series, including "Not collected" values.
49
+
50
+ Pass the same `syncId` to related charts to synchronize by timestamp. Products
51
+ must supply the same expected timestamp grid and window to that group. Array
52
+ positions alone do not establish correspondence. Panels retain their own
53
+ series and scales.
54
+
55
+ Series use continuous strokes. Numbered labels, per-series highlighting,
56
+ and the exact table provide identification beyond color. Select a legend
57
+ label with pointer, touch, Enter, or Space to highlight that series; select
58
+ it again to clear. The active label is underlined and exposes `aria-pressed`.
59
+ Other series stay in the plot and in every readout. The
60
+ "View exact values" disclosure exposes every sample as a native table, with
61
+ full UTC timestamps and unrounded source numbers. Include units in the chart
62
+ label; formatters change display text, never the exact table. Scrolling stays
63
+ inside the table at narrow widths.
64
+
65
+ Tab reaches the exact-values summary. Enter or Space opens it. No essential
66
+ information requires hover, color discrimination, animation, or a live
67
+ announcement on every background refresh. Keep series labels descriptive.
68
+
69
+ ## States
70
+
71
+ - Empty or wholly unavailable data: retain the caption, summary, exact table,
72
+ and plot height; show "No collected samples in this window."
73
+ - Partial collection: draw gaps and preserve the available numbers.
74
+ - One sample: show a point and the tables.
75
+ - Loading: compose a [Skeleton](skeleton.md) at the expected plot height.
76
+ - Error: compose an [Alert](alert.md) with the cause and retry action. Label
77
+ retained data as stale. An empty chart is not an error message.
78
+
79
+ ## Tokens and implementation
80
+
81
+ Series use `--color-chart-1`, `--color-chart-2`, `--color-chart-3`,
82
+ `--color-chart-4`, and `--color-chart-5`. Axes use
83
+ `--color-muted-foreground`; grid lines use `--color-border`; the crosshair
84
+ uses `--color-border-strong`. Focus uses `--color-focus`. The readout uses
85
+ `--color-elevated-surface`, `--color-foreground`, and `--shadow-md`.
86
+
87
+ ## Presentation options
88
+
89
+ | Prop | Values | Default |
90
+ | --- | --- | --- |
91
+ | `variant` | `line`, `stacked-area` | `line` |
92
+ | `layout` | `standard`, `compact`, `split` | `standard` |
93
+ | `legend` | `table`, `inline`, `sidebar` | `table` |
94
+ | `highlightSeries` | A series key or null | Local selection |
95
+
96
+ Standard uses a 200-pixel plot; compact uses 165 pixels and tighter spacing.
97
+ Split gives each series a labelled 90-pixel lane on one shared numeric scale,
98
+ with synchronized timestamps. Explicit `height` overrides the plot or lane
99
+ height. Split accepts only line charts; combining it with stacked-area throws
100
+ an error because a stack must retain one common plot.
101
+
102
+ Table keeps min, max, average, and current visible. Inline shows current values
103
+ in a wrapping legend. Sidebar shows current values at rest and the inspected
104
+ instant on hover, keyboard navigation, or touch, without a floating readout.
105
+ At narrow widths it moves below the plot. All forms keep the exact table.
106
+
107
+ `highlightSeries` makes highlighting controlled; pass null to clear it. Pair
108
+ it with `onHighlightSeriesChange` to update selection from the legend. Omitting
109
+ it enables local selection. Unknown keys produce no highlight, including when
110
+ a series disappears after a data change. Highlighting changes emphasis only;
111
+ it never removes contributions or changes the stack's total or scale.
112
+
113
+ Keyboard instructions remain available through the plot's accessible
114
+ description instead of taking permanent space above every plot.
115
+
116
+ The React component composes Recharts `ComposedChart`, `Line`, `Area`, axes,
117
+ and Tooltip, with animation disabled and `connectNulls={false}`. Synchronization
118
+ uses `syncMethod="value"`. See the [Recharts API](https://recharts.github.io/en-US/api/AreaChart/).
119
+ The existing `.chart` SVG helpers remain compatible; the component frame uses
120
+ `.framed-chart` to avoid changing older SVG consumers.
@@ -0,0 +1,28 @@
1
+ # DashboardGrid
2
+
3
+ ## Purpose and anatomy
4
+
5
+ Arrange metric panels in a responsive twelve-column grid. DashboardGrid owns
6
+ layout; DashboardPanel owns a column span and wraps a Card or other widget.
7
+ The grid adds no visual panel treatment or ARIA roles of its own.
8
+
9
+ ## Sizes and responsive behavior
10
+
11
+ Panel `span` accepts 3, 4, 6 (default), 8, or 12. Above 1024 pixels it uses that
12
+ many columns. At 1024 pixels and below, panels use six columns, except full
13
+ width panels which keep twelve. At 640 pixels and below, every panel uses one
14
+ full-width column. Internal tables may scroll; the page must not overflow.
15
+
16
+ ## States and accessibility
17
+
18
+ Each widget owns its loading, empty, stale, and error states. Reserve its
19
+ space with Skeleton while loading. The layout never reorders DOM content or
20
+ adds tab stops. Reading order, focus order, and visual order stay aligned.
21
+ Give sections meaningful headings and each chart its own name.
22
+
23
+ ## Tokens and composition
24
+
25
+ Use `--spacing-lg` for gaps. Span is layout metadata, not a new spacing token.
26
+ Compose [Card](card.md), [Chart](chart.md), [Disclosure](disclosure.md), and
27
+ [TimeRangeControl](time-range-control.md) under the
28
+ [Dashboard pattern](../patterns/dashboard.md). CSS Grid needs no dependency.
@@ -0,0 +1,29 @@
1
+ # Disclosure
2
+
3
+ ## Purpose and anatomy
4
+
5
+ Collapse a related section without leaving the page. A native `details`
6
+ contains a `summary` and content region. `summary` provides the visible name;
7
+ children provide the section content. Do not put links or buttons inside the
8
+ summary. Multiple sections may remain open independently.
9
+
10
+ ## States and behavior
11
+
12
+ Closed is the default. Pass native `open` to start open. The browser maintains
13
+ open and closed states and dispatches `onToggle`. Closed content remains in
14
+ the document but is hidden and removed from keyboard navigation. Preserve
15
+ application state inside the content; opening does not fetch data by itself.
16
+
17
+ ## Accessibility
18
+
19
+ Use native semantics and the browser's disclosure marker. Tab reaches the
20
+ summary; Enter or Space toggles it and focus stays there. Content follows the
21
+ summary in DOM order. Keep heading levels appropriate to the surrounding page.
22
+ On a coarse pointer, the summary is at least the minimum touch target height.
23
+ Do not animate the content or add redundant expanded-state ARIA.
24
+
25
+ ## Tokens and implementation
26
+
27
+ Spacing uses `--spacing-sm`; the summary uses `--type-weight-medium`.
28
+ Focus uses `--color-focus`. Touch targets use `--size-touch-min` only inside
29
+ `@media (pointer: coarse)`. Native details needs no Radix dependency.
@@ -0,0 +1,145 @@
1
+ # FormActions
2
+
3
+ ## Purpose
4
+
5
+ FormActions gives an explicit-submit form one place for its primary action,
6
+ secondary actions, and optional feedback. It works for sign-in, creation,
7
+ editing, and applying choices. It does not assume that submission saves a record.
8
+
9
+ ## Anatomy
10
+
11
+ 1. An action region after the form content.
12
+ 2. An optional visible message in a persistent status region.
13
+ 3. An action group with one primary Button and any secondary actions.
14
+
15
+ The message comes before the buttons in DOM order. Buttons align to the end
16
+ of the region. The layout wraps when the available width cannot hold both
17
+ message and actions. Labels and messages can wrap without horizontal scrolling.
18
+
19
+ ## React API
20
+
21
+ | Prop | Default | Meaning |
22
+ | --- | --- | --- |
23
+ | `children` | None | Consumer-provided Buttons or navigation Links. |
24
+ | `message` | None | React content rendered inside the status region. |
25
+ | `tone` | `neutral` | `neutral`, `warning`, or `danger` presentation. |
26
+ | `sticky` | `false` | Stick to the bottom of the nearest scrolling ancestor, within the form. |
27
+ | Native div props | None | Includes `className`, `style`, `ref`, and ARIA attributes. |
28
+
29
+ ```tsx
30
+ <FormActions
31
+ sticky
32
+ tone={error ? "danger" : dirty ? "warning" : "neutral"}
33
+ message={error || (dirty ? "Unsaved changes." : undefined)}
34
+ >
35
+ {dirty && <Button type="button" onClick={discard}>Discard changes</Button>}
36
+ <Button type="submit" variant="primary" disabled={saving || !dirty}>
37
+ {saving ? "Saving..." : "Save changes"}
38
+ </Button>
39
+ </FormActions>
40
+ ```
41
+
42
+ Loading indicators and `aria-busy` belong on the submitting Button. The
43
+ consumer prevents duplicate submission and decides whether cancellation is safe.
44
+ No buttons, labels, dirty tracking, or network behavior are built in.
45
+
46
+ ## Tones and messages
47
+
48
+ | Situation | Presentation |
49
+ | --- | --- |
50
+ | Initial or create | Neutral; no message required. |
51
+ | Unsaved edits | Warning with a message identifying the unsaved changes. |
52
+ | Submitting | A short progress message and loading submit Button. |
53
+ | Submission failed | Danger with a recovery message; preserve entries. |
54
+ | Saved | Neutral confirmation when the form stays open; clear dirty state. |
55
+
56
+ Tone does not encode business state. A product decides what "saved, awaiting
57
+ application" means and supplies suitable copy. FormActions does not model
58
+ machines, deployments, or synchronization. Use text to communicate meaning;
59
+ color alone is insufficient. An icon and bold lead are optional.
60
+
61
+ A new record can show Cancel and Create without a message. Discard appears
62
+ only when there are edits to discard. Avoid an always-visible "Saved" caption
63
+ that remains unchanged after editing.
64
+
65
+ ## Placement and scrolling
66
+
67
+ For page scrolling, put FormActions last inside Form and pass `sticky`.
68
+ Its bottom offset defaults to zero. When the page has outer padding, set
69
+ `inset-block-end` to the same spacing token so the floating actions respect
70
+ that inset. Cover the space below the floating bar with the page background,
71
+ including the container border, so scrolling fields do not show through the
72
+ outer padding. An offset alone does not create that visual separation.
73
+ When page scrolling moves a marked Card behind sticky actions, keep the top
74
+ marks on the Card and move both bottom corner marks to the sticky action edge.
75
+ Draw them above the inset cover so each corner retains its horizontal and
76
+ vertical tick. Do not leave
77
+ a second pair attached to the scrolling Card bottom.
78
+ For example, use `var(--spacing-xl)` for a page padded by
79
+ `--spacing-xl`, and match any responsive padding changes. The form must remain
80
+ in document flow: an ancestor with scrolling overflow changes the sticky
81
+ container.
82
+
83
+ For a bounded panel, put Form inside a height-constrained `form-scroll` region.
84
+ That region owns overflow and has no padding. Put padding on `form-body`
85
+ instead. Actions then span the container width without negative margins.
86
+ Form fills at least the panel height and pushes actions to its bottom.
87
+
88
+ ```tsx
89
+ <TabsContent value="general" className="form-scroll" style={{ height: "28rem" }}>
90
+ <Form onSubmit={save}>
91
+ <div className="form-body">{/* FormFields */}</div>
92
+ <FormActions sticky>{/* Buttons */}</FormActions>
93
+ </Form>
94
+ </TabsContent>
95
+ ```
96
+
97
+ Keep an outer Card's corner marks outside the scrolling region. Do not nest
98
+ FormActions inside CardFooter or DialogFooter; it provides its own border and
99
+ padding. CardFooter stays a general visual container without form status.
100
+
101
+ If an existing scroller must retain padding, the consumer must account for it:
102
+ set `style={{ insetBlockEnd: "calc(-1 * var(--spacing-lg))" }}` when the
103
+ scroller's bottom padding is `--spacing-lg`. Align the form with the horizontal
104
+ edges separately. Use lengths such as `0px` when supplying values to CSS math.
105
+ Prefer the unpadded composition above for new screens.
106
+
107
+ The background layers semantic tint over an opaque card surface. Scrolled
108
+ content must not show through the message or buttons. The bar stops at the
109
+ form's end; it is not fixed to the viewport across unrelated content.
110
+
111
+ ## Accessibility
112
+
113
+ Only the message region has `role="status"` and `aria-atomic="true"`. Keep it
114
+ mounted when empty so updates can be announced. Do not put these attributes
115
+ on the entire action region, and do not put interactive controls in `message`.
116
+
117
+ Buttons retain their native semantics and tab order. The primary Button has
118
+ `type="submit"`; secondary Buttons have `type="button"`. Use a Link for an
119
+ actual navigation destination. Avoid duplicate announcements from an inline
120
+ message and a Toast for the same event.
121
+
122
+ Field validation stays beside its field. Keep form-level failures visible
123
+ until the person edits or retries. Verify sticky actions at narrow widths,
124
+ zoom, and keyboard focus. Shared scroll spacing reserves room for typical
125
+ wrapped actions; consumers must increase that space for unusually tall bars.
126
+
127
+ ## Tokens
128
+
129
+ Spacing uses `--spacing-sm`, `--spacing-md`, and `--spacing-lg`. Scroll spacing
130
+ uses `--spacing-4xl`. Text uses `--type-size-label` and
131
+ `--type-weight-semibold`. The base uses `--color-card`,
132
+ `--color-card-foreground`, and `--color-border`. Warning uses
133
+ `--color-warning-surface` and `--color-warning-border`; danger uses
134
+ `--color-danger-surface` and `--color-danger-border`.
135
+
136
+ ## When not to use
137
+
138
+ Do not add submission actions to immediate preferences or automatic filters.
139
+ Do not use this component for page-wide record actions unrelated to the form,
140
+ a generic Card footer, or a toolbar for selecting table rows.
141
+
142
+ ## Radix/shadcn mapping
143
+
144
+ FormActions is a Kiso composition of native grouping, a status region, and
145
+ Buttons. It does not add a keyboard interaction model or a Radix dependency.
@@ -7,6 +7,9 @@ identification, entry, guidance, and field-level feedback so their visual and
7
7
  accessible relationships remain intact. FormField is not a primitive and does
8
8
  not replace the semantics of its children.
9
9
 
10
+ Use [Form](form.md) and [FormActions](form-actions.md) for the whole form and
11
+ its submission actions. FormField remains responsible for one control.
12
+
10
13
  ## Anatomy
11
14
 
12
15
  The canonical composition is: