Files
ChangeHowandBohdan Triapitsyn 5ae1a949c8 fix(ui): improve composer focus, keyboard navigation, and settings (#3376)
* fix(ui): make composer keyboard interactions consistent

* docs(settings): refine description visibility guidance

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-09-07 20:30:19 +03:00

90 lines
5.4 KiB
Markdown

---
name: settings-ui-patterns
description: Use when creating or modifying OpenChamber Settings pages, dialogs, controls, configuration surfaces, responsive Settings layouts, or Settings search behavior.
---
# Settings UI Patterns
## Required Companion Skills
- Load `theme-system` for colors, buttons, icons, and visual states.
- Load `locale-ui-patterns` for every visible string, tooltip, placeholder, and accessible label.
- Load `ui-api-decoupling` when a setting reads/writes runtime data or adds a capability.
When examples conflict, shared component/theme and localization contracts win. Stop on unresolved material conflicts.
## Canonical Direction
Settings are built from the shared primitives in
`packages/ui/src/components/sections/shared/SettingsSection.tsx`,
`SettingsPageLayout.tsx`, and `SettingsInfoHint.tsx`. Never hand-roll page
chrome, section headers, field rows, checkbox rows, or info tooltips with raw
divs — use the primitives, and extend them (in the shared file) when a new
shape is genuinely missing.
- Flat hierarchy through spacing and typography; no cards, boxed backgrounds, or row chrome.
- Secondary helper text is hidden behind an info icon (`info` prop) by default; the default view stays quiet.
- Controls have one standard size (`h-9` / select `size="settings"`) and capped widths — no full-bleed inputs.
- Layouts respond to the settings pane width via container queries (`@xl:` / `@3xl:`), never viewport `sm:`/`lg:` breakpoints (the pane is much narrower than the viewport inside the dialog).
- Checkbox/radio state comes before labels; selected states are subtle and never shift layout.
## Load References By Task
| Task | Required reference |
|---|---|
| Page skeleton, sections, hierarchy, nav placement, spacing, columns, responsiveness | `references/layout.md` |
| Field rows, checkboxes, radios, chips, selects, inputs, numeric steppers, info hints | `references/controls.md` |
| Adding/moving controls, pages, availability, anchors, or search entries | `references/search.md` |
Load each reference whose task branch applies; reference loading is complete when layout, control, and search implications are each classified.
## Quick Primitive Selection
| Need | Shared primitive |
|---|---|
| Page wrapper (title, description, save status, scrolling, `@container`) | `SettingsPageLayout` |
| Titled block with divider | `SettingsSection` (`divider={false}` for the first one) |
| Label left / control right | `SettingsFieldRow` |
| Label above control (two-column cells, wide controls) | `SettingsStackedField` |
| Boolean | `SettingsCheckboxRow` |
| Mutually exclusive list | `SettingsRadioGroup` + `SettingsRadioOption` |
| Short segmented options | `SettingsChipGroup` |
| Sub-cluster with a quiet L3 title inside a section | `SettingsControlGroup` |
| Two-column area on wide panes | `SettingsTwoColumn` |
| Helper text on demand (hover + tap) | `info` prop or `SettingsInfoHint` |
Do not introduce raw `<Tooltip>`-based info icons, direct Remixicon components, hardcoded user-facing strings, or one-off color/button systems. New icons: reference a Remix icon name in code, then run `bun run icons:generate` to add it to the sprite.
## Description Policy (info hints)
- Explanatory prose goes behind the info icon via the `info` prop by default.
- When labels alone cannot explain the differences, consequences, or conditions needed to choose a setting, use a title, a visible description, then checkbox or radio controls. Large-text paste modes and send shortcuts with expanded-composer exceptions need this explanation. Having multiple options or a group title alone does not require a description; see `references/controls.md` for composition.
- Stays visible: security/data-loss warnings, destructive consequences, required syntax/placeholder lists the user reads while typing, dynamic status, empty states, validation errors, active-flow wizard instructions.
- Mixed text: keep the warning sentence visible, move the explanation to `info`.
## Save Feedback
`SettingsPageLayout showSaveStatus` renders the shared quiet indicator: success is silent, "Saving…" appears only past ~500 ms, failures show "Save failed". Anything persisted through `updateDesktopSettings` reports automatically; page-specific APIs must call `reportSettingsSaveState` from `@/lib/persistence`. Never add per-page save badges or success toasts for ordinary setting writes.
## Settings Search Contract
Every stable Settings control addition or move must consider search in the same change:
- explicit registry item in `packages/ui/src/lib/settings/search.ts` when searchable;
- matching `data-settings-item` anchor (primitives accept `settingsItem`);
- localized title/description keys;
- availability matching actual render conditions;
- when a control moves to another page, update the item's `page` too.
Dynamic entity rows normally are not indexed. Load `references/search.md` for exact rules.
## Completion Criteria
- Built from shared primitives; no ad-hoc page/section/row markup.
- Description placement follows the policy above; warnings/syntax/status remain visible.
- Container-query (`@xl:`/`@3xl:`) responsiveness — no viewport breakpoints in pane content.
- Controls use the standard size and width caps; no stretched full-width inputs.
- Localized visible and accessibility text everywhere.
- Search registry, anchor, page, localization, and availability agree.
- Nearby Settings precedent and relevant tests remain consistent.