diff --git a/.github/workflows/landing-check.yml b/.github/workflows/landing-check.yml index bf1d786..c83920e 100644 --- a/.github/workflows/landing-check.yml +++ b/.github/workflows/landing-check.yml @@ -7,7 +7,9 @@ # privacy page at a range of widths and fails on horizontal overflow. # # Kept out of android-build.yml so it does not tangle with the required "build" -# check and only runs when the docs actually change. +# check and only runs when the docs actually change. The stylesheets are in that +# set too: layout lives in the CSS, so a markup-only filter would let exactly the +# change most able to overflow a page through untested. name: Landing check on: @@ -15,11 +17,13 @@ on: branches: [main] paths: - 'docs/**.html' + - 'docs/**.css' - 'scripts/check-landing-overflow.mjs' - '.github/workflows/landing-check.yml' pull_request: paths: - 'docs/**.html' + - 'docs/**.css' - 'scripts/check-landing-overflow.mjs' - '.github/workflows/landing-check.yml' workflow_dispatch: diff --git a/HISTORY.md b/HISTORY.md index 99c54df..676cd2c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,11 @@ +## 2026-07-27 - Landing stylesheets made direction-aware (#146) + +- Trigger: looking back at #146 — the Persian reader whose right-to-left note content laid out left-to-right, fixed in v2.28.0 — the same question one level up: the app handles RTL, the site around it never had to. The work started as a Persian README plus landing page; the maintainer cut that mid-flight, leaving only the part that has to exist before any RTL page can. +- Analysis: both stylesheets were written in physical directions — `left` on the skip link and the file-receipt card, `border-left` on the trust-ledger dividers, `padding-left` on the feature-point bullets and their `::before` dash, `margin-left` in the file-browser bar, `text-align: right` on the channel-row status, `padding-left` on the privacy page's lists. Each is a no-op to swap under LTR and a visible break under RTL. Three things do not follow from direction at all: the font stacks name Latin/CJK faces only, the display tracking is negative (which closes up a joined script), and `translateX` on the channel-row hover has no logical form. +- Contract/scope: physical offsets become logical (`inset-inline-start`, `padding-inline-start`, `border-inline-start`, `text-align: start/end`); the three exceptions get `[dir="rtl"]` rules — an Arabic-script *system* stack (no webfont, the pages fetch nothing), `letter-spacing: normal`, and a mirrored hover nudge with its `:active` reset. No new page and no content change: the six landing pages, six privacy pages and six READMEs are untouched. +- Verification: geometry diff of every element on 14 pages x 4 widths, old CSS vs new. The twelve real pages come out box-identical; the only computed-style change anywhere is `text-align` reporting `start` instead of `left` on three `.channel-row em`. Two throwaway `dir="rtl"` copies of `index.html` and `privacy.html` moved 20 elements and picked up the new stack, and screenshots confirm the mirror by eye — the file-receipt card and its accent border swap to the inline-start edge. `check-landing-overflow.mjs` passes 14 pages x 6 widths with both probes included. Pixel diffing was tried first and abandoned: the animated tablet GIFs and the `loading="lazy"` webps make the *same* tree screenshot differently twice, which read as four to nine phantom regressions per run. +- Gate: `landing-check` only fired on `docs/**.html`, so a stylesheet change — the one most able to overflow a page — reached `main` untested. `docs/**.css` added to both path filters, which is what makes this change itself gated. + ## 2026-07-25 - Notes grid, first-line titles, and v2.32.0 (#279, #280) - Trigger: two feature requests filed the same afternoon by one external reporter — a grid view for the notes list (#279), and a title field for each note (#280), the second reported as "what is set as the title is not necessarily the first line". diff --git a/docs/landing.css b/docs/landing.css index fa89db7..5e7cdab 100644 --- a/docs/landing.css +++ b/docs/landing.css @@ -141,6 +141,29 @@ li { word-break: keep-all; } +/* Right-to-left pages. The layout itself mirrors on its own — every offset in + this file is a logical property — but typography does not follow from + direction. Arabic-script text needs a face the Latin/CJK stack above does not + carry; no webfont is fetched, so this is a system stack. Groundwork from + #146, where a Persian reader ran into the app's own right-to-left handling. */ +[dir="rtl"] { + --font-display: Vazirmatn, "Noto Naskh Arabic", "Noto Sans Arabic", "Segoe UI", Tahoma, system-ui, sans-serif; + --font-body: Vazirmatn, "Noto Naskh Arabic", "Noto Sans Arabic", "Segoe UI", Tahoma, system-ui, sans-serif; +} + +/* Tight negative tracking is a Latin display trick; in a joined script it pulls + letters into each other. */ +[dir="rtl"] :is(h1, h2, h3) { + letter-spacing: normal; +} + +/* Mono + wide tracking + uppercase reads as a Latin small-label device, and + Arabic script has no equivalent — keep the scale, drop the Latin styling. */ +[dir="rtl"] :is(.kicker, .story-step, .channel-status, .release-line) { + font-family: var(--font-body); + letter-spacing: normal; +} + :focus-visible { outline: 0.1875rem solid var(--color-focus); outline-offset: var(--space-1); @@ -157,7 +180,7 @@ li { background: var(--color-primary-ink); color: var(--color-surface); font-weight: 700; - left: var(--space-4); + inset-inline-start: var(--space-4); padding: var(--space-3) var(--space-4); position: fixed; text-decoration: none; @@ -417,12 +440,12 @@ li { .file-receipt { background: var(--color-surface); - border-left: var(--space-1) solid var(--color-primary); + border-inline-start: var(--space-1) solid var(--color-primary); bottom: 0; box-shadow: 0 var(--space-2) var(--space-6) var(--color-shadow-soft); display: grid; gap: var(--space-1); - left: calc(-1 * var(--space-6)); + inset-inline-start: calc(-1 * var(--space-6)); max-width: 18rem; padding: var(--space-4) var(--space-6); position: absolute; @@ -448,7 +471,7 @@ li { } .hero-product > figcaption { - padding-left: var(--space-4); + padding-inline-start: var(--space-4); } .trust-ledger { @@ -466,7 +489,7 @@ li { } .proof-item + .proof-item { - border-left: 0.0625rem solid var(--color-white-line); + border-inline-start: 0.0625rem solid var(--color-white-line); } .proof-item p { @@ -546,7 +569,7 @@ li { .feature-points li { color: var(--color-ink); - padding-left: var(--space-6); + padding-inline-start: var(--space-6); position: relative; } @@ -554,7 +577,7 @@ li { color: var(--color-primary); content: "—"; font-weight: 800; - left: 0; + inset-inline-start: 0; position: absolute; } @@ -606,7 +629,7 @@ li { .file-browser-bar strong { font-size: var(--text-caption); - margin-left: var(--space-2); + margin-inline-start: var(--space-2); } .ownership-demo pre { @@ -808,6 +831,16 @@ li { transform: translateX(0); } +/* The nudge should follow the reading direction; translateX has no logical + form, so right-to-left gets its own pair (order matters — :active resets). */ +[dir="rtl"] .channel-row:hover { + transform: translateX(calc(-1 * var(--space-1))); +} + +[dir="rtl"] .channel-row:active { + transform: translateX(0); +} + .channel-row strong, .channel-row small { display: block; @@ -828,7 +861,7 @@ li { font-size: var(--text-caption); font-style: normal; font-weight: 750; - text-align: right; + text-align: end; } .channel-paused em { @@ -943,7 +976,7 @@ li { } .file-receipt { - left: var(--space-4); + inset-inline-start: var(--space-4); } .trust-grid { @@ -955,7 +988,7 @@ li { } .proof-item + .proof-item { - border-left: 0; + border-inline-start: 0; border-top: 0.0625rem solid var(--color-white-line); } @@ -1028,7 +1061,7 @@ li { .file-receipt { bottom: var(--space-2); - left: var(--space-3); + inset-inline-start: var(--space-3); max-width: calc(100% - var(--space-6)); padding: var(--space-3) var(--space-4); } @@ -1054,7 +1087,7 @@ li { } .channel-row em { - text-align: left; + text-align: start; } } diff --git a/docs/style.css b/docs/style.css index c633d47..b7f2f33 100644 --- a/docs/style.css +++ b/docs/style.css @@ -27,6 +27,12 @@ body { line-height: 1.65; } +/* Arabic-script text has no face in the stack above; a right-to-left page needs + one. No webfont is fetched — see the same note in landing.css. */ +[dir="rtl"] body { + font-family: Vazirmatn, "Noto Naskh Arabic", "Noto Sans Arabic", "Segoe UI", Tahoma, system-ui, sans-serif; +} + a { color: inherit; } @@ -336,7 +342,7 @@ footer a { } .content ul { - padding-left: 1.3rem; + padding-inline-start: 1.3rem; } .content pre {