mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Add design council skill and interaction-and-feedback contract
This commit is contained in:
@@ -0,0 +1,80 @@
|
|||||||
|
---
|
||||||
|
name: council
|
||||||
|
description: Consult the design council — the external interaction, content, and accessibility canon (IBM Carbon, GOV.UK Design System, WAI-ARIA APG, Nielsen Norman) — before committing a user-facing design decision. Auto-invoke when writing user-facing error / empty-state / notification copy, or when designing the keyboard / focus / ARIA behavior of an interactive widget (modal, menu, listbox, toast, splitter, disclosure). Also invokable on demand for any interaction, content, accessibility, or usability decision.
|
||||||
|
disable-model-invocation: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Design Council
|
||||||
|
|
||||||
|
A standing panel of external design authorities, consulted the way we already consult
|
||||||
|
Carbon: **local corpora, grepped — not WebFetched.** The council **advises**; Astrolabe's
|
||||||
|
own contract **decides**. When a source conflicts with `SOUL.md`,
|
||||||
|
`docs/architecture/09-visual-design.md` (visual contract), or
|
||||||
|
`docs/architecture/10-interaction-and-feedback.md` (interaction contract), **our contract
|
||||||
|
wins** — note the divergence and move on.
|
||||||
|
|
||||||
|
## Two modes
|
||||||
|
|
||||||
|
- **Pointwise (now).** Consult on a single decision in front of you, via the routing table
|
||||||
|
below. Cheap, precise, fits the editing flow — this is the default, and what the
|
||||||
|
auto-triggers fire.
|
||||||
|
- **Audit (future).** Periodically sweep the **whole codebase** against the roster — every
|
||||||
|
interactive widget vs. APG, every user-facing string vs. GOV.UK/Carbon content rules,
|
||||||
|
every data surface vs. the loading/empty/error triad, every latency path vs. NN/g budgets
|
||||||
|
— and emit a prioritized gap list into `docs/architecture/10` + `// TODO`s. This is a
|
||||||
|
fan-out review (closer in shape to `/alignment`, but graded against external canon), best
|
||||||
|
run at **milestone boundaries**, not per change — and much of it is milestone-gated anyway
|
||||||
|
(you can't audit modals or datasets that don't exist yet).
|
||||||
|
|
||||||
|
## How to convene (the routing rule)
|
||||||
|
|
||||||
|
Pull the **minimal** relevant member(s) for the decision at hand — do **not** sweep all
|
||||||
|
four; that wastes tokens and dilutes the answer. Map the decision to its seat(s):
|
||||||
|
|
||||||
|
| Decision in front of you | Primary seat → then |
|
||||||
|
| ---------------------------------------------------------- | ------------------------------------------------------ |
|
||||||
|
| Error / failure / empty-state / notification **copy** | **GOV.UK** → Carbon notifications → NN/g heuristic #9 |
|
||||||
|
| Keyboard / focus / ARIA roles of an **interactive widget** | **WAI-ARIA APG** → Carbon component |
|
||||||
|
| **Latency / feedback / loading / progress** budgets | **NN/g** response-time limits → Carbon loading pattern |
|
||||||
|
| **Forms / validation / destructive-action** flow | **GOV.UK** → Carbon |
|
||||||
|
| General **usability** gut-check on a flow | **NN/g** 10 heuristics |
|
||||||
|
| **Visual** styling (type, spacing, colour, component look) | **Carbon** + our `docs/architecture/09` |
|
||||||
|
|
||||||
|
Then: read the cited file(s), extract the **specific** principle, and report it back with
|
||||||
|
a **citation (member + file path)** and a one-line "how it lands in Astrolabe." Don't
|
||||||
|
paraphrase the whole source — quote the rule that decides the question.
|
||||||
|
|
||||||
|
## The roster
|
||||||
|
|
||||||
|
All paths are under `/Users/oleh/code/reference/`. Treat clones as **inspiration, not
|
||||||
|
law** — they drift; the published guidance is the truth, the clone is the fast index.
|
||||||
|
|
||||||
|
| Member | Path | Authoritative for | How to query |
|
||||||
|
| ------------------------------ | ---------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| **IBM Carbon** | `carbon-website/src/pages/` | Notification taxonomy, status levels, empty/loading states, content basics, data-viz styling | grep `.mdx` under `components/notification`, `patterns/{empty-states,loading,status-indicator}-pattern`, `guidelines/content` |
|
||||||
|
| **GOV.UK Design System** | `govuk-design-system/src/` | Error & validation messages, failure pages, forms, plain-language content, accessibility | `index.md` under `components/{error-message,error-summary,notification-banner}`, `patterns/{problem-with-the-service-pages,service-unavailable-pages,check-answers}`, `accessibility/` |
|
||||||
|
| **WAI-ARIA APG** | `aria-practices/content/patterns/` | Keyboard interaction, focus management, ARIA roles/states for widgets | `<pattern>/<pattern>-pattern.html` — e.g. `dialog-modal`, `alertdialog`, `alert`, `listbox`, `menu-button`, `disclosure`, `switch`, `tabs`, `tooltip`, `windowsplitter` |
|
||||||
|
| **Nielsen Norman (distilled)** | `principles/nielsen-norman.md` | 10 usability heuristics; response-time / feedback budgets (0.1s / 1s / 10s) | read directly — it is short and curated |
|
||||||
|
|
||||||
|
## Close the loop
|
||||||
|
|
||||||
|
The council exists so we **don't re-derive the same decision twice**. When a consultation
|
||||||
|
settles a _recurring_ question (not a one-off), capture the resolution into our own
|
||||||
|
contract — `docs/architecture/10-interaction-and-feedback.md` for interaction/feedback,
|
||||||
|
`09-visual-design.md` for visual — via the `/doc-update` skill. Upstream canon informs;
|
||||||
|
the downstream contract records. A code site that embeds such a rule should cite our
|
||||||
|
contract, not the external source.
|
||||||
|
|
||||||
|
## Adding a seat
|
||||||
|
|
||||||
|
1. If it's an open-source repo, shallow-clone it into `/Users/oleh/code/reference/`
|
||||||
|
(`git clone --depth 1 …`). If it's articles/blogs (not cloneable), **distill** the
|
||||||
|
stable parts into `reference/principles/<source>.md` with attribution + links, like
|
||||||
|
the NN/g note.
|
||||||
|
2. Add one row to **the roster** and one routing entry above.
|
||||||
|
3. Keep it lean — a seat earns its place only if it's authoritative for a decision the
|
||||||
|
others don't cover well.
|
||||||
|
|
||||||
|
Candidate future seats (not yet seated): **FT Visual Vocabulary / Datawrapper** (chart
|
||||||
|
choice — our domain), **Shopify Polaris** (UX-writing depth), **web.dev** (perceived
|
||||||
|
performance / PWA / offline UX).
|
||||||
@@ -62,7 +62,7 @@ src/
|
|||||||
styles/ # Global CSS (tokens, base)
|
styles/ # Global CSS (tokens, base)
|
||||||
docs/
|
docs/
|
||||||
├── spec/ # Authoritative behavioral specification (00–10) — the WHAT
|
├── spec/ # Authoritative behavioral specification (00–10) — the WHAT
|
||||||
├── architecture/ # Architecture playbook (00–09) — the HOW (self-contained)
|
├── architecture/ # Architecture playbook (00–10) — the HOW (self-contained)
|
||||||
│ └── visual-specimen.html # Standalone token sandbox + reusable-primitive catalog
|
│ └── visual-specimen.html # Standalone token sandbox + reusable-primitive catalog
|
||||||
├── IMPLEMENTATION-PLAN.md # Milestone sequence (M0–M6)
|
├── IMPLEMENTATION-PLAN.md # Milestone sequence (M0–M6)
|
||||||
└── WHY-A-SEPARATE-REBUILD.md
|
└── WHY-A-SEPARATE-REBUILD.md
|
||||||
@@ -115,6 +115,10 @@ Invoke with `/<name>` (defined in `.claude/skills/`):
|
|||||||
`// TODO:` breadcrumbs at code sites for out-of-scope observations.
|
`// TODO:` breadcrumbs at code sites for out-of-scope observations.
|
||||||
- **`/doc-update`** — capture session-discovered knowledge gaps into the right doc layer
|
- **`/doc-update`** — capture session-discovered knowledge gaps into the right doc layer
|
||||||
(`docs/spec/` for behavior, `docs/architecture/` for patterns).
|
(`docs/spec/` for behavior, `docs/architecture/` for patterns).
|
||||||
|
- **`/council`** — consult the design council (Carbon, GOV.UK, WAI-ARIA APG, Nielsen
|
||||||
|
Norman, cloned under `reference/`) before a user-facing interaction/content/a11y
|
||||||
|
decision. Auto-fires on error/empty-state copy and new interactive-widget keyboard/focus
|
||||||
|
work; on demand otherwise. It advises; `docs/architecture/09`+`10` decide.
|
||||||
- **`/release`** — bump version, update the changelog, prepare a git tag.
|
- **`/release`** — bump version, update the changelog, prepare a git tag.
|
||||||
|
|
||||||
### Versioning
|
### Versioning
|
||||||
|
|||||||
@@ -8,10 +8,15 @@ See @AGENTS.md for project overview, architecture rules, and the AI developer pr
|
|||||||
- **[docs/spec/](docs/spec/)** — authoritative behavioral specification (sections 00–10):
|
- **[docs/spec/](docs/spec/)** — authoritative behavioral specification (sections 00–10):
|
||||||
the **what**. This is the contract; implement to it.
|
the **what**. This is the contract; implement to it.
|
||||||
- **[docs/architecture/](docs/architecture/00-overview.md)** — architecture playbook
|
- **[docs/architecture/](docs/architecture/00-overview.md)** — architecture playbook
|
||||||
(00–09): the **how** (state, persistence, modals, routing, rendering, inference,
|
(00–10): the **how** (state, persistence, modals, routing, rendering, inference,
|
||||||
relationships, vega-editor techniques, visual design). Self-contained — no external
|
relationships, vega-editor techniques, visual design, interaction & feedback).
|
||||||
repo needed. Companion: **[visual-specimen.html](docs/architecture/visual-specimen.html)** —
|
Self-contained — no external repo needed. Companion:
|
||||||
token sandbox + reusable-primitive catalog (open in a browser).
|
**[visual-specimen.html](docs/architecture/visual-specimen.html)** — token sandbox +
|
||||||
|
reusable-primitive catalog (open in a browser).
|
||||||
|
- **[`/council`](.claude/skills/council/SKILL.md)** — the design council: consult external
|
||||||
|
interaction/content/a11y canon (Carbon, GOV.UK, WAI-ARIA APG, Nielsen Norman, cloned
|
||||||
|
under `reference/`) before a user-facing decision. It advises; our contract (architecture
|
||||||
|
09/10) decides. Resolutions are recorded back into the contract.
|
||||||
- **[docs/IMPLEMENTATION-PLAN.md](docs/IMPLEMENTATION-PLAN.md)** — incremental milestone
|
- **[docs/IMPLEMENTATION-PLAN.md](docs/IMPLEMENTATION-PLAN.md)** — incremental milestone
|
||||||
plan (M0–M6), MVP boundary, per-milestone tests + manual checks, and an architecture
|
plan (M0–M6), MVP boundary, per-milestone tests + manual checks, and an architecture
|
||||||
reference index.
|
reference index.
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
# 10 · Interaction & Feedback
|
||||||
|
|
||||||
|
> **Status:** interaction contract. Where [09 · Visual Design](09-visual-design.md) is the
|
||||||
|
> _visual_ contract (how the app **looks**), this is the _interaction_ contract (how the
|
||||||
|
> app **behaves while the user works in it**). It is the HOW for the cross-cutting,
|
||||||
|
> non-feature-specific behavior that spec [§10 Non-Functional](../spec/10-non-functional.md)
|
||||||
|
> mandates as the WHAT.
|
||||||
|
|
||||||
|
These rules already live, scattered, as one-off comments across the codebase (the
|
||||||
|
confirm-dialog's "transactional-modal rule," the toaster's "non-blocking outcomes are
|
||||||
|
toasts," the preview's "empty is not an error"). This document consolidates them into one
|
||||||
|
referenceable contract so a new feature **checks the rule instead of re-deriving it** —
|
||||||
|
and diverges only on purpose.
|
||||||
|
|
||||||
|
**Upstream sources.** These principles are drawn from the design council (`/council`):
|
||||||
|
IBM Carbon, the **GOV.UK Design System**, the **WAI-ARIA Authoring Practices Guide**, and
|
||||||
|
**Nielsen Norman Group**. The council advises; this contract decides. When you face a new
|
||||||
|
interaction decision this doc doesn't cover, consult the council, then record the
|
||||||
|
resolution back here.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. The feedback-channel decision table
|
||||||
|
|
||||||
|
Astrolabe has four distinct ways to tell the user something. They are **not**
|
||||||
|
interchangeable; picking the wrong one is the most common interaction bug. Choose by the
|
||||||
|
nature of the message, not by convenience.
|
||||||
|
|
||||||
|
| Channel | Use when | Blocks? | Dismissal | Implemented by |
|
||||||
|
| -------------------- | --------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------- | -------------------------------------------------------- |
|
||||||
|
| **Confirm dialog** | A **destructive or irreversible** action needs explicit consent (delete, revert, reset) | Yes — modal | User must choose; Escape/Cancel = no; backdrop click does **not** dismiss | `ConfirmStore` + `ConfirmDialog` |
|
||||||
|
| **Toast** | A **non-blocking outcome** happened the user should know about (save failed, published, imported) | No | Auto for success/info; persists for error/warning; always a close button | `NotificationStore` + `Toaster` |
|
||||||
|
| **Inline error** | A problem is **tied to a specific surface** and recovers in place (invalid spec → editor + preview) | No | Clears automatically when the cause is fixed | `PreviewStore`, surfaced in `SpecEditor` + `LivePreview` |
|
||||||
|
| **Status indicator** | **Passive, ambient** state worth glancing at (draft vs. published, storage usage) | No | N/A — it just reflects state | library draft dot; storage monitor (later) |
|
||||||
|
|
||||||
|
**Rules.**
|
||||||
|
|
||||||
|
- **One blocking question at a time.** Confirm dialogs and feature modals are mutually
|
||||||
|
exclusive (the modal coordinator enforces this); a confirm may layer _over_ a modal
|
||||||
|
(discard-changes prompt), nothing else stacks.
|
||||||
|
- **Match disruptiveness to urgency** (Carbon). A toast interrupts less than a dialog;
|
||||||
|
don't use a dialog for something a toast can carry, and don't bury a
|
||||||
|
consent-for-destruction in a toast.
|
||||||
|
- **Errors persist; success fades.** An error/warning toast waits for the user (a critical
|
||||||
|
message must not vanish on a timer); success/info auto-dismiss (~6s).
|
||||||
|
- **The same failure can light up two channels.** An unrenderable spec shows the _same_
|
||||||
|
message inline in both the editor (§03E) and the preview (§04) — one producer
|
||||||
|
(`PreviewStore`), two subscribers. That's intentional, not duplication.
|
||||||
|
|
||||||
|
## 2. Latency & feedback budgets
|
||||||
|
|
||||||
|
From NN/g's response-time limits (`reference/principles/nielsen-norman.md`). These are not
|
||||||
|
aspirations — they're the basis of the render pipeline's shape.
|
||||||
|
|
||||||
|
| Budget | Feels like | Owed feedback | Astrolabe surfaces |
|
||||||
|
| ---------- | --------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
||||||
|
| **≤ 0.1s** | Instantaneous | None beyond showing the result | Keystrokes into the buffer, hovers, toggles, selection |
|
||||||
|
| **≤ 1s** | Uninterrupted thought | None needed, but direct-manipulation feel is lost | A typical render after the debounce; opening a modal; switching snippets |
|
||||||
|
| **> 1s** | Noticeably waiting | **Must not block input**; show a busy indication | A heavy spec / large-dataset render |
|
||||||
|
| **> 10s** | Attention lost | **Progress indicator + stay cancellable**; let the user work elsewhere | (guard for large M3+ dataset work) |
|
||||||
|
|
||||||
|
**Rules.**
|
||||||
|
|
||||||
|
- **Input is sacred.** Typing and navigation never block on rendering or persistence (spec
|
||||||
|
§10). The render pipeline is debounced (`RENDER_DEBOUNCE_MS`), runs async, and uses a
|
||||||
|
**generation token** so a slow render can't overwrite a newer one.
|
||||||
|
- **Auto-save is cheap and silent** (`AUTOSAVE_DEBOUNCE_MS`) — it never stalls typing and
|
||||||
|
produces no toast on success (only on failure).
|
||||||
|
- **A busy indication may overlay the preview but must not freeze the editor** (spec §10).
|
||||||
|
When a render _might_ exceed ~1s, owe a non-blocking indicator; never a frozen UI.
|
||||||
|
|
||||||
|
## 3. The non-happy-path triad
|
||||||
|
|
||||||
|
Every surface that shows data owes **three** designed states, not one. The empty and error
|
||||||
|
states are part of the feature, not an afterthought (Carbon empty-states, GOV.UK).
|
||||||
|
|
||||||
|
- **Loading** — when data isn't ready yet. Prefer a skeleton/placeholder over a spinner for
|
||||||
|
structural loads; show it only for a beat.
|
||||||
|
- **Empty** — when there is legitimately nothing. **Empty ≠ error**: a blank editor renders
|
||||||
|
a clean, calm empty preview, never an error (`LivePreview` treats blank as success). An
|
||||||
|
empty list says what would be here and how to add it.
|
||||||
|
- **Error** — when something went wrong. Split by **who can fix it**:
|
||||||
|
- **User-fixable** → state the consequence and the **next step** ("Storage full — delete
|
||||||
|
snippets to free space, then edit again"). A user action is mandatory (Carbon/GOV.UK).
|
||||||
|
- **Not user-fixable** → explain plainly **and** attach a reportable **diagnostic** (the
|
||||||
|
operation + underlying error) so it can be traced. See `services/storage-errors.ts` —
|
||||||
|
this split is the module's whole reason for being.
|
||||||
|
- **Wording** (NN/g #9, GOV.UK, Carbon content): plain language, **no error codes in the
|
||||||
|
user-facing line**, second person, name what stopped in the title, one or two sentences
|
||||||
|
in the body, never flippant.
|
||||||
|
|
||||||
|
## 4. Recovery & data-safety contract
|
||||||
|
|
||||||
|
The user must never silently lose work, and must always have a way back (NN/g #3 "user
|
||||||
|
control and freedom," #5 "error prevention"; spec §10 Reliability).
|
||||||
|
|
||||||
|
- **No silent data loss.** Edits auto-save as a **draft**; a known-good **published**
|
||||||
|
version is always preserved separately (§03D). A failed persist **surfaces as a toast**,
|
||||||
|
never a swallowed promise (`orchestration/persistence.ts`).
|
||||||
|
- **A marked exit from every committed change.** Revert restores the published spec;
|
||||||
|
Escape closes modals; delete/revert/reset require confirmation first.
|
||||||
|
- **Resilient rendering recovers on its own.** An invalid spec shows a readable error and
|
||||||
|
**auto-recovers when fixed** — it never leaves the app wedged (§04).
|
||||||
|
- **Non-destructive import.** Import merges, never overwrites; on failure the existing
|
||||||
|
workspace is left exactly as it was (§08).
|
||||||
|
|
||||||
|
## 5. Keyboard & focus contract
|
||||||
|
|
||||||
|
Astrolabe is keyboard-operable end to end (spec §10 Accessibility). The interaction
|
||||||
|
patterns follow **WAI-ARIA APG** (`reference/aria-practices/content/patterns/`); the wiring
|
||||||
|
lives in [04 · Routing & Global Events](04-routing-and-events.md).
|
||||||
|
|
||||||
|
- **One global router** owns document-level `keydown`/`paste`; components never attach
|
||||||
|
their own `window` listeners.
|
||||||
|
- **Escape is an ordered priority chain** that returns on first consumption (blocking
|
||||||
|
message → modal → menu → selection) and is checked **before** the interactive-context
|
||||||
|
gate, so it dismisses a modal even while the editor has focus. All _other_ shortcuts are
|
||||||
|
gated by `isInInteractiveContext()` so they never fire mid-typing.
|
||||||
|
- **Shortcuts claim their key** with `preventDefault()` so they override the browser
|
||||||
|
default (⌘S doesn't "save page," ⌘K doesn't focus the URL bar).
|
||||||
|
- **Focus moves into an overlay on open and returns to the trigger on close**; focus is
|
||||||
|
**trapped** within it (`useFocusTrap`). Destructive confirms focus Cancel first.
|
||||||
|
- **Match the APG pattern** for any new interactive widget — a modal is `dialog-modal`, a
|
||||||
|
destructive confirm is `alertdialog`, a resize handle is `windowsplitter`, a toast region
|
||||||
|
uses `alert`/`status` roles by severity. Don't invent keyboard models; adopt the
|
||||||
|
documented one.
|
||||||
|
|
||||||
|
**Resolved — pane resize handle (window splitter).** A `ResizeHandle` is a focusable
|
||||||
|
`role="separator"` that **reports the controlled pane's size**, per APG → Window Splitter:
|
||||||
|
|
||||||
|
- `aria-valuenow` on a **0–100 scale** (0 = pane at its minimum, 100 = at its maximum),
|
||||||
|
with `aria-valuemin=0`, `aria-valuemax=100`, and `aria-valuetext="<n>%"` for a clean
|
||||||
|
announcement. The 0–100 normalization (APG's "typical") beats raw pixels: it's stable and
|
||||||
|
announces as a percentage. The math is the pure `sideWidthValue()` in `PanesStore` (so it
|
||||||
|
is unit-tested, not trapped in the component); the live value needs the container width,
|
||||||
|
observed via `ResizeObserver` so it tracks window resizes, not just drags.
|
||||||
|
- `aria-controls` points at the pane it sizes (`pane-library` / `pane-preview`).
|
||||||
|
- **Keyboard**: ←/→ nudge; **Home** → smallest pane size, **End** → largest. **Enter-to-collapse
|
||||||
|
is deferred to M6** with the pane-visibility / toggle strip — collapsing needs a
|
||||||
|
hidden-pane state that doesn't exist yet, so we don't fake it.
|
||||||
|
|
||||||
|
_(Consulted via `/council` → WAI-ARIA APG `windowsplitter`. This bullet is the contract;
|
||||||
|
cite it, not the APG file.)_
|
||||||
|
|
||||||
|
## 6. Motion & accessibility as default
|
||||||
|
|
||||||
|
Not features to add later — the baseline every surface is built on.
|
||||||
|
|
||||||
|
- **Reduced motion is honored globally.** Animations/transitions are neutralized under
|
||||||
|
`prefers-reduced-motion` (`styles/base.css`); never gate meaning on motion.
|
||||||
|
- **Colour is never the sole signal** (WCAG; Carbon status pattern). Pair it with a label,
|
||||||
|
icon, shape, or text — a red toast border also has a title and an `alert` role; the
|
||||||
|
draft dot has a `title`/`aria-label`.
|
||||||
|
- **Every control is labelled.** Icon-only buttons, toggles, and fields carry accessible
|
||||||
|
names so assistive tech can announce them.
|
||||||
|
- **Contrast holds in every theme.** A theme that can't meet legible contrast in part of
|
||||||
|
the UI is not complete (spec §10 / §07).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Do / Don't
|
||||||
|
|
||||||
|
**Do**
|
||||||
|
|
||||||
|
- Pick the feedback channel from §1's table by the _nature_ of the message.
|
||||||
|
- Treat loading/empty/error as three designed states for every data surface.
|
||||||
|
- Adopt the APG keyboard pattern for new widgets; route all global keys through the one
|
||||||
|
router.
|
||||||
|
- Consult `/council` when this contract is silent — then record the answer back here.
|
||||||
|
|
||||||
|
**Don't**
|
||||||
|
|
||||||
|
- Don't show a blocking dialog for something a toast can carry, or hide a
|
||||||
|
consent-for-destruction in a toast.
|
||||||
|
- Don't let a render or a save block typing.
|
||||||
|
- Don't treat "empty" as "error."
|
||||||
|
- Don't put error codes in the user-facing line — put diagnostics in the detail
|
||||||
|
disclosure, the next step in the message.
|
||||||
|
- Don't invent a keyboard model, attach ad-hoc `window` listeners, or gate Escape behind
|
||||||
|
the typing check.
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
This section defines quality attributes the rebuild must satisfy — performance, accessibility, reliability, privacy, and platform posture — independent of any single feature. Feature behavior lives in the other sections; this one constrains _how well_ that behavior must work.
|
This section defines quality attributes the rebuild must satisfy — performance, accessibility, reliability, privacy, and platform posture — independent of any single feature. Feature behavior lives in the other sections; this one constrains _how well_ that behavior must work.
|
||||||
|
|
||||||
|
> The cross-cutting **interaction patterns** that satisfy these attributes — the feedback-channel decision table, latency budgets, the loading/empty/error triad, the recovery and keyboard/focus contracts — are specified in [Architecture 10 · Interaction & Feedback](../architecture/10-interaction-and-feedback.md). This section is the _what_ (the quality bar); that doc is the _how_ (the patterns that meet it).
|
||||||
|
|
||||||
## Platform & Form Factor
|
## Platform & Form Factor
|
||||||
|
|
||||||
- **Target**: modern evergreen desktop browsers. The app is a single-page application that loads once and then runs locally.
|
- **Target**: modern evergreen desktop browsers. The app is a single-page application that loads once and then runs locally.
|
||||||
|
|||||||
Reference in New Issue
Block a user