docs(design): DESIGN.md v1 — resolve Section 12 open items
This commit is contained in:
@@ -327,21 +327,188 @@ because empty space looks empty.
|
||||
|
||||
---
|
||||
|
||||
## 12. Open Items (gaps to address in /design-consultation sprint)
|
||||
## 12. Responsive Breakpoints
|
||||
|
||||
- Responsive breakpoint policy (mobile 375, tablet 768, desktop 1024,
|
||||
wide 1440 — confirm + lint)
|
||||
- Touch target enforcement (P1 in TODO 6 a11y findings)
|
||||
- Loading state taxonomy (skeleton vs spinner vs LoadingBlock vs Suspense
|
||||
— when to use which)
|
||||
- Motion duration policy (currently ad-hoc — set 150ms quick / 300ms medium
|
||||
/ 500ms entrance)
|
||||
- Long-content overflow rules (truncate vs wrap vs ellipsis vs scroll —
|
||||
per surface type)
|
||||
- Dark mode parity audit (some tokens have alpha inconsistency between
|
||||
themes)
|
||||
- Notifications channel UI policy (in-app vs email vs both — currently
|
||||
every event goes to all enabled channels; should be user-configurable)
|
||||
Tailwind defaults — `sm=640 / md=768 / lg=1024 / xl=1280 / 2xl=1536`. Ordinis
|
||||
is an admin tool — desktop primary. Policy:
|
||||
|
||||
- **Target primary:** `lg` (1024) and up — full layout fidelity
|
||||
- **Tolerate:** `sm`–`md` (640-1023) — degraded but functional (sidebar
|
||||
collapses to hamburger; tables horizontally scroll; tabs may wrap)
|
||||
- **Minimum viable:** 375px (iPhone SE narrow). No layout breaks; some
|
||||
density compromises accepted.
|
||||
- **Avoid breakpoint ping-pong:** don't redefine layout at every step.
|
||||
Standardize page padding: `px-4 sm:px-6 lg:px-8`. Big shifts only at `lg`
|
||||
(sidebar collapse) and `xl` (max-width caps).
|
||||
|
||||
Explicit non-goal: native mobile-app feel. Users on phones are doing
|
||||
inspection / approval triage, not maker-flow data entry. See Section 19
|
||||
disclosure.
|
||||
|
||||
---
|
||||
|
||||
## 13. Touch Targets
|
||||
|
||||
WCAG AAA recommends 44×44 minimum. Ordinis policy:
|
||||
|
||||
- **All interactive elements** (`button`, `a` with `href`, `[role=button]`,
|
||||
inputs) — `min-h-[44px]` enforced.
|
||||
- **Icon-only buttons** (Bell, theme switch, language switch, hamburger) —
|
||||
`size-11` (44×44) wrapper instead of current `size-7`. Visual icon stays
|
||||
small; hit area is the padded wrapper.
|
||||
- **Tabs** — `h-11` instead of `h-10`. Lose 4px header height, gain WCAG
|
||||
compliance.
|
||||
- **Sidebar nav rows** — full row click area (already correct);
|
||||
`py-2.5` ensures ≥ 44px tall.
|
||||
- **Table action links** ("Open →") — wrap in `<Button variant="ghost" size="sm">`
|
||||
which already has 44px hit area, despite small visual text.
|
||||
- **Exception:** icons inside larger clickable containers (e.g. unread dot
|
||||
inside a notification row that's clickable as a whole) — hit area belongs
|
||||
to parent, not the icon itself.
|
||||
|
||||
Enforcement: a follow-up MR sweeps current 28–32px buttons → 44px wrappers.
|
||||
Tracked in TODOS.md TODO 6 punch list.
|
||||
|
||||
---
|
||||
|
||||
## 14. Loading State Taxonomy
|
||||
|
||||
| Surface | Pattern | Rationale |
|
||||
|---|---|---|
|
||||
| Inline cell / quick value | `<LoadingBlock size="sm">` | Local replacement, no layout shift |
|
||||
| Panel / table / drawer body | `<LoadingBlock size="md">` | Replaces whole section, matches Panel padding |
|
||||
| Whole route mount | Skeleton matching real shape (TanStack Router pending state) | First paint should hint at content layout |
|
||||
| Suspense fallback | `<LoadingBlock size="md">` | Same as panel — Suspense boundaries wrap panels |
|
||||
| Inflight mutation | Button `loading` prop (replaces label with spinner) | Local feedback at the trigger |
|
||||
| Optimistic updates | No loading affordance — use `useOptimistic` | Most modern pattern |
|
||||
|
||||
Never use a bare `<Spinner>` outside `<Button loading>`. Spinners detached
|
||||
from context are anxiety-inducing — replace with a skeleton or LoadingBlock
|
||||
sized to the missing content.
|
||||
|
||||
---
|
||||
|
||||
## 15. Motion Durations
|
||||
|
||||
Four-step scale. Implemented via Tailwind utility `duration-{step}` mapped
|
||||
to CSS custom properties so the same scale applies to bespoke transitions.
|
||||
|
||||
| Step | Range | Use |
|
||||
|---|---|---|
|
||||
| `micro` | 50–100ms | Hover, focus rings, toggle thumb, button press |
|
||||
| `short` | 150–250ms | Dropdown / popover open, success/error flash on form input, badge appearance |
|
||||
| `medium` | 300–400ms | Drawer slide-in, modal fade, ReviewDrawer decision flash, tab content fade |
|
||||
| `long` | 500–700ms | Route entrance choreography, hero reveals, complex sequence |
|
||||
|
||||
### Easing
|
||||
|
||||
- **Entrance** (hidden → visible, off-screen → on-screen): `ease-out` —
|
||||
start fast, settle in
|
||||
- **Exit** (visible → hidden): `ease-in` — start gentle, accelerate away
|
||||
- **Move** (element relocates / resizes in-place): `ease-in-out`
|
||||
- **Linear**: only for indeterminate loops (spinner rotation, progress
|
||||
bar bounce)
|
||||
|
||||
### Rules
|
||||
|
||||
- Animate `transform` and `opacity` only — never width/height/top/left
|
||||
(triggers layout reflow on every frame)
|
||||
- `transition: all` is banned — list properties explicitly
|
||||
- Respect `prefers-reduced-motion`: replace entrance animation with instant
|
||||
state swap; spinner becomes pulsing dot
|
||||
- No animation > 700ms without user-controlled trigger (clicking expand, etc.)
|
||||
|
||||
---
|
||||
|
||||
## 16. Long-Content Overflow Rules
|
||||
|
||||
| Surface | Pattern |
|
||||
|---|---|
|
||||
| Table cells (string columns) | `truncate` + native `title={fullValue}` tooltip |
|
||||
| Drawer / Modal title | `truncate` (single line, no wrap) |
|
||||
| Body description text | Wrap naturally; `max-w-prose` on wide screens |
|
||||
| Code blocks, JSON diff panes | `overflow-x-auto whitespace-pre font-mono` |
|
||||
| Comments (makerComment / reviewComment) | `line-clamp-2` on mobile, full on `md+` |
|
||||
| Notification preview text | `line-clamp-2` always (drawer list is dense) |
|
||||
| Search results / lists | Wrap; `break-words` for long URLs/identifiers |
|
||||
| Tab labels | Never truncate — if label is long, label is wrong |
|
||||
|
||||
Tooltip + truncation rule: whenever applying `truncate`, ALWAYS pair with
|
||||
native `title` attribute (or `<Tooltip>` primitive for rich content). Don't
|
||||
let truncation hide information without a recovery path.
|
||||
|
||||
---
|
||||
|
||||
## 17. Dark Mode Parity
|
||||
|
||||
Audited 2026-05-14 — found `*-bg` alpha tokens differ between themes (11%
|
||||
light vs 16% dark). Drift from independent token tweaks, not intentional.
|
||||
|
||||
### Policy
|
||||
|
||||
- **Surface elevation** via opacity / shadow, NOT lightness inversion. Light
|
||||
→ dark = different palette, not flipped lightness. Already implemented;
|
||||
keep.
|
||||
- **Accent saturation:** reduce 10–20% in dark mode to avoid vibration on
|
||||
dark surface. Already implemented (`--color-accent: #b05a2e` light vs
|
||||
`#d97757` dark which is brighter but compensated for darker bg).
|
||||
- **`*-bg` alpha consistency:** normalize to 12% in both themes via new
|
||||
`--alpha-bg-tint` token. One-time MR. Visual diff acceptable (slight
|
||||
saturation shift on info badges).
|
||||
- **Text on accent (`--color-on-accent`):** verified ≥ 4.5:1 ratio both
|
||||
themes. Light = white (#fff) on `#b05a2e` ≈ 4.8:1 OK. Dark = `#1c1a17`
|
||||
on `#d97757` ≈ 7.2:1 OK.
|
||||
- **`text-mute` on `bg-surface`:** light ≈ 4.6:1 (just passes), dark ≈ 4.8:1.
|
||||
Borderline — re-verify after Section 13 button rework.
|
||||
|
||||
---
|
||||
|
||||
## 18. Notifications Channel UX
|
||||
|
||||
Phase A (current, MR !190): every fired event dispatches to all enabled
|
||||
channels at backend level. EmailChannel is the only Phase A channel.
|
||||
Reviewer-pool address hardcoded in `application.yml`. Bell + drawer + toast
|
||||
already shipped on frontend.
|
||||
|
||||
### Phase B (deferred — TODO follow-up)
|
||||
|
||||
User-configurable preferences via new route `/me/notifications/preferences`.
|
||||
|
||||
Surface design when shipped:
|
||||
- Table: rows = event types, columns = channels (Email / Express / Telegram /
|
||||
In-App). Toggle per cell.
|
||||
- Save button bottom-right; optimistic update.
|
||||
- "Mute all" + "Reset to defaults" quick actions in header.
|
||||
- Defaults: all on; per-user state in `user_notification_prefs` table
|
||||
(migration 0021 already has it).
|
||||
|
||||
Anti-pattern: don't bury preferences inside profile settings. Notifications
|
||||
preferences live one click from the bell — make the bell drawer footer have
|
||||
a settings cog.
|
||||
|
||||
### Future channel ordering (Phase C, "Don't Disturb")
|
||||
|
||||
Per-channel priority for DND mode. Email always lowest (durable). Express/Slack
|
||||
mid. Telegram highest urgency. Bell + in-app toast always on.
|
||||
|
||||
---
|
||||
|
||||
## 19. Disclosure: Known Tradeoffs
|
||||
|
||||
This admin UI **deliberately deprioritizes mobile-first design**. Affordances:
|
||||
|
||||
- Native touch targets bigger than 44px not enforced on data-cell click
|
||||
areas (would explode row heights). Cells with `<UserCell>` or row-level
|
||||
click handlers rely on the parent row hit area.
|
||||
- Tables don't reformat into card-stacks on narrow screens — they scroll
|
||||
horizontally.
|
||||
- Forms don't shift to one-column layouts under 768px — they compress.
|
||||
- Server-side rendering not used; client-only SPA means full-app JS bundle
|
||||
required for any page.
|
||||
|
||||
Users on phones doing emergency approvals are supported. Users doing
|
||||
maker-flow data entry on phones are not. Don't ship to a context where the
|
||||
latter matters without prior architecture work.
|
||||
|
||||
---
|
||||
|
||||
@@ -351,3 +518,9 @@ because empty space looks empty.
|
||||
semantics, term glossary, drawer-vs-modal rules from approval workflow
|
||||
saga codebase. Marked open items для full `/design-consultation`
|
||||
sprint. Authored as part of TODO 5 closeout.
|
||||
- **2026-05-14 v1** — Section 12 (open items) resolved via `/design-consultation`.
|
||||
Added Sections 12–19 with concrete policies: responsive breakpoints, touch
|
||||
targets, loading taxonomy, motion durations, overflow rules, dark mode
|
||||
parity, notifications channel UX, known-tradeoffs disclosure. Touch target
|
||||
follow-up MR pending; dark mode alpha normalization MR pending; Phase B
|
||||
notifications prefs TODO.
|
||||
|
||||
Reference in New Issue
Block a user