Fable review: drop a fix that fixed nothing, and correct PRODUCT.md
Whole-branch review by claude-fable-5, per AGENTS.md's highest-available- capability rule. Returned DO NOT MERGE on two blocking findings. Both verified independently before acting on them; both were right. B1 — the "whole row clickable" fix was a no-op, and its measurement was a false record. rastrillo v0.22.0's own tokens.css, which this app serves live through ui.TokensCSS(), already ships every part of it: [rst-row] carries position: relative (tokens.css:500), [rst-row]:hover carries the --rst-surface-2 tint (:515), and [rst-row-main] > a::after carries content:"" with inset: 0 (:558) under rastrillo's own comment, "The name link's overlay makes the whole row clickable without nesting one anchor inside another." Our markup already matched that selector. The row was already fully clickable, already hover-tinted, and already past 24×24 without leaning on SC 2.5.8's Spacing exception. The "7% clickable" measurement came from getBoundingClientRect on the anchor, which reports an element's own box and cannot see an absolutely positioned pseudo-element. The later hit test that "confirmed" the fix was hitting rastrillo's overlay — it would have passed identically before the change, which is the check that was never run. Exactly the failure shape AGENTS.md names: a verification that cannot distinguish "no" from "I can't see it", this time producing a fix for a defect that did not exist and nearly writing it into the permanent record. Removed: [docs-row], [docs-row-link]::after, the @media (hover: hover) tint (which could not have worked anyway — rastrillo's own :hover is un-gated and still applies on touch), the docs-row/docs-row-link attributes, and TestListRowsAreClickableAcrossTheWholeRow. That test was tautological besides: it asserted the page contained the string "[docs-row-link]::after", which also appeared in the CSS comment served alongside it, so deleting the rule left it green. Kept, in list.html, is a comment recording that the row is already stretched by rastrillo and that measuring the anchor's box will mislead you — the next reader will otherwise measure 268×16 and reach for the same non-fix. Verified after removal: all six probe points across the 990×61 row (thumbnail, title, empty middle, date, both far corners) open the document, and the hover tint still resolves to --rst-surface-2. B2 — PRODUCT.md asserted, in the present tense, a security property this app does not have: that content is encrypted in SQLite under a DEK and that a plaintext byte-grep test enforces it. Neither is true today. Store.Text's own doc comment says sealing is out of scope for the plan that built it, and no such test exists in the repo. Rewritten as the spec commitment it is, with an explicit instruction not to describe an instance as encrypting content at rest. Product Principle 2 corrected the same way. Also from the review: - S2: TestIconIsServedAndLinkedConsistently checked views/head.html's bytes. If newRenderer stopped parsing that template into the base, every page would silently lose its icon while the test stayed green — the file would still contain the href and the icon would still serve. Now asserts the rendered sign-in page carries the link, which subsumes the file check. - S3: "passkeys are ruled out" overstated §8.1, which rules out instance-local passkeys; §8.1.1's suite path keeps them at the home's single relying party. Narrowed. - Nits: the empty-state title was quoted with a full stop the catalog does not have; the eleven non-English catalogs were described as machine-drafted when zero exist and Locales is ["en"]. Both corrected to say what is true now. - Noted in list.html: nowrap plus a non-shrinking button means a locale whose create label exceeds ~270px will overflow the 320px column. English is 131px and is the only catalog today, so this is a marker for whoever lands the other eleven, not a live bug. The four remaining fixes on this branch are unchanged and were re-verified after these edits: the back link, the sign-in measure, the mobile header at 133px, and the favicon. Gate exits 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsrkL4m68poXhtf1tGaoek
PRODUCT.md+30 −13internal/docs/assets_test.go+20 −6internal/docs/list_test.go+0 −48internal/docs/views/list.html+31 −43
CI passed — run details
Intent
do we need to do anything?
<task-notification> <task-id>aeffd0d50354caa23</task-id> <tool-use-id>toolu_01PMiZJcrymtXuqB9HUvq9SK</tool-use-id> <output-file>/home/paulca/.cache/tmp/claude-1001/-home-paulca-amadan-net-oficina-docs/48ea084f-0296-4b0a-9104-dba9bbd5928c/tasks/aeffd0d50354caa23.output</output-file> <status>completed</status> <summary>Agent "Fable whole-branch review" finished</summary> <note>A task-notification fires each time this agent stops with no live background children of its own. The user can send it another message and resume it, so the same task-id may notify more than once.</note> <result># Whole-branch review: `worktree-impeccable-init` (fd1d21a..ffebf47) Read as one diff per AGENTS.md. Verified by running things: `go build ./...` clean, `go test ./internal/... ./cmd/...` all pass, `gofmt -l internal/docs/` clean, and I ran the app via `./scripts/run-dev.sh` and curled rendered pages. I did not run `.amadan/ci.d/10-gate` (this branch touches no Rust/wasm/bundle; Go build+tests green; the amadan CI status check remains the merge gate regardless). ## BLOCKING **B1. The "whole row clickable" fix is a functional no-op, and its measurement narrative — written into comments, a test, and the commit message — is false.** rastrillo v0.22.0's `tokens.css`, which this app serves live via `ui.TokensCSS()` (`internal/docs/render.go:271`; confirmed byte-for-byte in the running app's `/static/tokens.<hash>.css`), already ships all three mechanisms this fix claims to add: - `[rst-row] { position: relative; }` — tokens.css:500–507 - `[rst-row]:hover { background: var(--rst-surface-2); }` — tokens.css:515, **un-gated** - `[rst-row-main] > a::after { content:""; inset:0; position:absolute; }` — tokens.css:558, under the framework's own comment *"The name link's overlay makes the whole row clickable without nesting one anchor inside another."* The pre-branch markup (`<span rst-row-main><a href=…>`) already matched that selector, and the row was already the anchor's containing block. So before this branch, the whole row was already clickable, already hover-tinted, and already cleared SC 2.5.8 without leaning on the Spacing exception. The branch's three rules (`internal/docs/views/list.html:88–104`) restate identical property:value pairs on the same elements — `[docs-row-link]::after` and `[rst-row-main] > a::after` are literally the *same* pseudo-element. The `@media (hover: hover)` gate achieves nothing: the framework's un-gated rule still applies on touch, so the sticky-tap tint the comment claims to prevent still happens. The comment's claim — "a 990×61 row whose only target was a 268×16 link — 7% of it … the thumbnail … did nothing at all" — is what you get from measuring the anchor's own bounding box, which cannot see a pseudo-element's hit area. That is AGENTS.md's named failure shape ("a verification that cannot distinguish 'no' from 'I can't see it'"), here about to be merged as a permanent false record, in a repo whose spec preamble says a document that quietly gets its facts wrong "teaches the next reader nothing." The commit message claims five defects; this one was not a defect. Fix before merge: delete the three CSS rules, the `docs-row`/`docs-row-link` attributes, `TestListRowsAreClickableAcrossTheWholeRow`, and the false comments (or rewrite them to record that rastrillo's row already stretches the link — that's worth a note). If a hover-capability gate is genuinely wanted, it must *override* the framework's rule, not duplicate it. **B2. PRODUCT.md asserts a security property, in the present tense, that does not exist.** Positioning item 3 (`PRODUCT.md:66–71`): "Content **is** encrypted in SQLite under a DEK held by the running instance; a test greps raw SQLite bytes and bucket objects for fixture plaintext and fails if it finds any (§2)." And Product Principles item 2 calls the byte-grep seal test "a shipped habit, not an aspiration" ("the seal is a byte-grep test"). Both are false today: `internal/docs/store.go:386–389` says outright "Sealing (spec §2 …) is out of scope for this plan; this plaintext replay is not a decision that content stays unsealed," and no plaintext-grep test exists anywhere in the repo (I searched). The file's own preamble promises anything that isn't citable fact is marked `[inferred — confirm]` — a false "content is encrypted at rest" claim in the product record is exactly what someone deploying an instance will trust. Rewrite both passages as the spec commitment they are (sealed at rest is designed and ruled, not shipped), or mark them. ## SHOULD FIX **S1 (before or after merge). `TestListRowsAreClickableAcrossTheWholeRow`'s third assertion is tautological** — independent of B1. It asserts `strings.Contains(body, "[docs-row-link]::after")`, and that exact string also appears in the CSS *comment* at `views/list.html:67`, which is served to the client. Delete the rule and the test still passes. Moot if B1 removes the test entirely; noted so the pattern isn't replicated. **S2 (after merge is fine). `TestIconIsServedAndLinkedConsistently` checks the template file's bytes, not rendered output** (`assets_test.go:170–177`). If `newRenderer` ever stops parsing `views/head.html` into the base (the exact wiring `render.go:216` adds), the favicon link silently vanishes from every page while both halves of this test stay green — the file still contains the href, the icon still serves. Assert the rendered sign-in page (no session needed) contains `href="/static/icon.svg"` instead; that subsumes the file check. I verified the current wiring works end-to-end — the rendered `<head>` does carry the link on all pages — so this is a test-strength gap, not a live bug. **S3 (before merge, one line). PRODUCT.md: "Passkeys are ruled out on purpose, not deferred."** Overstated: §8.1/§8.1.1 rule out *instance-local* passkeys; the suite path deliberately plans passkeys at the home's single relying party. Say "instance-local passkeys are ruled out." ## NIT - `[docs-create-form]`'s `flex-wrap: nowrap` with `flex: none` on the button means a locale whose submit label alone exceeds ~270px would overflow at 320px. Only `en` is declared today; worth remembering when the eleven catalogs land. - `TestDocumentPageLinksBackToTheList` asserts the exact serialization `<a docs-back href="/">` and `<div rst-row docs-row>` — attribute-order brittle, though it's the app's own markup two files away, consistent with house style. - PRODUCT.md quotes the empty-state title as "No documents yet." — the catalog value (`render.go:43`) has no period. Trivial. Also "the eleven non-English catalogs are machine-drafted" is spec-future stated as present (zero exist; `Locales = ["en"]`). ## GOOD (verified, not padded) - **The `head.html` mechanism is correct and verified end-to-end.** Parse order (shell → head → clones) does override the shell's `{{block "head"}}` — the rendered `<head>` carries the icon link on the live server; a page can still define its own `head` in its clone; `newRenderer`'s error handling is right; excluding it from `pageTemplates` is right (it defines no `content`). - **The `brand/` split is exactly right.** I read `.amadan/ci.d/10-gate:60–71`: the dirt check, `git checkout` and `git clean -fd` are all scoped to `internal/docs/static/`, so `brand/` cannot suffer the recorded failure; `build-editor.mjs` writes only into `static/`; `//go:embed brand` is correct; the new only-the-bundle assertion in `TestExactlyOneEditorBundleIsEmbedded` matches the gate's real mechanics. Serving the icon unauthenticated ahead of `RequireSession` is correct (the sign-in page needs it) and the day-cache-no-hash tradeoff is sound. `#2464e0` really is day's light `--rst-accent`. - **`[docs-back]` is a real fix done well**: the page genuinely had no in-app navigation; logical properties throughout; no glyph to mirror in RTL; the 24px floor stated outright with the token-drift lesson (`--rst-fs-sm` is 12.5px) recorded; no interference with the `[docs-editor]` mount or `[docs-save-status]` (editor-entry.js selects only those two attributes). The shared `docs.list.title` key is defensible — a breadcrumb naming its destination screen conventionally carries that screen's title — and the cross-page render-comparison test is genuinely clever: it holds the invariant in every locale and cleanly distinguishes "absent" from "present but different." - **`TestIconIsWellFormedXML`** decoding every token, with the double-hyphen-in-XML-comment lesson written down, is the house style at its best. - The sign-in measure and the create-form flex reasoning (`min-inline-size: 0`, wrap-breaks-before-shrinking) are correct CSS, correctly explained. ## Verdict **DO NOT MERGE** — one of the five claimed defects was never a defect: the whole-row-click fix duplicates rastrillo's own already-served stretched-link CSS while writing a false measurement into the permanent record (plus PRODUCT.md's false "content is encrypted at rest" claim); both fixes are cheap, and the other four fixes are sound and can merge once B1/B2 land.</result> <usage><subagent_tokens>133828</subagent_tokens><tool_uses>57</tool_uses><duration_ms>658266</duration_ms></usage> </task-notification>
Attributed to this commit: 101 g CO₂e (12719050 tokens). This is an attribution, not a measurement — usage counted from this commit's own marker forward to the next one on worktree-impeccable-init, never backward from it. A turn that produced more than one commit books all of its tokens to the last one, and a concurrent session on this branch can add to the same figure. Some of it was priced using the shared default rate, because no model-specific factor row exists yet for at least one model involved. The Carbon view has the repo total, which is never derived from this attribution.
diff --git a/PRODUCT.md b/PRODUCT.md| index 18b577a..f3ac319 100644 |
| --- a/PRODUCT.md |
| +++ b/PRODUCT.md |
| @@ -28,10 +28,12 @@ anonymous URL holder (spec §1, decision 1; §8.2). A guest is a real row with a |
| real identity, which is what lets author colours key off it (§10.2). |
| Access today is a magic link, gated by `$DOCS_ALLOWED_ADDRESSES` |
| -(`internal/docs/auth.go`) — a per-instance allowlist. Passkeys are ruled out |
| -on purpose, not deferred: `passkey.Config.Origin` derives its relying party |
| -from the instance's own host, so a person in several teams would enrol several |
| -credentials for one product (spec §8.1). |
| +(`internal/docs/auth.go`) — a per-instance allowlist. **Instance-local** |
| +passkeys are ruled out on purpose, not deferred: `passkey.Config.Origin` |
| +derives its relying party from the instance's own host, so a person in several |
| +teams would enrol several credentials for one product (spec §8.1). That is a |
| +ruling about *this* app enrolling them, not about passkeys as such — §8.1.1's |
| +suite path deliberately keeps them, at the home's single relying party. |
| **[inferred — confirm]** The team is assumed to be small enough that an |
| allowlist of addresses is an appropriate admission mechanism. Nothing in the |
| @@ -64,10 +66,18 @@ its foundations: |
| it** (spec §0, decision 5). Page breaks are ProseMirror decorations, never |
| nodes (decision 14), so page geometry never enters the CRDT and two |
| collaborators on different page setups never conflict. |
| -3. **Sealed at rest, with a stated leak list.** Content is encrypted in SQLite |
| - under a DEK held by the running instance; a test greps raw SQLite bytes and |
| - bucket objects for fixture plaintext and fails if it finds any (§2). The |
| - six things that *do* leak are enumerated rather than elided (§2.1). |
| +3. **Sealed at rest, with a stated leak list — designed and ruled, not yet |
| + shipped.** The spec's §2 commits to content encrypted in SQLite under a DEK |
| + held by the running instance, enforced by a test that greps raw SQLite bytes |
| + and bucket objects for fixture plaintext, and it enumerates the six things |
| + that *do* leak (§2.1) rather than eliding them. |
| + **None of that is implemented today.** `Store.Text` |
| + (`internal/docs/store.go`) says so in its own doc comment — sealing is out |
| + of scope for the plan that built it — and no plaintext-grep test exists in |
| + the repo. Do not describe this instance as encrypting content at rest, and |
| + do not let this entry be read as a shipped property; it is the position the |
| + product is being built toward, and the thing that makes the position |
| + defensible is the leak list, which is honest about what the trade costs. |
| ## Operating Context |
| @@ -149,7 +159,8 @@ full-text search (title and metadata search only — decision 15). |
| "Last opened by me" filters, grid/list/AZ/folder toggles, and all commercial |
| and platform chrome. |
| - **Voice, as evidenced by the shipped catalog:** plain, unhedged, second |
| - person, no exclamation marks, no product cheer. "No documents yet." |
| + person, no exclamation marks, no product cheer. "No documents yet" |
| + (`docs.list.empty.title`, no full stop — a heading, not a sentence). |
| "Documents you create will show up here, newest first." Error copy states |
| what happened in the past tense and what helps, and was corrected once |
| precisely because it described a recovery the person could no longer perform. |
| @@ -179,8 +190,11 @@ full-text search (title and metadata search only — decision 15). |
| divergence at the line level, the six-item leak list — the product says so |
| in the place a reader will look, and the spec refuses to claim zero. |
| 2. **A verification that cannot distinguish "no" from "I can't see it" is not |
| - a verification.** This is a shipped habit, not an aspiration: it is why |
| - `git branch --merged` is banned here and why the seal is a byte-grep test. |
| + a verification.** It is why `git branch --merged` is banned here, why the |
| + editor bundle is checked by rebuilding it rather than by trusting it, and |
| + why the seal, when it lands, is specified as a byte-grep over raw storage |
| + rather than as a code review. The principle is live; the seal is not built |
| + yet (see Positioning 3). |
| 3. **The empty state is a designed screen, not a blank page** (Paul's ruling, |
| in the catalog's own comments). |
| 4. **Chrome is quiet so the document is loud.** Everything around the page is |
| @@ -201,5 +215,8 @@ requirements are called out by name because they bind concrete decisions: |
| author colours directly: past the colour engine's separation capacity, the |
| name carries identity (§10.2). |
| -Twelve base locales are the target set; the eleven non-English catalogs are |
| -machine-drafted and the primitives each ship prose keys (§10.4). |
| +Twelve base locales are the target set. Today exactly one exists: `Locales = |
| +[]string{"en"}` (`internal/docs/render.go`). The spec's plan for the other |
| +eleven — machine-drafted catalogs, with each shared primitive shipping prose |
| +keys (§10.4) — is a commitment, not a shipped state, and future work should |
| +not read this section as though those catalogs are sitting in the repo. |
diff --git a/internal/docs/assets_test.go b/internal/docs/assets_test.go| index 72b95d7..55cfe0b 100644 |
| --- a/internal/docs/assets_test.go |
| +++ b/internal/docs/assets_test.go |
| @@ -103,12 +103,26 @@ func TestIconIsServedAndLinkedConsistently(t *testing.T) { |
| t.Errorf("body does not look like an SVG: %.80q", body) |
| } |
| - head, err := fs.ReadFile(viewsFS, headTemplate) |
| - if err != nil { |
| - t.Fatalf("read %s: %v", headTemplate, err) |
| - } |
| - if wantHref := `href="` + iconPath + `"`; !strings.Contains(string(head), wantHref) { |
| - t.Errorf("%s does not contain %s — the <link rel=icon> href and iconPath have drifted", headTemplate, wantHref) |
| + // And the link must reach a real page. Asserted on rendered output |
| + // rather than on views/head.html's bytes: the file containing the |
| + // href proves nothing on its own, because "head" only reaches a |
| + // page if newRenderer parses it into the base template ahead of the |
| + // per-page clones (render.go). Were that wiring dropped, a |
| + // bytes-of-the-file check would stay green while every page lost |
| + // its icon — the icon would still serve, the template would still |
| + // contain the href, and nothing would be able to tell. Rendering |
| + // the page subsumes the file check and closes that gap. |
| + // |
| + // The sign-in page, because it needs no session and so exercises |
| + // the same unauthenticated path the icon itself is served on. |
| + pageReq := httptest.NewRequest(http.MethodGet, "/signin", nil) |
| + pageRec := httptest.NewRecorder() |
| + app.Handler().ServeHTTP(pageRec, pageReq) |
| + if pageRec.Code != http.StatusOK { |
| + t.Fatalf("GET /signin = %d, want 200", pageRec.Code) |
| + } |
| + if wantHref := `href="` + iconPath + `"`; !strings.Contains(pageRec.Body.String(), wantHref) { |
| + t.Errorf("the rendered sign-in page does not contain %s — the shell's head block is not carrying the icon link", wantHref) |
| } |
| } |
diff --git a/internal/docs/list_test.go b/internal/docs/list_test.go| index eb254b0..91c7a7e 100644 |
| --- a/internal/docs/list_test.go |
| +++ b/internal/docs/list_test.go |
| @@ -277,51 +277,3 @@ func betweenTags(t *testing.T, body, open, close string) string { |
| } |
| return rest[:j] |
| } |
| - |
| -// Every row in the list must be openable across its whole area, not |
| -// only across the title's own text box. Measured before this landed: |
| -// a 990×61 row whose only target was a 268×16 link — 7% of it — with |
| -// the thumbnail, the part of a document row a person aims at most |
| -// readily, inert. The 16px height also cleared WCAG 2.2 AA's 24×24 |
| -// minimum (SC 2.5.8) only via that criterion's Spacing exception, |
| -// which spec §10.3 ("24×24 targets", "no exemptions") is a poor fit |
| -// for. |
| -// |
| -// This pins the markup contract the CSS overlay depends on — the row |
| -// carrying [docs-row] and its anchor carrying [docs-row-link] — since |
| -// the stretched-link rule is inert if either attribute is dropped, |
| -// and dropping one is silent: the page still renders, the link still |
| -// works, and only the target shrinks back to the title's text. |
| -func TestListRowsAreClickableAcrossTheWholeRow(t *testing.T) { |
| - app, _ := docs.NewForTest(t) |
| - ctx := context.Background() |
| - owner := app.TestPerson(t, "reader@example.com") |
| - if _, err := app.Store().Create(ctx, owner, "Board minutes"); err != nil { |
| - t.Fatal(err) |
| - } |
| - cookies := signInCookies(t, app, "reader@example.com") |
| - |
| - req := httptest.NewRequest(http.MethodGet, "/", nil) |
| - for _, c := range cookies { |
| - req.AddCookie(c) |
| - } |
| - rec := httptest.NewRecorder() |
| - app.Handler().ServeHTTP(rec, req) |
| - if rec.Code != http.StatusOK { |
| - t.Fatalf("status %d", rec.Code) |
| - } |
| - |
| - body := rec.Body.String() |
| - if !strings.Contains(body, "<div rst-row docs-row>") { |
| - t.Error("the row is missing [docs-row] — the stretched-link overlay has nothing positioned to cover") |
| - } |
| - if !strings.Contains(body, "<a docs-row-link ") { |
| - t.Error("the row's anchor is missing [docs-row-link] — the overlay rule will not match it") |
| - } |
| - // The overlay must be declared too: the attributes above are |
| - // inert markup without the rule that stretches the anchor over |
| - // the row. |
| - if !strings.Contains(body, "[docs-row-link]::after") { |
| - t.Error("no stretched-link rule on the page — the row attributes are present but nothing enlarges the target") |
| - } |
| -} |
diff --git a/internal/docs/views/list.html b/internal/docs/views/list.html| index abe4a05..8a1c504 100644 |
| --- a/internal/docs/views/list.html |
| +++ b/internal/docs/views/list.html |
| @@ -64,46 +64,27 @@ |
| color: var(--rst-text-muted); |
| font-size: var(--rst-fs-sm); |
| } |
| - /* [docs-row] + [docs-row-link]::after — the whole row opens the |
| - document, not just the title's own text box. |
| - Measured before this landed: a row is 990×61 and the title link |
| - inside it was 268×16, so 7% of what looks like one clickable |
| - object actually was one. The thumbnail — the part of a document |
| - row a person is most likely to aim at — did nothing at all, and |
| - the 16px-tall link cleared WCAG 2.2 AA's 24×24 minimum (SC 2.5.8) |
| - only by leaning on that success criterion's own Spacing |
| - exception, which is a thin defence in an app whose spec (§10.3) |
| - writes "24×24 targets" with "no exemptions" beside it. |
| - The overlay is the ordinary stretched-link idiom: one anchor, |
| - still the row's only tab stop and still carrying the document |
| - title as its accessible name, with an absolutely positioned |
| - ::after covering the row's padding box. Nothing is duplicated for |
| - a screen reader and no second link is invented, which is the |
| - thing that goes wrong when a row is made clickable by wrapping it |
| - in an anchor instead. |
| - What it costs: the timestamp under the overlay is no longer |
| - selectable text. Worth a 61px target on a row whose only purpose |
| - is to be opened. |
| - Focus stays on the title's own text rather than expanding to the |
| - row, so the ring points at what is actually being activated. */ |
| - [docs-row] { |
| - position: relative; |
| - } |
| - [docs-row-link]::after { |
| - content: ""; |
| - position: absolute; |
| - inset: 0; |
| - } |
| - /* A hover tint is the row's only signal that all of it is live — |
| - without it the enlarged target is real but invisible, which is |
| - the version of this fix that tests green and still feels broken. |
| - Pointer devices only: on touch there is no hover state to read, |
| - and :hover there sticks after a tap. */ |
| - @media (hover: hover) { |
| - [docs-row]:hover { |
| - background: var(--rst-surface-2); |
| - } |
| - } |
| + /* No row-click rule here on purpose, and it is worth saying why, |
| + because the measurement that argues for one is misleading. |
| + A row's title link measures 268×16 — well under the 24×24 WCAG |
| + 2.2 AA target minimum, with the thumbnail and the whole right |
| + half of the row apparently dead. That reading is wrong, and |
| + getBoundingClientRect is what makes it wrong: it reports an |
| + element's own box and cannot see an absolutely positioned |
| + pseudo-element. rastrillo already stretches this link over the |
| + whole row. tokens.css gives [rst-row] position: relative, gives |
| + [rst-row] :hover the --rst-surface-2 tint, and gives |
| + [rst-row-main] > a::after content:"" with inset: 0, under its own |
| + comment: "The name link's overlay makes the whole row clickable |
| + without nesting one anchor inside another." |
| + Our markup already matches that selector, so the row is fully |
| + clickable, fully hover-tinted and past 24×24 with nothing of ours |
| + involved. An earlier version of this file added [docs-row] and |
| + [docs-row-link]::after restating those exact declarations, and a |
| + hit test then "confirmed" a fix that had changed nothing — the |
| + probe was hitting rastrillo's overlay both before and after. |
| + If you are about to add one: hit-test the row before you change |
| + anything, not only after. */ |
| /* [docs-header-actions]: the header's one flex child opposite the |
| titles — [rst-page-header]'s space-between rule places it there |
| with no layout rule of its own beyond this; everything it holds |
| @@ -129,7 +110,14 @@ |
| The button itself is flex: none so the whole shortfall comes out |
| of the field. Shared proportionally, both would shrink, and |
| "New document" would start wrapping or clipping inside a control |
| - that had no reason to be narrower. */ |
| + that had no reason to be narrower. |
| + One thing to watch when the other eleven catalogs land: nowrap |
| + plus a non-shrinking button means a locale whose |
| + docs.list.create.label is wider than about 270px overflows the |
| + 320px column rather than wrapping out of it. English is 131px and |
| + is the only catalog today, so this is a note rather than a bug — |
| + but it is a real edge, and the fix when it arrives is a |
| + wrap allowance keyed to the label, not a smaller field. */ |
| [docs-create-form] { |
| display: flex; |
| flex-wrap: nowrap; |
| @@ -179,9 +167,9 @@ |
| {{range .Groups}} |
| <h2 docs-group-label>{{.Label}}</h2> |
| {{range .Rows}} |
| - <div rst-row docs-row> |
| + <div rst-row> |
| <span docs-thumb aria-hidden="true"></span> |
| - <span rst-row-main><a docs-row-link href="{{.Href}}">{{.Title}}</a></span> |
| + <span rst-row-main><a href="{{.Href}}">{{.Title}}</a></span> |
| <span docs-row-when>{{.When}}</span> |
| </div> |
| {{end}} |