Browse pages
Controlled UI Governance
The shared UI package is a constrained construction language.
Intent#
The shared UI package is a constrained construction language. It provides the primitive source, the interaction behavior, the layout, and the tokens. A route names one floorplan and fills its slots from the domain. A route suite proves every route.
The route code is the page contract. No page document restates it, because a second description of a page drifts from the code that renders it.
Concepts#
The floorplan set#
A floorplan is a page component that fixes one page layout and exposes named slots. The shared package holds the closed set, and a route composes exactly one of them. A closed set lets an author choose a known shape by rule rather than invent a layout. Each floorplan implements the chosen option of one page-frame pattern from the pattern catalog (UI pattern decisions).
The reference set holds one floorplan per reader task. A workspace declares the ones its routes need, and each carries the rule for choosing it.
| Floorplan | Choose it when the reader | Slots it carries |
|---|---|---|
WorkPage | Processes items one after another | The queue, the current item, and its actions |
ListPage | Finds one record among many | Filters, the result count, and the rows |
EntityPage | Acts on one record | Facts, state, the actions that state permits, and related records |
FormPage | Supplies the input of one command | The fields, one primary action, and the refusals |
OutcomePage | Receives one answer after an action | The answer and the next step |
The domain fills the slots, so a floorplan never invents a state, an action, or a refusal.
| Slot | Domain source |
|---|---|
| Status | The business record states the specification names |
| Actions | The use cases the current state permits |
| Refusals | The refusal results of the use case the route calls |
A floorplan exposes its name on its frame. A reviewer and the route suite read that name to learn which floorplan a route composes.
The tone set#
A tone names what a state means. The set is closed: a page picks one of them rather than a color.
| Tone | The state it means |
|---|---|
neutral | Nothing is wrong and nothing is asked for |
positive | The thing asked for happened |
caution | A person has to decide |
destructive | The thing asked for is refused |
info | The page is explaining rather than reporting |
The mapping from a state code to a tone lives in one table in each application. A reader then sees the same color for the same state on every screen.
The command surface#
A command's surface follows the size of its input. A larger surface than the command needs costs the reader attention it has no use for.
| The command takes | Use |
|---|---|
| Nothing and can be taken back | A button |
| Nothing and cannot be taken back | A confirmation dialog |
| One to four inputs | A form dialog |
| More than four inputs | A form page |
The route suite#
The route suite is one specification per frontend that visits every route. It asserts the checks below, and a visual snapshot supplements an assertion without replacing one. A snapshot varies across platforms, fonts, and hardware, so an assertion is the gate.
| Check | The route passes when |
|---|---|
| Floorplan | It composes exactly one floorplan |
| Reference | It names its reference or is a recorded freestyle route |
| Accessibility | It meets the applicable accessibility standard with zero violations |
| Heading | It carries exactly one top-level heading |
| Landmark | It carries one main content region |
| Reflow | At the narrowest width it has no clipped content; a data table may scroll inside its region |
| Keyboard | Every interactive element is reachable and shows a visible focus indicator |
| Announcements | Asynchronous status changes announce through the platform live region |
The suite discovers its routes from the application route definition rather than from a list. A new route is therefore checked with no new specification. A dynamic segment resolves to a record the deployment holds, and falls back to an absent record when it holds none.
Result proof#
A route calls use cases, and each use case declares its results. Proof is computed: a test proves a case when its steps call the use case and assert the result. A passing test proves every result a route calls, or a known-gap record with an owner and an end date names it. The calls come from the route code, so the obligation follows the code rather than a declaration.
Standards#
Select one visual authority#
rulecheckableRequirement A workspace MUST record one shared UI package in litespec.project.json, and each controlled frontend names its density profile against it.
Rationale A second general-purpose visual system in one workspace makes every component choice ambiguous. A frontend outside the contract declares another platform and says what replaces the contract.
A project chooses the density per frontend rather than per workspace, because density is a property of the audience. public-light suits a surface a visitor meets once. application-balanced suits a surface a signed-in user works in. admin-dense suits a surface an operator reads all day.
Pin the baseline in the shared package#
rulecheckableRequirement A workspace MUST install the pinned baseline from litespec.manifest.json once, into its shared UI package.
Rationale Installing per application makes a fourth copy, where a fourth drift is invisible. An additional baseline source is permitted under an exception recorded against this provision.
Publish a design contract#
rulecheckableRequirement A workspace MUST publish one design contract with its shared UI package, carrying the required sections: Brand, Tokens, Vocabulary, Patterns, Do, Do not, Motion, Voice.
Rationale An author that has read no contract picks its own tokens, its own motion, and its own voice. The picks differ on the next page, and the difference is what a reader sees as clutter. One contract keeps the answers in one file.
Example The metadata block names the package and the floorplans it provides.
{
"kind": "design-contract",
"schemaVersion": 1,
"package": "@acme/ui",
"profile": "application-balanced",
"shell": "app-shell",
"patterns": ["work-page", "list-page", "entity-page"]
}Declare a closed floorplan set#
ruleguidanceRequirement The Patterns section of the design contract MUST list every floorplan the package provides, each with a when-to-use rule.
Rationale A floorplan with no rule for choosing it leaves the choice to resemblance, and two authors then pick different floorplans for one kind of page. A floorplan outside the list is a design decision rather than a route-level choice.
Example The section states each floorplan and the question that selects it.
## Patterns
| Floorplan | Component | Use it when the reader |
|:---|:---|:---|
| `list-page` | `ListPage` | Finds one record among many. The page shows the result count. |
| `entity-page` | `EntityPage` | Acts on one record: its facts, its state, and the actions that state permits. |
| `form-page` | `FormPage` | Supplies the input of one command, with one primary action. |Compose each route from one floorplan#
ruleguidanceRequirement A route MUST render exactly one floorplan from the declared set, unless it is a recorded freestyle route.
Rationale The floorplan fixes the layout, the heading position, and the slots, so a reviewer reads which floorplan a route composes. The slots take domain values: the business record state, the use cases that state permits, and the refusal results of the called use case.
Example An order route composes the entity floorplan and fills each slot from the order.
Keep layout inside floorplans#
ruleguidanceRequirement Route code MUST NOT declare layout outside its floorplan and display composites.
Rationale A layout declaration in a route file is a second layout beside the floorplan, and the next route copies it. Floorplan and display composites own layout, so one change reaches every route composing them.
Record a freestyle route#
ruleguidanceRequirement A route that composes no declared floorplan MUST name the decision record that justifies its layout.
Rationale A freestyle route is the first consumer of a new floorplan or a genuine one-off. A named decision makes either case reviewable, and it keeps the declared set closed. A second route of the same shape is the signal to add a floorplan.
Derive each page type from a named reference#
ruleguidanceRequirement Every route MUST name its page type's reference, with a freestyle route naming its decision record instead.
Rationale An author with no named reference invents a page from primitives, and the next author invents another. A reference that resolves keeps every page of one type on one reference.
Select a tone from the closed set#
ruleguidanceRequirement A state indicator MUST take one tone from the closed set, each application declaring its state-to-tone mapping once as an exhaustive table of language terms.
Rationale A page that picks a color picks a meaning nobody agreed to. One mapping means the same state reads the same way on every screen. A new state then fails to compile until its tone and its word exist.
Keep tokens in one definition#
ruleguidanceRequirement An application MUST use the shared token definition and declare no value, with no literal color in any shipped definition.
Rationale One token definition is what makes two applications look like one product. An application uses it and declares no value, so a second definition never introduces a value the first has no opinion about. A definition the shared package ships declares no literal color.
Hold the declared base color#
ruleguidanceRequirement A token definition MUST carry only the values the declared baseline publishes, except under a recorded exception.
Rationale A token value outside the published ramp is a second palette beside the declared one, and the next author copies it. A recorded exception keeps the deliberate departure visible in the design contract.
Keep separators at the subtle step#
ruleguidanceRequirement A structural separator or input border MUST NOT be darker than the border step the declared base color publishes.
Rationale A heavier rule beside a lighter one reads as emphasis, so authors stop trusting the token. The border step keeps every separator at one weight.
Select a command surface by size#
ruleguidanceRequirement A command MUST use the surface the Concepts table assigns to its input.
Rationale A form dialog with more inputs than the table allows scrolls, loses its title, and hides the refusal beside the field that caused it. A confirmation dialog for an action taking nothing reads as a warning rather than as a question.
Show the trace ID in error detail#
ruleguidanceRequirement Every error the user sees MUST show its trace identifier in copyable form in the error detail, from the problem body or the X-Trace-Id header.
Rationale Support reads the identifier to find the failure, and the reader copies it without retyping. A headline carries what happened; the identifier belongs in the detail.
Check every route#
ruleguidanceRequirement Every route MUST pass each route suite check.
Rationale The Concepts section names the checks. They cover the floorplan, the reference, accessibility, the heading, the landmark, reflow, keyboard use, and status announcements. One suite visits every route, so a new route is checked without a specification written for it.
Prove every result a route calls#
ruleguidanceRequirement Every result a called use case returns MUST be proved by a passing test or a known-gap record with owner and end date.
Rationale A refusal a reader can reach and no test provokes is a refusal nobody sees render. The calls come from the route code, so the obligation follows the code rather than a declaration.
Example A test calls the use case and asserts one refusal result.
Restrict styling to semantic tokens#
ruleguidanceRequirement A style MUST use a semantic token rather than a raw value.
Rationale The approved style surface is the shared package definition, holding the tokens, the fonts, and the documented resets.
Govern behavior packages#
ruleguidanceRequirement A behavior package MUST name the decision record approving it for the capability it supplies, and render through baseline components and tokens.
Rationale Approval follows the capability rather than the visual catalog, so a package is not adopted for components the baseline already provides.
Prove UI behavior and appearance#
ruleguidanceRequirement A primitive, composite, token, source, floorplan, or route change MUST name the evidence in the matrix in this section.
Rationale The matrix maps each change class to the narrowest evidence that represents its risk.
| Change class | Narrowest evidence |
|---|---|
| Primitive or variant | The component test for that primitive |
| Floorplan or display composite | The route suite on one route that composes it |
| Token or global style | The route suite on one route in each profile |
| Route | The route suite on that route, and every test proving a result it calls |
| Pattern decision | The catalog entry changes with its implementation |
| Fork | The fork record and the component test |
| Behavior package | The accessibility check for the capability it supplies |
Record a frontend outside the controlled contract#
ruleguidanceRequirement A frontend outside the controlled platforms MUST declare an entry point record naming that platform and the boundaries replacing this contract.
Rationale A device client with no shared component and no router sits outside the floorplan set and the component rules. Declaring the platform keeps the decision visible instead of reporting as a frontend with nothing to check.
Conventions#
Use the product profiles#
conventioncheckableDefault Select one density profile from public-light, application-balanced, or admin-dense for each controlled frontend.
Replacement A consumer replaces this default by recording an exception with its reason and review date.
Rationale All profiles share one baseline.
Keep a blocked command visible with its reason#
conventionguidanceDefault Leave a command blocked only by the current state or role visible and disabled, with the reason beside it.
Replacement A consumer replaces this default by recording an exception with its reason and review date.
Rationale A merchant looking for an action needs to know it exists and why it is unavailable. A control that vanishes looks like a product that does not offer it.
Verification#
| ID | Mark | Evidence |
|---|---|---|
| litespec/frontend/ui#select-one-visual-authority | checkable | litespec consumer refuses a frontend with no declared platform and a workspace with a controlled frontend but no shared package. |
| litespec/frontend/ui#pin-the-baseline-in-the-shared-package | checkable | litespec ui reports a workspace with no pinned baseline installed once in its shared package. |
| litespec/frontend/ui#publish-a-design-contract | checkable | litespec ui reports a missing contract, a schema failure, or a missing section. |
| litespec/frontend/ui#declare-a-closed-floorplan-set | guidance | Does the Patterns section list every floorplan with a when-to-use rule? |
| litespec/frontend/ui#compose-each-route-from-one-floorplan | guidance | Does every route render exactly one floorplan from the declared set or record its freestyle decision? |
| litespec/frontend/ui#keep-layout-inside-floorplans | guidance | Does any route file declare layout outside its floorplan and display composites? |
| litespec/frontend/ui#record-a-freestyle-route | guidance | Does every freestyle route name the decision record that justifies its layout? |
| litespec/frontend/ui#derive-each-page-type-from-a-named-reference | guidance | Does every route name its page type reference, with each freestyle route naming its decision record? |
| litespec/frontend/ui#select-a-tone-from-the-closed-set | guidance | Does each application declare its state-to-tone mapping once as an exhaustive table of terms? |
| litespec/frontend/ui#keep-tokens-in-one-definition | guidance | Does each application use the shared token definition with no declared value and no literal color? |
| litespec/frontend/ui#hold-the-declared-base-color | guidance | Does the token definition carry only declared baseline values, with each departure recorded? |
| litespec/frontend/ui#keep-separators-at-the-subtle-step | guidance | Is every structural separator and input border at the published border step or lighter? |
| litespec/frontend/ui#select-a-command-surface-by-size | guidance | Does each command use the surface the Concepts table assigns to its input? |
| litespec/frontend/ui#show-the-trace-id-in-error-detail | guidance | Does every visible error show its trace identifier copyably in its detail rather than its headline? |
| litespec/frontend/ui#check-every-route | guidance | Does every route pass each route suite check? |
| litespec/frontend/ui#prove-every-result-a-route-calls | guidance | Is every result the route calls proved by a passing test or named by a known-gap record? |
| litespec/frontend/ui#restrict-styling-to-semantic-tokens | guidance | Does every style use a semantic token rather than a raw value? |
| litespec/frontend/ui#govern-behavior-packages | guidance | Does every behavior package name its approving decision and render through baseline components and tokens? |
| litespec/frontend/ui#prove-ui-behavior-and-appearance | guidance | Does each change name the evidence the matrix assigns to its class? |
| litespec/frontend/ui#record-a-frontend-outside-the-controlled-contract | guidance | Does every frontend outside the controlled platforms declare its platform and name its replacement boundaries? |
| litespec/frontend/ui#use-the-product-profiles | checkable | litespec ui refuses a controlled frontend with no density profile. |
| litespec/frontend/ui#keep-a-blocked-command-visible-with-its-reason | guidance | Does each disabled command stay visible with its reason? |