Skip to content

concierge: a decorative leading slot in the command bar - #14

Merged
DelanoJoey merged 1 commit into
mainfrom
feat/concierge-leading-slot
Sep 20, 2026
Merged

DelanoJoey merged 1 commit into
mainfrom
feat/concierge-leading-slot

Conversation

@DelanoJoey

Copy link
Copy Markdown
Collaborator

Why

Scrimmage wants a brand sprite inside its concierge bar. concierge.tsx styles entirely inline — no class names, no data attributes, no slots — so a host site cannot reach into the bar from its own stylesheet, and inline styles would beat it if it could.

The alternative was a Scrimmage-local fork of the bar. That would fork the guardrail, disclosure and lead-form logic with it — and the 2026-09-17 disclosure fix had to be chased across seven sites precisely because that logic lives in one place. It should stay there.

What

leading?: ReactNode, rendered ahead of the input.

left inset before the input
omitted (every other site) 10px 12px 10px 24px — unchanged nothing
supplied 10px 12px the node, aria-hidden

The inset drops by exactly the space the node takes, so the placeholder text starts where it always did.

The slot is aria-hidden and pointerEvents: none. The input already carries this control's accessible name; a second announced element in the same bar reads as a separate widget, and a focusable one would put a tab stop in front of the text field for decoration.

The claim that actually matters

This ships to every consumer on a ^ range. So the load-bearing claim is not "the slot works" — it's "a bar that passes no slot is unchanged."

Eight tests pin both halves, and each guarantee was mutation-checked rather than assumed:

mutation caught
padding forced unconditionally 1 fail
aria-hidden dropped 2 fails
slot rendered unconditionally 2 fails

Restored: 8/8 pass.

The bar is located in the tests by structure (the input's flex parent), not by an invented class — a test that added its own hook would be pinning the test's fiction rather than the shipped DOM.

Verification

  • suite 40/40 (8 new) · typecheck clean
  • version 1.3.0 → 1.4.0 (additive, minor)
  • jsdom needed a matchMedia stub — the widget reads it for the mobile footer collision and jsdom ships none

Publishing is tag-triggered (v* → GitHub Packages), so merging this does not release it.

🤖 Generated with Claude Code

Scrimmage wants a brand sprite inside its concierge bar. The widget styles
entirely inline — no class names, no data attributes, no slots — so a host
site cannot reach into the bar from its own stylesheet, and inline styles
beat anything it could write anyway. The alternative was a Scrimmage-local
fork of the bar, which would fork the guardrail, disclosure and lead-form
logic too; the 2026-09-17 disclosure fix had to be chased across seven
sites precisely because that logic lives in one place. It should stay there.

`leading` is an optional ReactNode rendered ahead of the input. Omitted —
which is every other site — the bar renders exactly as before, 24px inset
and all. Supplied, the inset drops to 12px so the text starts where it
always did, and the node sits in the space the padding gave up.

The node is wrapped `aria-hidden` and `pointerEvents: none`. The input
already carries this control's accessible name; a second announced element
inside the same bar reads as a separate widget, and a focusable one would
put a tab stop in front of the text field for decoration.

This ships to every consumer on a `^` range, so the load-bearing claim is
not "the slot works" but "a bar that passes no slot is unchanged." Eight
tests pin both halves, and all three mutations of the guarantee are caught:
forcing the padding unconditionally (1 fail), dropping the aria-hidden
(2 fails), rendering the slot unconditionally (2 fails).

Suite 40/40 · typecheck clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@DelanoJoey
DelanoJoey merged commit 9800151 into main Sep 20, 2026
1 check 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