Files
Art-gallery/Documentation/ui-interaction-and-component-standards.md

31 KiB
Raw Permalink Blame History

UI Interaction and Component Standards

Subject: Virtual Art Gallery — screen and interaction guidance
Applies to: Public visitor UI, 3D halls, curator tools
Companion rule: .cursor/rules/ui-interaction-standards.mdc


0. Introduction

The Gallery is a single React SPA (HomePage.tsx view union — no URL router except ?layout=). Public visitors browse a museum-dark timeline and 3D halls; curators use the same shell for catalog tools (Checkup, Translations, Influences, Tours, Users, Activity). Work lands in the same header, palette, and back-stack, so a new screen that invents its own chrome, confirmations, or loading pattern fragments the experience.

This document is the shared contract for how the UI behaves. Architecture, APIs, and 3D hall construction live in basics.md, API.md, and data-and-images.md. Tree geometry lives in movement-tree.md. Locale strings live in i18n-russian.md.

0.1 Purpose and audience

  • Purpose: Define common UI interaction, navigation, and component standards for the Gallery client.
  • Primary audience: Developers (and agents) adding or changing client/src UI; anyone writing curator-tool screens.
  • Secondary audience: QA, product, copy/i18n.

0.2 Scope

  • In scope:
    • Navigation and back-stack behaviour
    • Timeline / search / 3D / detail / curator-tool interaction patterns
    • Loading, empty, error, and confirmation behaviour
    • Permission-based UI (can() / RBAC)
    • Shared component and visual-language rules
  • Out of scope:
    • REST contracts, retries, and image pipeline internals
    • Three.js hall architecture, textures, and lighting (see basics.md)
    • Pixel-perfect branding kit (no separate design-system package; follow existing CSS)

0.3 Requirement levels

  • MUST: Mandatory for new work and for fixes that touch the same screen.
  • SHOULD: Recommended; deviate only with a short note in the PR or screen section.
  • MAY: Optional pattern when the screen specification calls for it.

0.4 Surface map

Surface Typical components Visitors Curators
Timeline home Timeline, VerticalTimeline, MovementBands, VerticalMovementBands, MovementTree, CatalogSearchBar yes yes
3D hall VirtualGallery, wing navigator, exit overlays yes yes
Painting / bio PaintingDetail, PaintingLightbox, ArtistBio, PaintingAnnotations yes + debug panel when can('images')
Overlays CuratorLoginModal, ArtistFilterModal, ToursPopup, DebugSearchResultsModal some all
Curator tools CheckupPage, TranslationsPage, InfluencesPage, ToursPage, UsersPage, AuditPage no permission-gated

1. Design Goals and Principles

1.1 Problem statement

  • Statement: Visitors and curators share one shell. Inconsistent back labels, ad-hoc modals, English-only captions, and one-off loading/error treatment make the museum feel like several apps glued together.
    • Scope: All client/src screens and overlays.
    • Rationale: The product is a gallery, not an admin console with a visitor skin. Predictable chrome is part of the exhibit.
    • Verification: Compare a new screens header, back control, loading marker, and locale keys against this document.

1.2 Functional design principles

  • Principle 1: One museum, two roles

    • Statement: Public browse chrome MUST stay museum-dark (navy / gold / Georgia). Curator tools MAY be denser but MUST reuse the same header back pattern, gold accent, and permission hiding — they MUST NOT look like a separate product.
    • Rationale: Curators enter from the same timeline; a visual cliff breaks trust.
    • Verification: Side-by-side with timeline header and Checkup / Tours editor.
  • Principle 2: Predictable drill-down and return

    • Statement: Navigation MUST follow Timeline → (movement picker) → hall → painting/bio, with an explicit in-app Back that restores the intended session — not the browser history stack.
    • Rationale: HomePage owns view state; the browser Back button is not wired.
    • Verification: Walk the basics.md navigation flow and the Back table in §2.4.
  • Principle 3: Clarity of actions and feedback

    • Statement: Every user-initiated load, save, delete, or failed request MUST show a loading, success, or error state the user can see without opening the console.
    • Rationale: 3D and image work is slow; silent failure looks like a broken hall.
    • Verification: Trigger catalog load, hall open, form save, and a failed API call.
  • Principle 4: Minimize cognitive load

    • Statement: Timeline charts MUST keep zoom/pan/click hints visible. Curator tables MUST put filter/search above the grid. Destructive actions MUST be confirmed.
    • Rationale: Dense history data and catalog tables are easy to mis-click.
    • Verification: Hint captions present; filters above tables; delete paths show a confirm.
  • Principle 5: Locale and permission are first-class

    • Statement: New visitor-facing copy MUST go through react-i18next (locales/{en,ru}). Actions the user cannot perform MUST be hidden, not disabled-without-explanation.
    • Rationale: EN/RU is a product requirement; exposing forbidden tools invites errors.
    • Verification: Toggle EN|RU; log in as a curator without the relevant can() and confirm the control is absent.

2. Navigation Principles

2.1 Application chrome (no left module rail)

The Gallery has no persistent left navigation menu. Primary wayfinding is the timeline header on home, and an explicit Back control on every nested view.

  • Statement: The timeline home MUST keep layout switch, catalog search, locale switcher, and role-appropriate tools in the site header.

    • Scope: HomePage when view is timeline / timeline-vertical / timeline-tree.
    • Rationale: Visitors need search and layout without hunting; curators need tools without leaving the museum frame.
    • Verification: Header remains usable at 100vh; search dropdown stacks above movement bands (z-index on .site-header).
  • Statement: Layout switch links MUST stay in the top-left (.site-layout-switch). The Tree of Art control SHOULD use .site-layout-link-feature.

    • Scope: Timeline home.
    • Rationale: Three layouts must stay discoverable; Tree is the featured alternative start page.
    • Verification: Classic / Vertical / Tree links match basics.md.
  • Statement: Nested views (hall, painting, bio, curator pages) MUST NOT reintroduce a second global nav. They MUST show a single primary Back control in the page header.

    • Scope: All non-home views.
    • Rationale: Avoid competing menus; the drill-down is the nav.
    • Verification: No duplicate “home” plus “modules” rails on curator pages.

2.2 Page hierarchy

  • Statement: Screens MUST stay within this hierarchy (max four levels):

    1. Timeline home (classic / vertical / tree)
    2. Overlay or picker (artist filter, tours popup, login) or curator tool page
    3. 3D hall (artist / movement / tour)
    4. Painting detail or artist bio (optional lightbox on top of detail)
    • Scope: All visitor and curator flows.
    • Rationale: Matches the existing drill-down; deeper stacks become unrecoverable without a router.
    • Verification: New views are added to the View union in HomePage.tsx with a defined parent and Back handler.
  • Statement: Curator tools MUST open as siblings of the timeline (replace the home canvas), not as a fifth level under a hall.

    • Scope: Checkup, Translations, Influences, Tours editor, Users, Activity.
    • Rationale: Tools operate on the catalog, not on a hall session.
    • Verification: Opening Checkup from a hall is not required; from timeline header, Back returns to timeline home.

2.3 Location awareness (no breadcrumbs)

  • Statement: The app MUST NOT add a breadcrumb trail unless a future router lands. Until then, the Back label MUST name the destination (← Back to Timeline, ← Back to Gallery, ← Back).

    • Scope: All nested views.
    • Rationale: There is no URL path to reflect; a fake breadcrumb would lie.
    • Verification: Labels match §2.4; they are i18n keys (backToTimeline, backToGallery, …).
  • Statement: Timeline layout MUST be deep-linkable via ?layout=classic|vertical|tree (omit param for classic). Other views MUST NOT pretend to be bookmarkable until a router exists.

    • Scope: Timeline home.
    • Rationale: Layout is the one shareable start-page choice; halls and tools are session state.
    • Verification: Load ?layout=tree, switch layouts, confirm history.replaceState updates the query.

2.4 Back navigation

Control MUST return to
← Back to Timeline (hall header, movement Exit to Timeline, painting opened from search) Home timeline via goToTimelineHome() — unmount hall, clear session, reset year window to full catalog bounds
← Back to Gallery (painting from a hall) Same hall session (camera / wing preserved)
← Back (artist bio) returnTo view (usually the hall that opened bio)
Curator page Back Timeline home
  • Statement: Back MUST be an in-app control. The browser Back button MUST NOT be relied on (it is not wired to View state).

    • Scope: All nested views.
    • Rationale: setView is the router.
    • Verification: From painting detail, in-app Back restores the hall; browser Back does not need to.
  • Statement: goToTimelineHome() MUST be the single implementation for “leave everything and show the timeline.” New exits MUST call it rather than duplicating reset logic.

    • Scope: Halls, search-opened paintings, curator Back.
    • Rationale: Year-range reset and session clear must stay consistent.
    • Verification: After Exit to Timeline, viewStart/viewEnd equal catalog bounds.

2.5 State preservation

  • Statement: Timeline pan/zoom (viewStart / viewEnd) MUST persist while the user stays on a timeline layout. Switching classic ↔ vertical ↔ tree MUST keep the same year window.

    • Scope: Timeline home.
    • Rationale: Layout is a lens, not a new dataset.
    • Verification: Zoom, switch to Tree, confirm the year rail range is unchanged.
  • Statement: Returning to timeline via goToTimelineHome() MUST reset the year window to full catalog bounds.

    • Scope: Hall / search / curator exits that call goToTimelineHome().
    • Rationale: Documented in basics.md; visitors expect a fresh overview, not a leftover zoom.
    • Verification: Zoom in, enter a hall, Back to Timeline → full span.
  • Statement: Hall camera and wing MUST be preserved across Back to Gallery from painting detail. They MUST be discarded on Back to Timeline.

    • Scope: Artist, movement, and tour halls.
    • Rationale: Inspecting a painting is a detour; leaving the museum is not.
    • Verification: Move in the hall, open a painting, Back to Gallery → same viewpoint.
  • Statement: Catalog search input MAY clear when the dropdown closes. It MUST NOT change timeline zoom by itself.

    • Scope: CatalogSearchBar.
    • Rationale: Search is a jump, not a filter on the chart.
    • Verification: Type a query, Escape; year window unchanged.

3. Common UI Interaction Patterns

3.1 Catalog search (visitor)

  • Statement: Timeline search MUST live in the header, require 2 trimmed characters, debounce 300 ms, and group results into Artists, Movements, Paintings.

    • Scope: CatalogSearchBar.tsx.
    • Rationale: Documented product behaviour; keeps /api/search load reasonable.
    • Verification: 1 character shows no fetch; 2+ after debounce shows groups.
  • Statement: Keyboard MUST support / highlight, Enter to open, Escape to close.

    • Scope: Catalog search dropdown.
    • Rationale: Timeline is pointer-heavy; search should still be keyboardable.
    • Verification: Keyboard-only open of an artist, movement, and painting.
  • Statement: Opening a painting from search MUST set returnTo timeline so Back is ← Back to Timeline, not Gallery.

    • Scope: Search → painting detail.
    • Rationale: There is no hall session.
    • Verification: Search a title, open, Back → home timeline.

3.2 Timeline charts (zoom, pan, click)

  • Statement: Scroll MUST zoom, drag MUST pan, click on a stream/limb/label MUST open the movement (artist filter → hall). All three layouts MUST use zoomTimelineView / panTimelineView.

    • Scope: MovementBands, VerticalMovementBands, MovementTree, era rails.
    • Rationale: Shared year window; one mental model.
    • Verification: Same wheel/drag behaviour on classic, vertical, and tree.
  • Statement: Each chart MUST show a localised hint caption (captionClassicTimeline, captionClassicFlow, captionVerticalTimeline, captionVerticalFlow, captionTreeFlow).

    • Scope: Timeline home.
    • Rationale: First-time visitors cannot discover zoom/pan otherwise.
    • Verification: EN and RU captions change with LocaleSwitcher.
  • Statement: Timeline layout shifts (lane packing, tree fit scale) SHOULD animate rather than snap.

    • Scope: Movement charts.
    • Rationale: Unexplained jumps look like bugs.
    • Verification: Zoom/pan does not teleport streams.

3.3 Filters and search (curator tables)

  • Statement: Filter/search controls MUST sit in a toolbar above the table, not in a column header hack or a page footer.

    • Scope: Checkup, Translations, Influences, Tours editor, Users, Activity.
    • Rationale: Matches Checkup (checkup-toolbar) and keeps the grid scannable.
    • Verification: Filters remain visible while the table scrolls.
  • Statement: Simple text filters MAY apply as the user types. Expensive operations (image search, import, refetch) MUST require an explicit button (e.g. Checkup Search visible).

    • Scope: Curator list screens.
    • Rationale: Checkup search is rate-limited and slow; typing must not fire it.
    • Verification: Typing in Checkup filter does not start image search.
  • Statement: When a filter hides rows, the toolbar SHOULD show how many rows are visible (e.g. N shown).

    • Scope: Filtered tables.
    • Rationale: Empty-looking tables need an explanation.
    • Verification: Filter to zero rows → empty state plus count.

3.4 Date selection

  • Statement: Year fields in the catalog and timeline MUST use numeric years (negative = BCE). They MUST NOT switch to locale-specific calendar widgets for historical BCE dates.

    • Scope: Timeline bounds, artist lifespan, painting years, curator year filters.
    • Rationale: The catalog spans 800 to the present; HTML date inputs cannot represent BCE.
    • Verification: Ancient era still filters correctly.
  • Statement: If a future screen needs a civil date (e.g. audit log day), it SHOULD use ISO YYYY-MM-DD and validate start ≤ end for ranges.

    • Scope: Activity / audit and any new timestamp filters.
    • Rationale: Consistent with API timestamps; avoids DD/MM ambiguity.
    • Verification: Invalid range shows a field-level message.

3.5 Tables and lists (curator)

  • Statement: Structured curator datasets MUST use a labeled HTML table (or existing page table classes), one logical record per row.

    • Scope: Checkup, Users, Influences worklists, Tours list, Translations worklist, Activity.
    • Rationale: Comparison and row actions need columns, not cards.
    • Verification: Column headers present; row click/action affects one record.
  • Statement: Visitor-facing catalog MUST NOT be presented as a spreadsheet. Timeline streams, tree limbs, and 3D hangs are the list metaphor.

    • Scope: Public home and halls.
    • Rationale: The product is a gallery, not a DAM table.
    • Verification: No “all paintings” data grid on the public home.
  • Statement: Tables MAY omit pagination while the dataset is curator-sized and client-filtered. If a list grows past comfortable scrolling, it SHOULD paginate or virtualise rather than rendering thousands of DOM rows.

    • Scope: Curator tools.
    • Rationale: Checkup is already filter-then-scroll; unbounded paint is a future foot-gun.
    • Verification: New tools with large lists have a documented paging or virtualisation plan.
  • Statement: Row actions MUST sit in a dedicated column or overflow control, not as random icons in every cell.

    • Scope: Interactive curator tables.
    • Rationale: Scanability.
    • Verification: Action column or consistent button set per row.

3.6 Multi-row and per-record actions

  • Statement: Bulk actions MUST use a leading checkbox column, Select All for visible rows only, and a confirmation that includes the affected count for destructive work.

    • Scope: Any new bulk-enabled table. (Today: ArtistFilterModal multi-select is a picker, not a bulk delete.)
    • Rationale: Same as the Logistics template; prevent silent mass edits.
    • Verification: Select All does not imply “all matching in the database” unless explicitly labelled.
  • Statement: Artist filter before a movement hall MUST be a modal checklist with explicit proceed/cancel, not a bulk-edit of the catalog.

    • Scope: ArtistFilterModal.
    • Rationale: It only chooses who appears in the hall.
    • Verification: Cancel leaves the user on the timeline; proceed opens the hall.
  • Statement: Primary row/object action SHOULD be the name/title (open painting, open user, open tour). Secondary actions SHOULD stay in the rows action controls.

    • Scope: Curator tables and search results.
    • Rationale: Matches search-result click-to-open.
    • Verification: Clicking a Checkup title opens the painting when that handler exists.
  • Statement: Unavailable-by-permission actions MUST be hidden. Unavailable-by-record-state SHOULD be disabled with a title/tooltip explaining why.

    • Scope: All tools.
    • Rationale: RBAC vs workflow are different signals.
    • Verification: Non-admin does not see Users; a disabled Fix button states why.

3.7 Forms

  • Statement: Short auth and picker flows MUST use a modal. Multi-section catalog editors (Users create/edit, Tours editor, Influences wizard, Translations worklist) MUST be full-page (or the existing page layout), not nested modals.

    • Scope: All forms.
    • Rationale: Halls and timeline need to stay the “place”; heavy edit needs space.
    • Verification: Login is modal; Users is a page.
  • Statement: Forms SHOULD be a single column. Related fields MAY group under a heading when there are more than five inputs.

    • Scope: Curator forms.
    • Rationale: Scanning beats dense multi-column on museum-width pages.
    • Verification: Users create form remains vertically grouped.
  • Statement: Required fields MUST use the native required attribute and/or a visible marker; validation errors MUST appear next to the field or as a form-level error the submit control does not obscure.

    • Scope: Login, Users, Tours, Influences, Translations.
    • Rationale: Silent submit-disable is not enough.
    • Verification: Submit empty login → field or form error, not a blank modal.
  • Statement: After successful save, the system MUST show an inline success message (or equivalent) and keep the user on the tool unless the spec says to return to timeline.

    • Scope: Curator mutations.
    • Rationale: Users page already uses message / error banners.
    • Verification: Save permissions → success text; failed save → error text.
  • Statement: Unsaved-change guards SHOULD be added when a form is long enough that accidental Back would lose work (Tours editor, Translations). Login and tiny pickers MAY skip this.

    • Scope: Heavy editors.
    • Rationale: window.confirm on delete already exists; abandon-edit is the remaining hole.
    • Verification: Dirty Tours editor + Back prompts or discards explicitly.

3.8 Edit interaction pattern selection

Pattern Use when
Modal Login, artist filter, tours list popup, debug image picker, lightbox, hall exit/wing overlays
Inline Checkup flags, debug Checked / Fix it on painting detail — small, reversible
Full page Curator tools, painting detail, artist bio, 3D hall
Drawer MUST NOT be introduced unless a spec adds a shared drawer component
  • Statement: Nested modals MUST NOT be used (no modal opened from another modal). The debug “More” picker MAY stack on painting detail because detail is a full page, not a modal.
    • Scope: All overlays.
    • Rationale: Focus traps and Back labels break.
    • Verification: Login does not open another dialog.

3.9 Modals and overlays

  • Statement: Modals MUST use role="dialog" and aria-modal="true", a visible close/cancel, and Escape to dismiss unless a submit is in flight.

    • Scope: CuratorLoginModal, ArtistFilterModal, ToursPopup, DebugSearchResultsModal, PaintingLightbox, hall exit overlays.
    • Rationale: Accessibility and parity with search.
    • Verification: Esc closes lightbox and debug picker; backdrop click matches existing login behaviour.
  • Statement: Backdrop click MAY close pickers and login. It MUST NOT close a modal that is applying a destructive or long-running action.

    • Scope: Overlays.
    • Rationale: Accidental dismiss during Fix/upload is costly.
    • Verification: Click outside login closes; do not dismiss mid-upload.

3.10 Loading, empty, and error states

  • Statement: Catalog, portrait, and hall loads MUST use GalleryLoadingMarker (overlay or banner), not an ad-hoc spinner per screen unless the marker cannot cover the region.

    • Scope: Home, halls, painting/bio image work.
    • Rationale: One recognisable “the museum is fetching” treatment.
    • Verification: First visit shows “Loading art history…”; hall open uses the same marker family.
  • Statement: Loading SHOULD be scoped to the affected region. Full-viewport overlay MUST be used only when the user cannot usefully interact (first catalog load, hall WebGL init).

    • Scope: All loads.
    • Rationale: Portrait banner vs full-page overlay already follows this.
    • Verification: Timeline remains visible while “Loading portraits…” banners.
  • Statement: Empty datasets MUST explain themselves (e.g. vertical flow: no movements in range; search: no matches; Checkup: no rows for filter).

    • Scope: Charts, search, tables.
    • Rationale: Blank gold-on-navy reads as a crash.
    • Verification: Zoom to a year with no movements; search a nonsense string.
  • Statement: Failed loads MUST set a visible error string (error-banner, form error, or page error) — never console.error alone.

    • Scope: All data-fetching views.
    • Rationale: Visitors have no console.
    • Verification: Stop the API and confirm home shows a load failure message.

3.11 3D hall interaction

  • Statement: Halls MUST keep ← Back to Timeline, pointer-lock / click-to-move as already implemented, and E (or documented key) for exit/wing navigation. New hall UI MUST not steal those keys without updating this section.

    • Scope: VirtualGallery.
    • Rationale: Muscle memory across artist, movement, and tour halls.
    • Verification: Same Back label and exit overlay pattern in all three hall kinds.
  • Statement: Clicking a framed painting MUST open painting detail with returnTo the current hall. Missing images MUST show the draped-canvas placeholder, not a broken <img>.

    • Scope: Halls.
    • Rationale: Documented in basics.md.
    • Verification: Work without a file still shows a frame cover.

4. Behavioral Standards (Screen UX)

4.1 Permission-based rendering (RBAC)

  • Statement: Header tools and debug controls MUST render only when can('<permission>') (and login for curator). Anonymous visitors MUST see browse + Tours popup + locale, not Checkup/Users/etc.

    • Scope: HomePage header, painting/bio debug panel.
    • Rationale: Security UX: hide, dont tease.
    • Verification: Logged-out home; curator without users cannot open Users.
  • Statement: Route-like views that require a role MUST show the existing “Curator access required” panel with login and back-to-gallery actions — they MUST NOT render an empty privileged page.

    • Scope: Checkup, Translations, Influences, Tours editor, Users, Activity.
    • Rationale: Deep view state can still be set; the gate must hold.
    • Verification: Set view to Users while logged out → curator required copy.
  • Statement: The same permission MUST hide the same action on every surface (header, painting debug, API). Do not leave a visible button that 403s.

    • Scope: All mutations.
    • Rationale: Predictable roles.
    • Verification: can('images') off → no debug panel and no Fix buttons.

4.2 Notifications and user feedback

  • Statement: Curator mutations MUST show success or error text in the pages existing banner/message area. The app has no global toast system; new screens MUST NOT invent a third notification widget without replacing this standard.

    • Scope: Curator pages.
    • Rationale: Users/Influences already use inline message / error.
    • Verification: One visual treatment per page, consistent placement under the header.
  • Statement: Visitor-facing destructive actions (painting Remove entry in debug) MUST confirm and then show failure inline if the API rejects.

    • Scope: Debug painting tools.
    • Rationale: Catalog deletes are irreversible.
    • Verification: Cancel confirm → no delete.
  • Statement: Copy MUST be concise and, for errors, actionable (“Is the server running?”, “Sign in as a curator…”).

    • Scope: All user-visible strings.
    • Rationale: Support load.
    • Verification: Home catalog failure string remains understandable.

4.3 Confirmation and cancellation

  • Statement: Destructive curator actions (delete influence, delete tour, remove painting, deactivate user if offered) MUST confirm before the request. window.confirm with an i18n string is the current standard (confirmDelete); a shared modal MAY replace it later but MUST stay one pattern.

    • Scope: Influences, Tours, painting remove, Users.
    • Rationale: Accidental clicks on dense tables.
    • Verification: Delete tour → confirm; Cancel → no API call.
  • Statement: Cancel on a confirm MUST leave filters, selection, and unsaved fields unchanged.

    • Scope: All confirms.
    • Rationale: Context preservation.
    • Verification: Filtered Checkup, cancel a destructive action → filter still applied.

5. Component Consistency Rules

5.1 Shared interaction pattern usage

  • Statement: New UI MUST reuse existing components before creating parallels: GalleryLoadingMarker, CatalogSearchBar, LocaleSwitcher, CuratorLoginModal, ArtistFilterModal, PaintingLightbox, DebugSearchResultsModal, DebugUploadButton.

    • Scope: client/src.
    • Rationale: Duplicate spinners and dialogs already caused drift.
    • Verification: PR does not add a second login modal or loading overlay.
  • Statement: Movement colours MUST go through utils/movementColor.ts (vividMovementColor, shadeMovementColor). Hex parsing MUST tolerate #rgb / #rrggbb and keep the first six digits of longer values.

    • Scope: Timeline charts and any movement swatch.
    • Rationale: Classic, vertical, and tree already share this util.
    • Verification: No local parseHexColor copies in components.
  • Statement: Timeline zoom/pan MUST use utils/timelineView.ts. Tree horizontal layout MUST use utils/movementTree.ts (view-independent structure).

    • Scope: Timeline surfaces.
    • Rationale: Layouts share one year window.
    • Verification: No one-off wheel handlers that bypass the util.

5.2 Component and style selection

  • Statement: There is no Ant Design / MUI. UI MUST be React + colocated CSS (ComponentName.tsx + ComponentName.css). Global museum tokens SHOULD reuse existing values:

    Token Typical value Use
    Gold #c9a96e / rgba(201, 169, 110, …) Accents, borders, links
    Ink / navy #0f0f1a, #1a1a2e, #16213e Page background
    Type Georgia, serif Titles, captions, layout links
    Viewport 100vh, overflow: hidden on home No document scroll for the museum shell
    • Scope: All client UI.
    • Rationale: The look is the design system.
    • Verification: New CSS does not introduce a bright Bootstrap theme.
  • Statement: Prefer semantic <button type="button"> for actions and native <form> for submits. Do not use <div onClick> for primary actions.

    • Scope: All interactive chrome.
    • Rationale: Keyboard and a11y.
    • Verification: Header tools are buttons.
  • Statement: Colocate styles; do not add a CSS-in-JS runtime. Shared layout classes live in HomePage.css / page CSS, not inline theme objects.

    • Scope: Client.
    • Rationale: Matches the repo.
    • Verification: New component ships a .css file or uses an existing one.

5.3 i18n

  • Statement: New user-visible chrome MUST add keys to client/src/locales/en/*.json and ru/*.json (namespace that matches the screen: home, common, debug, users, …). Hardcoded English is allowed only for curator-only screens that are not yet migrated, and those SHOULD be migrated when the screen is touched.

    • Scope: All new copy.
    • Rationale: i18n-russian.md.
    • Verification: Locale toggle changes the new string.
  • Statement: Catalog entities (names, titles, bios) MUST use API locale resolution, not a second client-side dictionary.

    • Scope: Timeline labels, search, halls, detail.
    • Rationale: entity_translations is canonical for RU catalog text.
    • Verification: RU locale shows published translations.

5.4 Exception process

  • Statement: Deviations (new overlay type, toast system, left nav, router, design library) MUST be documented in this file or in the features Documentation/*.md with rationale before they spread to a second screen.

    • Scope: Client-wide patterns.
    • Rationale: One exception is an experiment; two without a write-up is fragmentation.
    • Verification: PR description links the exception note.
  • Statement: Known current exceptions (do not cargo-cult; fix when touching the file):

    • Browser history is not a router.
    • Only ?layout= is deep-linked.
    • Some curator pages (e.g. Checkup) still have English chrome.
    • Some modals may still omit Escape (login); new modals MUST include it.
    • Destructive confirms use window.confirm rather than a shared dialog component.

5.5 Versioning of behaviour

  • Statement: Behaviour changes to shared patterns (Back, search debounce, layout query, loading marker) MUST update this document in the same change.
    • Scope: Shared components and HomePage navigation.
    • Rationale: Agents and humans use this as the spec.
    • Verification: Doc diff accompanies the code diff.

6. Screen-Level Checklist

Use this before submitting a new view, overlay, or curator tool. Unchecked items need a note.

  • Surface type is identified (timeline / hall / detail / overlay / curator page).
  • Parent view and Back label/destination are defined (returnTo or goToTimelineHome).
  • Permissions: controls hidden unless can(…) / signed in as required.
  • Loading uses GalleryLoadingMarker or a justified scoped indicator.
  • Empty and error states are visible copy, not a blank canvas.
  • Destructive actions confirm; cancel leaves state unchanged.
  • Visitor chrome strings are in locales/{en,ru}; catalog text uses API locale.
  • No new CSS framework; gold/navy/Georgia; colocated CSS.
  • Search/filter (if any) sits above the list; expensive work is explicit-button.
  • Modals: role="dialog", close control, Escape.
  • Timeline work uses timelineView / movementColor / movementTree as applicable.
  • Hall work preserves camera on Back to Gallery and resets on Back to Timeline.
  • This document updated if a shared pattern changed.