website: M3 part A - the controller's shell, and the home page inside it
gates / gates (push) Successful in 6m58s
gates / gates (push) Successful in 6m58s
The direction the operator chose: the controller's FRAME around the website's own
content. Staged at /preview/m3/, noindex, nothing live.
website/assets/shell.css - 117 rules COPIED VERBATIM from felhom-controller
style.css @ dcd89364a4 (sidebar, mobile top bar, off-canvas drawer, and the
component atoms the restyled pages use: btn, alert, app-info-card, launch-*,
stack-detail-card, form-*). Exactly ONE rule is not verbatim and says so:
body.shell { display:flex; min-height:100vh }, which lives in the controller's
base rather than in a component, scoped to .shell so other pages are untouched.
The generator NAMES every rule it copies and REFUSES to write the file if one has
disappeared upstream. That is not ceremony: it already caught two things this
session. It refused because .sidebar::-webkit-scrollbar does not exist (only the
-track override does), and because prefers-reduced-motion is split across FIVE
separate blocks in the controller - reading only the first would have silently
lost four rules. The M1 round lost a rule exactly this way.
The bare `body { flex-direction: column }` that broke M1's phone layout is copied
this time, by name, as are body.nav-open and the no-js/js pair.
Load order is deliberate: shell.css AFTER site.css. Thirteen selectors exist in
both files with different meanings (.nav-links, .page-header, .lang-globe*, .ico)
- a horizontal site nav there, a vertical sidebar here. Later wins, and a page
that does not load shell.css is completely unaffected.
website/preview/m3/index.html - the home page in the shell. Content is /preview/'s
cleaned text; body carries `shell page-index` so the existing .page-index content
rules (caps, flow, vs, band, who-mini) are reused rather than duplicated. Six
sections, left-aligned headers, the controller's cards. Head tags (title, meta
description, canonical, hreflang) are already the FINAL values, so the switch in
part D needs no head edit.
Four changes from the operator's mockup, each with its reason in the generator:
1. the active item follows the page (the mockups show Inditopult active on all)
2. Teszteles is ALSO a primary button at the top of the lower group - it is the
one action we want and the mockup buries it at the bottom
3. the Alkalmazasok categories are anchors on /alkalmazasok: real URLs, no
JS-only state
4. ADDED GYIK. It is in today's nav; dropping it silently would take a page out
of navigation, which is not a change a restyle gets to make.
scripts/site_gates.py - gate 3 extended rather than duplicated, as asked: a SHELL
set whose pages must carry an IDENTICAL <nav class="sidebar"> after the active
marker is normalised. RED-PROOFED: a one-character change to one menu label on a
second shell page convicts. It also prints "DIFFER" rather than a reassuring
"identical" count when it fails - a false reassurance printed beside a conviction
is the exact defect this repo files rows about.
documentation/design/design-system.md - new "Shell" section: what is shared (tokens
+ shell + atoms), what each side owns, how a change flows (controller first, the
website's copy the same week), and that the cross-repo gate is deliberately NOT
built yet.
This commit is contained in:
@@ -160,6 +160,59 @@ scale addition: hero H1 up to ~3.5rem/800. Assets are cache-busted with `?v=N` (
|
||||
nav/footer consistency, analytics presence, no-CDN, banned tokens, zero `<style>` blocks,
|
||||
cache-busting).
|
||||
|
||||
## Shell (M3, 2026-10-11)
|
||||
|
||||
**The website and the controller now share two things: the tokens, and the shell.** Everything else
|
||||
each owns.
|
||||
|
||||
| | Shared | Website owns | Controller owns |
|
||||
|---|---|---|---|
|
||||
| Tokens | **yes** — 18 names, verified byte-identical in both `:root` blocks | — | — |
|
||||
| Shell: sidebar, mobile top bar, off-canvas drawer | **yes** — `website/assets/shell.css` is a COPY | the menu's items, the wordmark under the logo, the sidebar's tester button, the language globe's targets | the menu's items, the language globe's POST form |
|
||||
| Component atoms: `.btn*`, `.alert*`, `.app-info-card`, `.launch-*`, `.stack-detail-card`, `.form-*` | **yes** — same copy | — | — |
|
||||
| Page content and its layout | no | all of it (`site.css`) | all of it (`style.css`) |
|
||||
|
||||
**`website/assets/shell.css` is a copy, not a library.** It is generated from
|
||||
`felhom-controller/controller/internal/web/templates/style.css` and carries that commit in its
|
||||
header. Exactly one rule in it is not verbatim: `body.shell { display: flex; min-height: 100vh }`,
|
||||
restated because it lives in the controller's base rather than in a component, and scoped to
|
||||
`.shell` so a page without the shell is untouched.
|
||||
|
||||
**Load order matters and is deliberate.** `shell.css` loads AFTER `site.css`. Thirteen selectors
|
||||
exist in both with different meanings — `.nav-links`, `.nav-links a`, `.page-header`, `.lang-globe*`,
|
||||
`.ico`, `.ico-sm` — a horizontal site nav in one, a vertical sidebar in the other. Later wins, so a
|
||||
shell page gets the shell's meaning and a page that does not load `shell.css` is unaffected.
|
||||
|
||||
### How a change flows
|
||||
|
||||
1. **The controller changes first.** It is the surface a household operates daily; the website
|
||||
follows it, never the other way round.
|
||||
2. **The website's copy is re-generated in the same week**, by re-running the generator against the
|
||||
controller's new commit and updating the header. A copy that silently falls behind is the failure
|
||||
this section exists to prevent.
|
||||
3. **A later gate may enforce step 2. One is NOT built today** (operator's brief, 2026-10-11) — the
|
||||
cross-repo check waits until the shell has settled.
|
||||
|
||||
### The trap that makes this a copy with a generator, rather than a copy by hand
|
||||
|
||||
Copying components by class name **silently drops the element-level rules those components depend
|
||||
on.** Measured on 2026-10-11: copying the whole `@media(max-width: 768px)` block *except* its bare
|
||||
`body { flex-direction: column }` left the sticky top bar as a row flex item, pushed the content
|
||||
135 px right, collapsed the launcher grid to one column, made a page 15 597 px tall on a phone and
|
||||
overflowed two others. Everything looked copied. Full account:
|
||||
`documentation/audits/mockups-2026-10-11/SHARED-DESIGN.md` §2.
|
||||
|
||||
So the generator **names every rule it copies and refuses to write the file if one has disappeared
|
||||
upstream** — an incomplete copy fails loudly instead of rendering wrong.
|
||||
|
||||
### Enforcement for the shell
|
||||
|
||||
`scripts/site_gates.py` gate 3b: every page in the `SHELL` set must carry a
|
||||
`<nav class="sidebar">` block **identical** to the others' after the active marker is normalised
|
||||
away. The site is static with no build step, so the shell is duplicated into each page; this is what
|
||||
keeps the duplication honest. Red-proofed 2026-10-11: a one-character difference in one menu label
|
||||
convicts.
|
||||
|
||||
## Enforcement
|
||||
|
||||
The D0 grep gate (all counts must be 0 in `controller/internal/{web,setup}`):
|
||||
|
||||
Reference in New Issue
Block a user