Files
govoplan-core/docs/PAGE_LAYOUT_USAGE_GUIDELINES.md
T

7.8 KiB

Page Layout and Action Guidelines

This document defines the binding composition grammar for headed GovOPlaN pages. Core owns the reusable anatomy; each module owns its domain actions, wording, authorization, consequences, and data state.

Required Page Frame

  • Use PageLayout for every headed standalone, workspace, or embedded page.
  • Declare exactly one semantic archetype; do not infer page intent from the mode, which controls geometry and scroll ownership only.
  • Use WorkspaceFrame for a full-height module surface and WorkspaceLayout only where navigation/content or list/detail panes are genuinely part of the interaction.
  • Put page-wide feedback in PageLayout notices. Use DismissibleAlert for a recoverable warning or failure and StatePanel when the entire surface is loading, empty, unavailable, or blocked.
  • Do not reproduce shared page padding, heading, toolbar, form-grid, section, table, dialog, or breakpoint CSS in a module.

Semantic Page Archetypes

Archetype Use when
overview The page summarizes health, metrics, or several peer areas without owning one primary collection or draft.
collection The primary object is a searchable/listable collection and Create, when available, applies to that collection.
detail The page primarily presents one record, report, or immutable projection.
editor The page owns one explicit draft with Save and Discard behavior.
workspace The page coordinates several panes, stages, or task-local operations that cannot honestly be reduced to one record or draft.

The archetype remains stable for the current interaction. A page may switch from overview to editor when the user explicitly enters configuration mode. It must not call a page an editor merely because a dialog or an inline filter is editable.

Page Action Rules

Pass one PageActionBar to the PageLayout actions slot. Full-canvas workspaces use the same contract through WorkspaceActionBar, with an explicit workspace, collection-pane, detail-pane, or editor-pane scope. The variant makes the surface's intent inspectable and preserves the same keyboard and visual order across modules. ActionToolbar remains the lower-level component for section-local controls; it is not a substitute for a semantic page or pane action bar.

Page kind Leading group Trailing group
Overview Reload when refreshable, then context Help, then ordinary primary actions
Collection Reload when refreshable, then collection context such as export Help, then Create at the far right
Detail Reload when refreshable, then object context Help, ordinary primary actions, then a separated destructive group
Editor Reload only when refresh is a distinct safe operation, then context Dirty state, Help, ordinary primary actions, separated destructive actions, Discard, then Save at the far right
Workspace Reload when the coordinated projection can become stale, then task context Help, ordinary primary actions, then a separated destructive group

Reload means re-fetch or re-evaluate the current surface. A page declaring refreshable must provide it, and a non-refreshable page must not use Reload as a synonym for Cancel, Reset, or Discard. Reload never silently destroys a dirty draft. Create is a collection-wide action and is not duplicated in a persistent side panel. Save is present only where the page owns an editable draft; a read-only detail page must not display a disabled or inert Save merely to fill the slot.

Editor bars always keep Discard and Save visible. Their required state projection is one of clean, dirty, invalid, saving, save-failed, or conflict, and the central component announces it through a live status label. Clean and saving states disable both persistence actions; invalid disables Save while retaining Discard. Failed saves and conflicts keep the draft recoverable and allow an authorized retry after the module has shown the owning error or conflict evidence. A module may add a more specific validation, policy, or permission blocker. The editor must register its draft with useUnsavedDraftGuard (or a shared hook that uses the same registration contract), so browser unload, route navigation, section changes, Reload, and the explicit Discard path cannot silently lose work.

Reload is rendered by Core from a descriptor rather than passed as arbitrary button markup. It can project current, stale, reloading, or reload-failed; loading is the shorthand for reloading. A failed refresh must preserve usable loaded data, expose its stale/failure state, and leave Reload available for recovery. Reload goes through the same unsaved-navigation guard as route changes.

Destructive page actions use destructiveActions; never put a danger action in contextActions or the ordinary primary group. Core renders a persistent visual and semantic boundary before this group. In an editor it precedes the Discard/Save pair, keeping Save in the final keyboard and visual position.

PageActionBar controls non-editor placement and owns the standard editor persistence buttons. Other actions continue to use central Button, IconButton, or TableActionGroup components. When an action is visible but unavailable because of permission, target, policy, state, or validation, keep it in its stable slot and supply disabledReason. Do not silently hide a normally applicable action.

Forms and Dialogs

  • Compose forms from FormLayout/FormGrid, FormSection, and FormField.
  • Use FieldLabel through FormField for every field that is not genuinely self-explanatory; record justified omissions in the owning UI ledger.
  • Use Dialog, DialogForm, DialogSection, and DialogActions for modal work. A dialog can be domain-specific while its anatomy remains central.
  • Use useUnsavedDraftGuard for explicit Discard and guarded navigation on an editable page or dialog.
  • Explain irreversible or operationally consequential actions before the commit button, including reversibility and durable evidence.

Collections and Details

  • Use FilterBar for collection query controls and DataGrid for tabular collections. Keep a single ordered TableActionGroup action set per table.
  • Use MetricGrid/MetricCard for summary measures, Card or ContentSection for logical sections, and DescriptionList for labelled facts.
  • Add a MetricCard.drilldown only when the displayed measure has a useful, authorized underlying collection or detail. Name the destination explicitly (for example, “Review failed deliveries”) and preserve the current scope and filters in its href or action. The card itself remains non-interactive so the action is visible and keyboard-predictable. Derived, privacy-suppressed, non-enumerable, or purely informational aggregates remain plain metrics; when an ordinarily available drill-down is temporarily blocked, keep its action and provide disabledReason.
  • Preserve loaded data after a refresh failure and mark it stale; offer Reload as the recovery action. Distinguish initial loading, empty, unavailable, permission-blocked, conflict, success, and retry states.

Review Evidence

Every new or changed page or workspace pane must have structural evidence for its frame, semantic archetype/scope and slot order, refresh declaration, shared component usage, stable disabled actions, dirty guard, destructive boundary, and module-owned help identity. Type checks enforce conditional Reload and editor persistence props. The product check discovers all consumers, rejects undeclared archetypes and ActionToolbar panel-header copies, and requires semantic actions for every WorkspaceFrame route. Browser conformance confirms keyboard order, lifecycle changes, accessibility, destructive separation, narrow wrapping, and screenshot geometry.