Filed from firma2-design (ProjectFirma 2.0 spoke) · 2026-08-19
Found while composing the spoke's first prototype (/prototypes/projects) inside esa-app-shell.
Four independent defects in one component. Each verified against hub source and in a headless browser.
1. The bar's border-bottom rule never applies (Astro scoping)
esa-app-shell.astro declares:
.esa-app-shell__bar { flex: none; border-bottom: var(--border-width-default, 1px) solid var(--color-border-default-strong, #bbbbbb); }
It never matches. The shell renders <EsaAppBar tone="surface" as="header" class="esa-app-shell__bar">, and because EsaAppBar is a child .astro component its root <header> carries esa-app-bar's own scope id (data-astro-cid-ahjxwpid), not the shell's (data-astro-cid-za3k4dj5). The scoped selector selects nothing.
Verified in-browser — the tokens resolve fine, the rule simply doesn't land:
--border-width-default -> 1px
--color-border-default-strong -> #b9bcb8
header computed border-bottom -> 0px none ← rule never applied
Why it matters: the bar has no fill to fall back on. Hub defaults set --color-background-elevation-raised (what esa-app-bar paints) to var(--color-gray-1) — the same value as --color-background-default, the canvas. The border is the only thing separating app chrome from page, so the bar renders invisibly.
Probably affects any other rule the shell writes against a child component's root, not just this one — worth auditing together.
Note for whoever fixes it: re-pointing --color-background-elevation-raised so the bar gets its own value is not a viable spoke-side workaround. It drops two dataviz swatches below their contrast floor — check-themes.mjs goes 66/66 → 2 FAIL (dataviz-categorical-8 2.93:1, needs 3:1; dataviz-sequential-1 1.96:1, needs 2:1). That role carries dataviz backgrounds too, so it isn't free to move.
2. .esa-app-shell--accent {} is an empty stub
The accent prop is documented in the component header as adding "a faint brand-TINTED neutral wash to the bar (a color-mix, not a fill)". It is wired to .esa-app-shell--accent — which has no declarations. Passing accent does nothing.
This would have been the sanctioned fix for #1's invisible bar.
3. No full-height-sidenav layout variant
The shell hard-codes one arrangement: full-width bar stacked above a row containing the rail. The common alternative — rail owning full viewport height with the bar beside it — has no prop.
Working around it from a spoke means reaching into the shell's internals, because the nav is a grandchild (.esa-app-shell > .esa-app-shell__body > esa-sidebar-nav) and can't be placed by a grid on the shell:
.esa-app-shell { display: grid; grid-template-columns: auto minmax(0,1fr); grid-template-rows: auto minmax(0,1fr); }
.esa-app-shell__body { display: contents; } /* dissolve the wrapper */
.esa-app-shell__nav { grid-column: 1; grid-row: 1 / 3; }
.esa-app-shell__bar { grid-column: 2; grid-row: 1; }
.esa-app-shell__content { grid-column: 2; grid-row: 2; min-height: 0; }
A layout="rail-full-height" prop (or similar) would stop spokes needing to know that DOM shape.
4. The omnibox trigger's icon is a raw literal, off the icon ramp
Already filed as #27 — folded in here only so this list is complete. See that issue.
Workarounds currently shipping in firma2-design
All in src/layouts/AppLayout.astro, each commented as temporary and pointing here:
Related
The same prototype surfaced a separate tokens-architecture gap (no tier-1 control-size ramp, so --sidenav-item-height and friends are raw px): #29
Filed from firma2-design (ProjectFirma 2.0 spoke) · 2026-08-19
Found while composing the spoke's first prototype (
/prototypes/projects) insideesa-app-shell.Four independent defects in one component. Each verified against hub source and in a headless browser.
1. The bar's
border-bottomrule never applies (Astro scoping)esa-app-shell.astrodeclares:It never matches. The shell renders
<EsaAppBar tone="surface" as="header" class="esa-app-shell__bar">, and becauseEsaAppBaris a child.astrocomponent its root<header>carries esa-app-bar's own scope id (data-astro-cid-ahjxwpid), not the shell's (data-astro-cid-za3k4dj5). The scoped selector selects nothing.Verified in-browser — the tokens resolve fine, the rule simply doesn't land:
Why it matters: the bar has no fill to fall back on. Hub defaults set
--color-background-elevation-raised(whatesa-app-barpaints) tovar(--color-gray-1)— the same value as--color-background-default, the canvas. The border is the only thing separating app chrome from page, so the bar renders invisibly.Probably affects any other rule the shell writes against a child component's root, not just this one — worth auditing together.
Note for whoever fixes it: re-pointing
--color-background-elevation-raisedso the bar gets its own value is not a viable spoke-side workaround. It drops two dataviz swatches below their contrast floor —check-themes.mjsgoes 66/66 → 2 FAIL (dataviz-categorical-82.93:1, needs 3:1;dataviz-sequential-11.96:1, needs 2:1). That role carries dataviz backgrounds too, so it isn't free to move.2.
.esa-app-shell--accent {}is an empty stubThe
accentprop is documented in the component header as adding "a faint brand-TINTED neutral wash to the bar (a color-mix, not a fill)". It is wired to.esa-app-shell--accent— which has no declarations. Passingaccentdoes nothing.This would have been the sanctioned fix for #1's invisible bar.
3. No full-height-sidenav layout variant
The shell hard-codes one arrangement: full-width bar stacked above a row containing the rail. The common alternative — rail owning full viewport height with the bar beside it — has no prop.
Working around it from a spoke means reaching into the shell's internals, because the nav is a grandchild (
.esa-app-shell > .esa-app-shell__body > esa-sidebar-nav) and can't be placed by a grid on the shell:A
layout="rail-full-height"prop (or similar) would stop spokes needing to know that DOM shape.4. The omnibox trigger's icon is a raw literal, off the icon ramp
Already filed as #27 — folded in here only so this list is complete. See that issue.
Workarounds currently shipping in firma2-design
All in
src/layouts/AppLayout.astro, each commented as temporary and pointing here:.esa-app-shell__search > svg { width: var(--icon-size-md); height: var(--icon-size-md); }Related
The same prototype surfaced a separate tokens-architecture gap (no tier-1 control-size ramp, so
--sidenav-item-heightand friends are raw px): #29