fix(theme): rail-btn-block must use an explicit width (0.4.1)

0.4.0's rail-btn-block used display:block + width:auto, which fills for an <a>
but SHRINK-WRAPS a <button> — a form control does not take the fill-available
behaviour a non-replaced block box gets. Measured in one 197px rail-foot
container: <a> filled to 177.42px (0 short), <button> came up 114px short.

That matters because a rail foot normally holds both element types: Sign In is
an anchor, Sign Out is a form-submit button. So 0.4.0's modifier got one right
and one wrong, and the wrong one is what signed-in operators see — worse than
the width:100% overflow it was meant to replace (~10px of visible clipping).

Fixed with an explicit calc(100% - 1.2rem) + box-sizing:border-box, which
measures 0px short for both element types. Both rejected shorthands and their
measured failures are recorded in the CSS so neither is reintroduced as a
'simplification'.

Found by HistorianGateway while evaluating adoption; it held off adopting
rather than trading one defect for a larger one. No consumer had adopted the
modifier, so nothing shipped broken. The .btn half of 0.4.0 is unaffected and
is already live-verified in three apps.

Build 0 errors, 48/48 tests.
This commit is contained in:
Joseph Doherty
2026-08-11 03:01:15 -04:00
parent e7a126a9fc
commit 49fbbcfaeb
2 changed files with 22 additions and 7 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Version>0.4.0</Version> <Version>0.4.1</Version>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
@@ -226,14 +226,29 @@
text-decoration: none; text-decoration: none;
} }
/* Full-width modifier for a rail-foot action (sign in / sign out), composed as /* Full-width modifier for a rail-foot action (sign in / sign out), composed as
class="rail-btn rail-btn-block". Block + width:auto fills the rail inside the class="rail-btn rail-btn-block".
same 0.6rem inset .rail-btn already carries. Deliberately NOT width:100% —
that resolves against the containing block and then *adds* the 1.2rem of side The width is explicit — calc(100% - 1.2rem) — and NOT one of the two obvious
margin, overflowing the rail. Apps that hand-rolled this override hit exactly shorthands, because a rail foot typically holds BOTH element types: Sign In
that. */ is an <a>, Sign Out is a form-submit <button>. Each shorthand breaks one:
width: 100% overflows. It resolves against the containing block and then
.rail-btn's 0 0.6rem side margins are ADDED on top, pushing
the control ~1.2rem past the rail edge (measured: 10px of
visible clipping on a 197px rail).
width: auto shrink-wraps a <button>. A form control does not take the
fill-available behaviour a non-replaced block box like <a>
gets, so the anchor filled and the button came up 114px short
in the same container. That asymmetry is worse than the
overflow it replaced, because the button is the one signed-in
operators see.
calc() gets both right (measured 0px short for <a> and <button> alike). The
1.2rem must stay in step with .rail-btn's side margins above. */
.rail-btn-block { .rail-btn-block {
display: block; display: block;
width: auto; box-sizing: border-box;
width: calc(100% - 1.2rem);
text-align: center; text-align: center;
} }