Guteneo combines the character of a printing workshop with an operational interface: warm paper, precise ink, cobalt illustrations and readable controls. A new frontend should feel like another room in that workshop. Start with shared components and real product states, then add a small amount of illustration.
This guide is the implementation companion to docs/BRAND_IDENTITY.md,
docs/MULTILINGUAL.md and docs/REPOSITORY_STANDARDS.md. Those files own their
respective rules. Change them and this guide together when the rules evolve.
For service boundaries, use the system architecture.
Choose the right surface
| Surface | Reuse | Preserve |
|---|---|---|
| Public website | public-chrome.tsx, public-chrome.css, legal-links.tsx, brand.tsx |
One header/footer, English canonical routes, visitor language selection |
| Authenticated web app | Existing application shell and components.tsx |
Account language, workspace authority, unsaved work and the current route |
| Belvédère | Existing private shell and docs/belvedere/DESIGN.md |
Its denser operational layout, read-only data and separate platform access |
| Engineering site | Nimbus layouts, shared MDX components and its theme | English technical prose, light/dark themes, one layout-owned page heading |
| Native app | Existing SwiftUI components and bundled string catalog | Platform navigation, dynamic text, native accessibility and account language |
Do not copy an entire shell into a new page. Public shell changes belong in the shared source; another independent header quickly drifts in wording, language controls and small-screen behavior. A component migration must preserve every meaningful state, including loading, empty, denied, unavailable and recoverable failure states.
Use the established visual vocabulary
The web source tokens live in apps/web/src/styles.css. Use token references in
components rather than another palette of almost-identical values.
| Role | Source token / value | Application |
|---|---|---|
| Paper | --paper, #f6f5ef |
Main reading surface |
| Surface | --surface, #fffefa |
Fields and restrained panels |
| Ink | --ink, #181b22 |
Body, headings and ordinary wordmark |
| Cobalt | --blue, #2450db |
Actions, focus and approved blue artwork |
| Display | --font-display, EB Garamond |
Editorial headings and wordmark |
| Body | --font-body, IBM Plex Sans |
Prose, controls and operational data |
| Code | --font-code, IBM Plex Mono |
Code and technical identifiers |
| Shape | --radius, 3px |
Ordinary controls and restrained corners |
| Reading measure | --content-measure, 68ch |
Long-form text |
| Control height | --control-height, 44px |
Comfortable interactive targets |
Use thin separators and spacing to establish hierarchy. Reserve depth for an actual overlay or an explanatory spatial figure. Keep numeric comparisons aligned with tabular figures. Status colors always accompany a written state; missing data must remain unknown rather than appearing as zero or success.
Belvédère has intentional local density and token choices documented in its own design contract. Engineering maps the brand to Nimbus theme variables. Extending either surface does not require replacing its complete theme with raw web tokens.
Compose a page around one task
Start with one page title, a short introduction only where it adds context, and
the primary action appropriate to the current actor. The shared PageHeading
accepts title, optional intro and optional action. Reuse EmptyState,
ErrorNotice and the established forms instead of inventing a parallel vocabulary.
Keep the page’s identity out of repeated cards and banners. Move common legal or support links into their shared owner. Retain exact cost, recipient, document version, options and recovery information at the decision where a person needs them. Reducing repetition never removes approval information.
On an authenticated page, client visibility is a convenience. The server still determines tenant membership, permissions and current authority. An attractive disabled button is not an authorization check. Read the roles and authority guide before designing an action.
Use portraits, stamps and illustrations deliberately
The transparent Gutenberg portrait, the paper stamp and the simple emblem have different jobs. Preserve the portrait asset’s transparency; the lowercase wordmark is ink black in light mode. The approved engineering-header exception uses ivory lettering directly on the dark header. Its stamp may sit on a small white paper backing confined to the stamp, with no light panel behind the wordmark. Decorative stamps likewise retain white paper in both themes: do not invert their portraits or make their printed paper dark.
The communication stamp already carries a domain signature. Do not add a second wordmark or domain to the same promotional composition. Keep the official source assets intact and record generated derivatives separately with their prompt, reference images, provenance and optimized exports.
Gutenberg can introduce a useful note or demonstrate a workflow. Engineering
illustrations use the established animated-film character and its printing
workshop vocabulary. Keep the small character in editorial notes; do not replace
it with miniature postage stamps or add city panoramas. Selected section openings
can use a playful scene with the same character: diagnosing a PDF, drawing a plan
or organizing features. GutenbergScene.astro owns their three routes, responsive
images and short captions. Keep the page title and all technical information as
HTML outside the picture. Decorative scenes and notes opt out of Nimbus image
zoom with data-nb-zoom="off"; they must not become extra keyboard stops.
A short printer’s rule, restrained registration marks and a cobalt navigation marker add character without changing the reading hierarchy. Keep imagery sparse and leave text unobstructed. Where birds belong in an approved composition, reuse the existing blue swallow sheet. Decorative artwork uses an empty alternative and is hidden from assistive technology. Explanatory diagrams need an equivalent readable explanation.
Reserve image dimensions, use appropriately sized WebP exports and lazy-load below-the-fold decoration. Keep labels and instructions as real HTML. A generated picture must not become the only source of a technical fact or an instruction.
Internationalization is part of the component
The product supports French, English, German and Luxembourgish (fr, en, de,
lb). Repository code, comments, diagrams, API contracts and engineering prose
are authored in English. English source does not change the product’s locale
resolution or its French fallback for unsupported visitor languages.
Before login, explicit public ?lang= wins over this browser’s saved selection,
then supported browser languages are considered. After login, the account’s
preferredLocale applies; an account without it uses browser language, without
inheriting a visitor’s or another account’s preference. Saving a public choice
does not save an account preference. Preserve those boundaries when adding a
frontend or changing its shell.
For web copy, call msg("English source", ...values) during render or reuse a
common t entry. Register complete translations in the established locale
catalogs. Their existing storage format uses a French lookup with an
English/German/Luxembourgish array; message-catalog.ts derives the English
source lookup. That storage format is localization data, not permission to add
French prose to component source.
Use msgIn("stable-context", "English source", ...values) when the same English
phrase has distinct grammatical meanings, and register its explicit context in
locales/message-contexts.json. Preserve interpolation parameters in every
language. Build label-bearing arrays during rendering or through getters so
switching languages updates existing screens without a reload.
| Translate | Preserve exactly |
|---|---|
| Labels, descriptions, validation and recovery text | User content, names, addresses and document bytes |
| Accessible names and state descriptions | Standard ARIA attribute values and machine states |
| Dates, numbers and displayed amounts through locale formatters | Integer monetary values and quote precision |
| Video narration, captions, posters and on-screen copy | Stable video identities and provenance |
| Navigation labels | English paths, fragments, API keys, scopes and error codes |
Never pass arbitrary server values or user documents through msg. Preserve
canonical routes with the shared helpers and retain public language parameters.
Native strings live in ios/Guteneo/Resources/Localizable.xcstrings; new Swift
lookup keys are English. Belvédère follows the same account preference. Films
must select the matching localized asset and stop old playback when the language
changes; use the video production guide.
Build accessible interactions, then inspect them
Use semantic headings, landmarks, buttons, links and form labels. Every action must work from a keyboard, expose its purpose and show a visible focus indicator. Keep focus visible beneath sticky headers. A disclosure reports its expanded state; Escape closes an overlay and returns focus to its trigger. Preserve the application’s existing route-focus and unsaved-work behavior.
The shared public mobile menu handles Escape at document level while open.
WebKit can leave focus on the document body after pointer activation, so a
header-only key listener misses that input. Both pointer and keyboard opening
must close with Escape and restore the menu button’s focus. Remove the listener
when the menu closes, and respect an Escape already consumed by another control.
The focused regression in tests/preview-e2e/public-menu-escape.spec.ts covers
the homepage and developer guide at 320px and 390px in Chromium and WebKit.
Labels remain visible when fields contain values. Put errors beside the relevant field and announce important asynchronous outcomes without repeatedly taking focus. Keep navigation distinct from actions, and provide an alternative to drag-only interactions. Never make color, a canvas, motion or a hover tooltip the only route to information.
The WCAG 2.2 quick reference is the review baseline. Check contrast, text alternatives, keyboard access, focus, reflow, labels, status messages and accessible authentication. A passing automated suite is not a declaration of complete WCAG conformance.
For Guteneo’s review, inspect 320px and phone widths, desktop, zoomed text,
all four interface languages and every supported theme. Keep large tables or
diagrams scrollable inside their region while surrounding controls reflow.
Respect prefers-reduced-motion; decorative birds and diagrams must remain
understandable when animation stops. Check captions and a text alternative for
new instructional media rather than assuming that narration is accessible to all.
A practical recipe for the next frontend
- Find its actors, entry conditions and failure states in the feature atlas. Choose the existing surface and shared shell before designing a new one.
- Implement the smallest task flow with shared headings, controls and routes. Use real contract states and synthetic fixtures that are clearly examples.
- Add English source copy and all supported translations together. Test a language change with an unfinished form and account switching where relevant.
- Apply tokens and approved imagery. Verify light/dark contrast, keyboard order, mobile reflow and reduced motion in the rendered page.
- Update the feature tree, its developer contract, and the affected engineering guide. Follow testing and evidence and CI and release for publication.
Useful focused checks include tests/unit/web-locale.test.ts,
tests/e2e/locale.spec.ts, tests/e2e/locale-boundaries.spec.ts and the preview
or Belvédère suite for the actual surface. Inspect the new interaction manually
with keyboard and assistive technology as well; catalog parity alone cannot
prove good wording, reading order or usable error recovery.