Skip to content

fix(frontend): inline-direction spacing is logical, so RTL is a dir attribute - #78

Merged
khafifithebork merged 1 commit into
masterfrom
fix/logical-properties
Sep 27, 2026
Merged

khafifithebork merged 1 commit into
masterfrom
fix/logical-properties

Conversation

@khafifithebork

Copy link
Copy Markdown
Owner

Split out of ADR-033 the same way ADR-032 was: a utility swap needs no product decision, while §4's deferral of localisation does.

No locale is approved and none is assumed. This caps a debt that grows with every page, and costs nothing today — the logical utilities are the same length and behave identically in a left-to-right document.

The estimate in the ADR was wrong

"About four" came from reading by eye. Counted with a pattern: nine, in two kinds.

Six are content flow and are now logical — Curriculum's nesting indent and accordion label, TranscriptPanel's cue marker, scrollbar gutter and cue text, Field's "(optional)" marker.

Two are navigation chrome and stay physical. SiteNavTree closes the desktop rail and the mobile drawer with border-r while both are anchored to the left edge, the drawer sliding in from -100%. Making those logical flips which side the navigation lives on, which border closes it, and which way the animation travels — ADR-029 accepted a left rail, so that's a layout decision, not a tidy-up.

Pinned, not exempted

That distinction is what made a guard possible at all. The test asserts the physical set is exactly those two, so removing them fails just as adding a new one does:

Provocation Fails
A physical utility reappears in content "logical everywhere except the pinned chrome"
The pinned rail is made logical without updating the pin "still physical in exactly the two pinned places"

An exemption list silently absorbs the next offender; a pinned set makes either direction a deliberate edit to a test. left-/right- insets are deliberately out of scope — they position against the viewport and physical is correct there, so a rule catching them would fire on code that is right.

One false positive found by running the pattern rather than reasoning about it: border-l matches border-line, a colour token in this palette. Hence the (?![a-z]), and a test asserting the token isn't flagged.

⚠️ The RTL flip is not verified, and the reason is worth reading

Setting dir="rtl" made direction compute to rtl while the physical mapping did not move — which looked exactly like the swap having achieved nothing, and I nearly reported it as such.

An isolated probe settled it: a bare element with an inline padding-inline-start: 16px also fails to flip when dir is set directly on it. So this browser does not re-resolve logical properties on a direction change and cannot demonstrate the behaviour either way.

That's the third tooling artefact this session — after a frozen requestAnimationFrame and a stylesheet that hadn't loaded reporting every radius as 0px. Each first read as a bug in the code. Isolate before concluding: not doing so here would have cost a revert of a correct change.

A person with a real browser and dir="rtl" closes this.

What is verified

The emitted CSS carries real padding-inline-start, border-inline-start-width, margin-inline-start and text-align: start. LTR rendering is unchanged — the curriculum indent still computes to padding-left: 16px with a left border, exactly as pl-4 border-l did, checked against a rounded-full control.

425 frontend tests across 37 files, tsc, eslint, verify:css, verify:a11y, verify:static all pass.

ADR status after this

ADR-033 now says which of its three habits is done (§2), which is blocked on backend modelling rather than a decision (§3, <track> language attributes), and that only §4's deferral still needs you. STATUS records that both remaining ADRs were split on the same principle.

🤖 Generated with Claude Code

…ttribute

Split out of ADR-033 the same way ADR-032 was, at the owner's instruction: a
utility swap needs no product decision, while §4's deferral of localisation does.
No locale is approved and none is assumed — this caps a debt that grows with every
page, and costs nothing today because the logical utilities are the same length
and behave identically in a left-to-right document.

**The estimate in the ADR was wrong, in the direction estimates usually are.**
"About four" came from reading by eye. Counted with a pattern there were **nine**,
and they split into two kinds:

Six are content flow and are now logical — `Curriculum`'s nesting indent and
accordion label, `TranscriptPanel`'s cue marker, scrollbar gutter and cue text,
`Field`'s "(optional)" marker.

Two are navigation chrome and stay physical. `SiteNavTree` closes the desktop rail
and the mobile drawer with `border-r` while both are anchored to the left edge, the
drawer sliding in from `-100%`. Making those logical flips which side the
navigation lives on, which border closes it and which way the animation travels —
**ADR-029 accepted a left rail**, so that is a layout decision, not a tidy-up.

They are **pinned rather than exempted**, which is what made a guard possible at
all: the test asserts the physical set is exactly those two, so removing them fails
just as adding a new one does. An exemption list silently absorbs the next
offender. Provoked both ways. `left-`/`right-` insets are deliberately out of
scope — they position against the viewport and physical is correct there, so a rule
catching them would fire on code that is right.

One false positive found by running the pattern: `border-l` matches `border-line`,
which is a colour token in this palette. Hence the `(?![a-z])`, and a test that
asserts the token is not flagged.

**The RTL flip is not verified, and the reason is the third tooling artefact of
this session.** Setting `dir="rtl"` made `direction` compute to `rtl` while the
physical mapping did not move — which looked exactly like the swap having achieved
nothing. An isolated probe settled it: a bare element with an **inline**
`padding-inline-start: 16px` also fails to flip when `dir` is set directly on it,
so this browser does not re-resolve logical properties on a direction change and
cannot demonstrate the behaviour either way.

After a frozen `requestAnimationFrame` and a stylesheet that had not loaded
reporting every radius as `0px`, that is three artefacts that each first read as a
bug in the code. **Isolate before concluding** — not doing so here would have cost
a revert of a correct change.

What is verified: the emitted CSS carries real `padding-inline-start`,
`border-inline-start-width`, `margin-inline-start` and `text-align: start`; and LTR
rendering is unchanged, the curriculum indent still computing to
`padding-left: 16px` with a left border exactly as before, checked against a
`rounded-full` control.

ADR-033 now states which of its three habits is done, which is blocked on backend
modelling, and that only §4's deferral still needs the owner. STATUS records that
both remaining ADRs were split on the same principle.

425 frontend tests across 37 files, tsc, eslint, verify:css, verify:a11y and
verify:static all pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@khafifithebork
khafifithebork merged commit 179fcb5 into master Sep 27, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant