Enforce shared metric and description layouts
Dependency Audit / dependency-audit (push) Successful in 1m39s
Deployment Installer / deployment-installer (push) Successful in 6s
Security Audit / security-audit (push) Successful in 11m3s

This commit is contained in:
2026-08-18 11:30:39 +02:00
parent 5efb0eea6f
commit 5bb8028147
4 changed files with 230 additions and 25 deletions
@@ -115,18 +115,29 @@ The current structural vocabulary is:
- `FormSection` owns form-section heading, description, actions, content flow,
separation, and panel presentation. It does not own field values,
validation, permissions, or domain wording.
- `MetricGrid` owns the responsive grouping around `MetricCard`: fixed one-to-five
columns or auto-fit, minimum card width, density, surrounding rhythm, and a
named collapse point. Modules provide the metric, tone, and consequence;
they do not recreate the group grid or reach across module CSS to size it.
- `DescriptionList` and `DescriptionItem` own semantic property presentation.
The stacked variant supports compact multi-column facts; the inline variant
supports one-column term/value rows with a standard term width. Both own
density, wrapping, and responsive collapse while modules retain the terms,
values, provenance, and actions.
- `Dialog` owns size and administration variants, body padding, description,
notices, and fixed footer placement. `DialogActions`, `DialogForm`, and
`DialogSection` own the footer action flow, native form flow, and body
grouping used inside it. Modules compose fields and consequences rather than
recreating dialog anatomy.
Raw toolbar tags, the former generic grid classes, raw dialog-form wrappers,
and module-local definitions of these contracts are rejected by the focused
workspace checks. The next promotion candidates are metric groups,
description/property lists, filter/search composition, responsive data
summaries, and unified empty/error state composition. Their APIs must remain
composable; a central component is not a single oversized page template.
Raw toolbar tags, the former generic grid and property-list classes, raw
dialog-form wrappers, and module-local definitions of these contracts are
rejected by the focused workspace checks. Dialog widths matching the Core size
scale must use `Dialog size`; other local widths require a reviewed exception
and may only decrease. The next promotion candidates are assignment/picker
composition, filter/search composition, and unified empty/error state
composition. Their APIs must remain composable; a central component is not a
single oversized page template.
## Surface Archetypes
@@ -1,7 +1,8 @@
# Shared WebUI Primitive Inventory
This 2026-08-18 inventory records the implementation state after the first
product-wide toolbar, grid, form-section, and dialog-anatomy migration. It is
This 2026-08-18 inventory records the implementation state after the
product-wide toolbar, grid, form-section, dialog-anatomy, metric-group, and
description-list migrations. It is
evidence for prioritization, not a substitute for the normative
[interface pattern language](../architecture/INTERFACE_PATTERN_LANGUAGE.md).
@@ -16,25 +17,38 @@ evidence for prioritization, not a substitute for the normative
| `DialogActions` | Every Core `Dialog` footer | Footer wrapping and alignment are no longer repeated by consumers. |
| `DialogForm` | 6 source files | Raw `*-dialog-form` form wrappers are rejected. |
| `DialogSection` | 6 source files | Repeated dialog field/content grouping is available as a shared primitive. |
| `MetricGrid` | 29 uses in 25 source files | The former `metric-grid` class and module-specific column overrides were removed. Core now owns 15/auto-fit columns, minimum width, density, block/inset/zero spacing, and collapse. |
| `DescriptionList` and `DescriptionItem` | 51 lists in 28 source files; item composition in 11 files | The former `admin-details-grid` and `detail-list` classes were removed. Core now owns stacked and inline property layouts, density, term width, wrapping, and collapse while preserving native `dl`/`dt`/`dd` semantics. |
| Standard dialog sizing | 9 consumers and 6 duplicated width rules removed | Confirmations and compatible Calendar, credential, Campaign, Files, and Voting dialogs use the Core small/large/wide scale. The 61 remaining specialized selectors across 22 CSS files are registered and decrease-only. |
`tools/checks/check-shared-webui-primitives.py` verifies Core exports and
ownership, representative consumers, the absence of the retired raw anatomy,
and composition of every `Dialog` footer through `DialogActions`.
## Dialog Width Classification
The remaining 61 width selectors do not duplicate the Core 460/560/680/1040/
1440px scale. They cover bounded editor widths between scale steps, high-density
definition and governance editors, preview/chooser canvases, message and file
overlays with coupled height behavior, and responsive full-canvas workflows.
Their exact selector set lives in
`tools/checks/shared-webui-dialog-width-exceptions.txt`. The focused check fails
for a new selector, a stale baseline entry, or any local width that duplicates
the Core scale.
## Remaining Promotion Candidates
The post-migration scan found 101 uses of 30 non-Core grid class names across
54 files. Most are intentionally domain-specific; repeated groups identify the
next useful primitives:
The post-migration scan still finds domain-specific grids, but the repeated
generic metric and property-list geometry is gone. Remaining grids are mostly
unequal-track editors, visualizations, workflow facts, import mappings, and
collection layouts. The next useful candidates depend on interaction semantics:
| Priority | Remaining pattern | Evidence | Proposed contract |
| --- | --- | ---: | --- |
| 1 | Metric-card groups | `metric-grid`: 29 uses in 25 files | `MetricGrid` or `MetricGroup` owning 14/auto-fit columns, compact spacing, inset mode, and narrow stacking around the existing `MetricCard`. |
| 2 | Property/description lists | `admin-details-grid`: 13 uses in 11 files | Semantic `DescriptionList` with compact/default density, 12 columns, wrapping, and narrow collapse. |
| 3 | Assignment/picker groups | `admin-assignment-grid`: 5 uses in 4 files | A selection/assignment layout only after its list, search, empty, and permission variants are compared; plain geometry can already use `ContentGrid`. |
| 4 | Filter and search rows | Toolbars are centralized, but filter fields, result counts, bulk selection, and action grouping still vary | `FilterBar` composed from `ActionToolbar`, with an explicit search region, filters, result summary, and bulk actions. |
| 5 | Dialog sizing exceptions | Dialog consumers still use 62 module class tokens across 53 files; 26 module/Core CSS files contain dialog/modal width declarations needing review | Migrate standard widths to `Dialog size`; retain only documented full-canvas, preview, chooser, and domain-editor exceptions. |
| 6 | State and collection anatomy | Loading components exist, but empty/error/retry/action placement is not yet one composition | A composable `StatePanel`/`CollectionState` covering empty, recoverable error, permission block, partial result, and next action without hiding domain consequences. |
| 1 | Empty and collection state anatomy | More than 70 module-prefixed empty/state class uses remain; only loading, alerts, blockers, and DataGrid empty actions are centralized | A composable `StatePanel`/`CollectionState` covering empty, recoverable error, permission block, partial result, and next action without hiding domain consequences. First distinguish a whole-surface state from a compact empty row or optional-value placeholder. |
| 2 | Filter and search rows | More than 50 filter/search class tokens cover simple text search, facets, popovers, result counts, active filters, bulk selection, and overlay search | Define `FilterBar` only after the input/submit, live filtering, facet, result-summary, and bulk-action accessibility variants have been compared. Plain action placement already uses `ActionToolbar`. |
| 3 | Assignment/picker groups | `admin-assignment-grid`: 5 uses in 4 files | Promote a selection/assignment composition only after its list, search, empty, policy, and permission variants are compared; plain geometry can already use `ContentGrid`. |
| 4 | Repeated domain fact and statistic grids | `campaign-header-grid` (8), `review-flow-fact-grid` (6), `postbox-form-grid` (5), plus smaller families | Compare semantics before promotion. Some can use `ContentGrid` or `DescriptionList`; others intentionally own unequal tracks or workflow visualization. |
The other custom grids are mostly bounded domain visualizations or unequal-track
editors: conflict mappings, import mapping, campaign review facts, charts,
@@ -45,8 +59,8 @@ created merely because two implementations both use CSS Grid.
## Next Audit
The next bounded slice should implement the metric and description-list
contracts, migrate their consumers, and then classify dialog width declarations
as standard-size migrations or registered exceptions. After that, compare
filter/search rows before defining their composition; their accessibility and
bulk-selection behavior matter more than visual similarity.
The next bounded slice should compare whole-surface empty states across list,
detail, permission, capability, and recoverable-error contexts and promote only
their shared anatomy. Filter/search composition follows after its live versus
submitted filtering and bulk-selection behavior is explicit. Assignment grids
remain deferred until their permission and policy variants are understood.