Skip to content

Dogfood BareDOM components in demo gallery#2

Merged
avanelsas merged 20 commits intomainfrom
feature/demo-dogfood-components
Apr 13, 2026
Merged

Dogfood BareDOM components in demo gallery#2
avanelsas merged 20 commits intomainfrom
feature/demo-dogfood-components

Conversation

@avanelsas
Copy link
Copy Markdown
Owner

@avanelsas avanelsas commented Apr 13, 2026

Summary

Replaces the hand-rolled scaffolding in every demo/*.html page with the library's own components. Every demo already loads the full dev bundle, so the swap is free at runtime. The value is consistency (one visual language across 80+ pages), dogfooding (demos are the best place to find ergonomics / a11y / theming bugs in our own components), and ~541 fewer lines of repeated inline CSS.

86 files changed · +1436 / −1979 (net −543 lines) · 20 commits

What was swapped

Category Target Scope Files Elements
Section eyebrows <h2><x-typography variant="overline"> exclude x-typography.html 79 482
Page titles <h1><x-typography variant="h1"> exclude x-typography.html 81 81
Control buttons <button><x-button> exclude x-button.html; reverted 38 in 4 files where native class hooks were load-bearing 39 238
Enum selects <select><x-select> exclude x-select.html 60 124
Boolean toggles <input type="checkbox"><x-switch> (or <x-checkbox> for multi-select preferences) exclude x-checkbox.html / x-switch.html 54 112

Deferred per plan: category 6 (text/number <input><x-form-field>) and category 7 (event log .log<x-card>+<pre>) are intentionally out of scope — form-field is block-level with a built-in label and would be invasive; event-log panels have lower visual impact and await <x-code>.

Shared infrastructure changes

  • demo/demo-responsive.css — shared alignment rules for .row label / .row x-form-field, line-height fix for <x-switch> inside labels, compact size tokens for <x-select> inside grid layouts, and margin rules so <x-typography variant="h1"|"overline"> has the same vertical rhythm as native <h1>/<h2>.
  • demo/demo-theme.js — three compat shims: select-change / x-switch-change / x-checkbox-change are each re-dispatched as native change events and x-select's value attribute is echoed back on change, so existing demo JS that reads .value and listens for change keeps working without per-file edits. Theme-picker bar z-index also dropped from 9999 to 100 so overlay components render above it.

CSS cleanup

One dedicated commit walks every demo's inline <style> block and removes rules whose selector targets a type that no longer appears in the file (conservative: only single-selector rules where every comma-separated part is dead, @media blocks recursed into, x-typography.html preserved because it still uses real headings). 207 dead rules stripped across 81 files.

Commit log

# SHA Summary
1 3526bbe Fix demo h1 color not tracking theme changes
2 f565011 Phase 1 pilot — dogfood library components in x-alert.html
3 99e9139 Share demo control-row alignment rules
4 e14f252 Swap section h2 eyebrows → x-typography overline (482 tags, 79 files)
5 de4964a Restore vertical rhythm around dogfooded overline eyebrows
6 488a869 Swap page titles → x-typography h1 (81 files)
7 9701a67 Swap control buttons → x-button (238 buttons, 39 files)
8 59f49ff Wire x-button variants for class-styled buttons
9 52f448a Revert false-positive buttons in dropdown/dock/menu/splash
10 6360c60 Replace ambiguous class hooks with variant=secondary
11 ba881ad Swap selects → x-select (124 selects, 60 files) + shim + value defaults
12 c7ea9ec Swap checkboxes → x-switch (112, 54 files) + shims + querySelector extensions
13 93c320a Strip dead CSS rules from demo inline styles (207 rules, 81 files, −641 lines)
14 cc79c8f Stop forcing x-select width:100% inside .controls
15 1a41fe7 Fix leftover native-button / native-select styling on 12 demos
16 9d0a836 Fix dead button[data-stack] selector in x-morph-stack
17 4705645 Strip more leftover inline styles (slider/color-picker/timeline-item/liquid-glass/sidebar)
18 7832247 Fix unreadable text in x-soft-body color variants in dark mode
19 de3e434 Secondary nav buttons in x-spacer; use press event in x-switch control panel
20 5d2a8bc Lower demo theme-picker z-index so overlay components sit above it

Audit scans (all clean)

  • Zero leftover painterly style=\"...\" attributes on dogfooded components
  • Zero leftover class=\"...\" on <x-button> (only the intentional class=\"brand\" in x-button.html)
  • Every querySelectorAll('.controls input/select') call extended to also match x-switch / x-select
  • Native <h1> / <h2> only in x-typography.html; native <select> only in x-select.html
  • Native <button> only in 7 demos where live class CSS targets native buttons (intentional reverts)

Known residuals (cosmetic, non-blocking)

  • demo/x-alert.html still carries a local x-typography[variant=\"overline\"] { ... } override that duplicates the identical rule now in demo-responsive.css. Harmless (identical declarations). Can fold into a one-line cleanup later.
  • 4 dead input[type=\"checkbox\"] { ... } CSS rules remain in x-timeline-item / x-scroll-parallax / x-pagination / x-neural-glow (the cleanup script skipped compound selectors). They target no element and don't affect rendering.

Follow-ups for separate PRs

  • x-table-cell border alignment: [part=cell] needs height: 100% so cell border-bottom lines connect across columns with uneven intrinsic heights. Exists on main; caught during spot-checks.

Test plan

  • npx shadow-cljs watch app and open http://localhost:8000/demo/index.html, browse the gallery
  • Smoke-test the Phase-1 pilot: demo/x-alert.html — spawn alerts with varying attributes, confirm the event log populates
  • Spot-check a few high-interaction demos: demo/x-modal.html, demo/x-carousel.html, demo/x-table.html, demo/x-fieldset.html, demo/x-form.html, demo/x-notification-center.html
  • Theme smoke-test: append ?theme=ocean / ?theme=forest / ?theme=aurora to the URL and confirm the dogfooded chrome re-themes in lockstep with the target component
  • Verify overlay components render above the theme picker: demo/x-drawer.html, demo/x-sidebar.html, demo/x-toaster.html
  • Confirm the typography demo still works: demo/x-typography.html (intentional skips — native h1/h2 preserved)

🤖 Generated with Claude Code

avanelsas and others added 20 commits April 13, 2026 10:12
The "Library demo" title relied on inherited color from body through
the x-theme slot boundary, which did not update when themes switched.
Set color explicitly from --page-fg so applyThemeToPage rewrites reach
the h1 the same way they reach header p and .header-eyebrow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hand-rolled demo chrome in demo/x-alert.html with library
components so the demo gallery starts using the library it showcases:

- <h1>/<h2> -> <x-typography variant="h1"|"overline">
- demo control <button> -> <x-button> (press event)
- enum <select> -> <x-select> with select-change echo to keep .value live
- text/number <input> -> <x-form-field>
- .log div -> <x-card variant="filled"> wrapping <pre>

Drop the now-unused .row button / .row select / .row input / h2 / .log
CSS rules from the inline <style>. Wrap each control in an inline-flex
<label> so x-form-field (block) and x-select (inline-block) line up
consistently in the Spawn row.

This file is the reference template for rolling the same swaps across
the other ~80 demos in subsequent phases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fold the x-alert pilot's local alignment fix into the shared demo
stylesheet so future dogfooded demos inherit it automatically:

- .row/.panel/.controls/.control-group label => inline-flex, so the
  label text sits beside the control regardless of whether the control
  is inline-block (x-select, x-switch, x-button) or block (x-form-field).
- .row/.panel/.controls/.control-group/.field x-form-field =>
  inline-block, so a form-field dropped into a horizontal row does not
  force its inline <label> onto a new line.

This is Phase 2 step 0 of the demo dogfooding rollout. Local overrides
in demo/x-alert.html remain for now as a safety net and will be removed
in a cleanup commit once the shared rules are proven across several
demos.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 2 category (2) of the demo dogfooding rollout. Every demo page
used a hand-rolled "h2 { font-size:13px; text-transform:uppercase; ... }"
eyebrow pattern for section dividers; x-typography already has an
"overline" variant (0.625rem, uppercase, 0.1em tracking) that matches,
so this is a pure markup swap with no JS changes.

Scope:
- 482 h2 tags across 79 demo files rewritten to
  <x-typography variant="overline">.
- demo/x-typography.html is intentionally skipped: it showcases the
  h1-h6 variants and still needs a real h2 element.
- The dead "h2 { ... }" CSS rules in each file are left in place for
  this commit and will be removed in a follow-up CSS-cleanup pass so
  the diff here stays focused on markup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After swapping demo section <h2> elements to
<x-typography variant="overline">, the pages looked visibly crammed:
native <h2> had UA default top/bottom margins, but x-typography is
purely presentational and renders with zero margin.

Add the same rule the x-alert.html pilot uses locally
(display:block; margin:32px 0 10px) to the shared demo stylesheet so
every demo inherits the old vertical rhythm automatically. The local
override in demo/x-alert.html stays as a safety net for now.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 2 category (1) of the demo dogfooding rollout. Every demo page
used a bare native <h1>"x-foo demo"</h1> as its page title; swap to
<x-typography variant="h1"> so the page title is themed and styled
by the library itself.

Scope:
- 81 <h1> tags across 81 demo files rewritten.
- demo/x-typography.html is intentionally skipped (showcases the
  h1-h6 variants so must keep a real <h1>).
- Add a shared margin rule for x-typography[variant="h1"] in
  demo-responsive.css so the dogfooded title preserves the old
  bottom spacing the native <h1> had.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 2 category (3) of the demo dogfooding rollout. Every demo page
used hand-rolled native <button> elements (with inline
".row button { background:#1d4ed8; ... }" styling) for reset, spawn,
and control-panel actions; swap to <x-button> so the demo gallery
uses the library's own button component.

Scope:
- 238 native <button> tags across 39 demo files rewritten.
- demo/x-button.html never contained any native <button> (it already
  showcases <x-button> exclusively), so no files are intentionally
  skipped in this sweep.
- Pure HTML swap: no JS handler changes. x-button has a native
  <button> inside open shadow DOM, so native "click" events bubble
  out naturally and every existing click handler keeps working.
  Moving to the idiomatic "press" event is a possible follow-up.
- Dead ".row button { ... }" CSS rules in each file are left in
  place for this commit and will be removed in a follow-up CSS
  cleanup pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Follow-up to the Phase 2 button sweep: six demos had hand-rolled
class="secondary" / class="danger" / class="modal-close" / class="btn
btn-primary" hooks that styled native <button> elements. After the
mechanical swap they all rendered as default primary, so the visual
hierarchy (cancel, close, destructive actions) was lost.

Fixes:
- x-modal.html: close X and Cancel -> variant="ghost"; Clear log ->
  variant="secondary". Close button uses label="Close" for a11y.
- x-drawer.html: same treatment as x-modal.
- x-toaster.html: "With progress" / "With heading" -> variant="secondary";
  "Spam x 8" -> variant="danger".
- x-notification-center.html: info/success/warning quick-push buttons ->
  variant="secondary"; error quick-push and Clear All -> variant="danger".
- x-form.html: drop dead "btn btn-primary" class (primary is default),
  "btn btn-secondary" -> variant="secondary", inline font/padding hacks
  -> size="sm". x-button type="submit" cannot find the shadow <form>
  owned by x-form via closest("form"), so wire Sign in / Send / Reset /
  Clear via onclick="document.getElementById('...').submit()|.reset()".
- x-search-field.html: same treatment for the submit row inside x-form.

Native <form> submits in x-switch.html and x-radio.html keep working
via type="submit" and are not touched.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Follow-up to the Phase 2 button sweep: four demos used native
<button> with dedicated CSS class hooks as non-action visual
elements. The mechanical swap produced <x-button> everywhere and
broke their layouts because the class-based styling no longer
matched a native <button> element.

Reverts:
- x-dropdown.html: 23 <x-button class="menu-item"> (incl. the
  "danger" variant) back to native <button>. These are stacked
  menu rows inside <x-dropdown>, not action buttons; the local
  .menu-item CSS styles them as menu entries.
- x-liquid-dock.html: 6 <x-button class="dock-icon"> back to
  native <button>. Custom dock styling.
- x-menu.html: 8 <x-button slot="trigger" class="trigger-btn">
  back to native <button>. Trigger buttons carry a <span class=
  "chevron"> child and rely on the local .trigger-btn CSS to
  match the x-menu visual.
- x-splash.html: 1 <x-button class="demo-close-btn"> back to
  native <button>. Custom absolutely-positioned close.

Legit action buttons in these files (e.g. x-dropdown's .demo-btn
control panel) are left as <x-button>.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Follow-up to the Phase 2 button sweep: seven demos used hand-rolled
class hooks (demo-btn, ctrl, action, action-btn, toggle-btn) that
originally targeted "button.foo" selectors — outlined neutral
control buttons. After the swap to <x-button> those selectors no
longer matched and the buttons rendered as default primary.

Map the dead class hooks to the closest semantic variant:
- demo-btn / ctrl / action / action-btn / toggle-btn ->
  variant="secondary" (33 instances across 7 files)

Also fix x-popover.html, which had two extra class hooks not in the
original sweep list and produced an orange-inside-a-frame look:
- 2x trigger-btn (Delete item / No footer) -> variant="secondary"
- pop-footer-btn (Cancel / Close) -> variant="ghost"
- pop-footer-btn.primary (Confirm) -> default primary

The original .demo-btn / .ctrl / .trigger-btn / .pop-footer-btn CSS
rules in each file's inline <style> are now dead and will be removed
in the follow-up CSS cleanup pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 2 category (4) of the demo dogfooding rollout.

Markup sweep:
- 124 native <select> elements across 60 demo files rewritten to
  <x-select>. demo/x-select.html is intentionally skipped.
- For every swapped element, a value="..." attribute is added with
  the explicit <option selected> value if present, otherwise the
  first <option>'s value, so the dogfooded select renders with the
  same default selection that native <select> had.

Shared compat shim (demo-theme.js):
- A document-level select-change listener echoes detail.value back
  to the host's value attribute (because x-select does not auto-set
  it on user interaction) and re-dispatches a synthetic native
  "change" event. That keeps every existing demo handler working
  unchanged: el.value reads the live selection, and listeners
  registered with addEventListener("change", ...) still fire.

Shared layout (demo-responsive.css):
- Grid/panel/controls/control-group/field x-select gets width:100%
  + a compact md size override (--x-select-height-md: 2rem;
  --x-select-font-size-md: 0.8125rem;) to recreate the footprint
  the original native <select> had via the demos' inline CSS.
- Narrow the .row label inline-flex rule down to .row only. The
  earlier broader selector list (.row, .panel, .controls,
  .control-group) overreached: .controls is used inconsistently
  across demos (x-form treats it as a flex row, x-toast treats it
  as a CSS grid with column-stacked labels), so blanket inline-flex
  centering broke the stacked layouts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 2 category (5) of the demo dogfooding rollout.

Markup sweep:
- 112 native <input type="checkbox"> elements across 54 demo files
  rewritten to <x-switch>. demo/x-checkbox.html and demo/x-switch.html
  are intentionally skipped.
- demo/x-fieldset.html "Notification preferences" toggles converted
  back to <x-checkbox>: those are multi-select preferences (Email
  updates / SMS alerts / Weekly digest), semantically checkboxes, not
  on/off settings.

Compat shims (demo-theme.js):
- x-switch-change -> synthetic native "change" event so existing
  addEventListener("change", ...) handlers fire transparently.
- x-checkbox-change -> same shim, mirrors x-switch.

querySelector fixes:
- 23 demos used querySelectorAll(".controls input, .controls select")
  to wire up live controls. After the sweep, those selectors silently
  skipped the new <x-switch> / <x-select> elements (they aren't <input>
  or <select>). Extend each query to also include ".controls x-switch"
  and ".controls x-select" so the change handlers actually attach.

Shared layout (demo-responsive.css):
- x-switch is 24px tall (much taller than the native 16px checkbox it
  replaced), so flex labels with align-items:center end up with text
  glyphs sitting visually below the switch midline. Force all labels
  containing a direct x-switch child to display:inline-flex with
  min-height:24px and line-height:1, so the switch and the surrounding
  text label sit on the same visual midline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 2 housekeeping. The earlier sweeps swapped native <h1>, <h2>,
<button>, <select>, and <input type="checkbox"> elements to library
components, but left the original local CSS rules behind as dead
selectors that no longer matched anything.

Walk every demo's inline <style> block and remove any rule whose
selector is fully dead — i.e. every comma-separated part targets a
type that no longer appears anywhere in the file's body. Conservative
rules:
- only single-purpose selectors are removed (rules whose every part
  targets the now-dead type, evaluated by the rightmost compound)
- rules that combine dead and live selectors (e.g. "input, select")
  are left alone
- @media / @supports blocks are recursed into
- demo/x-typography.html keeps its h1 / h2 rules because that demo
  intentionally still uses native heading elements

Total: 207 dead rules removed across 81 files. No layout changes
intended — every remaining rule still matches a live element.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The earlier .controls x-select { width: 100% } rule was added to
satisfy x-toast.html, where labels are intentionally flex-column and
the dogfooded select needs to fill its grid cell. But .controls is
used inconsistently across demos: x-avatar-group (and others) use
.controls as a flex row with default-inline labels, where stretching
the select to 100% pushed the select onto its own line below the
label text and broke alignment with the other inline controls in
the row.

Narrow the width:100% to .grid only (which is unambiguously a stacked
grid layout). Keep the compact size override (--x-select-height-md /
--x-select-font-size-md) on all the common control containers — that
one is safe everywhere.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Post-sweep polish: several demos still carried leftover class hooks
or inline "style" attributes from their native-element days, which
rendered as duplicate host-level frames / colors on top of the
dogfooded x-button / x-select chrome.

Inline style leftovers on x-select (host border/background/padding
copied from the original native <select>):
- x-form.html         ctrl-autocomplete
- x-form-field.html   ctrl-type
- x-search-field.html ctrl-autocomplete

Inline style leftovers on x-button (padding/background/color copied
from a native <button>):
- x-form-field.html   Submit / Reset (kept only align-self:flex-start)

Dead / conflicting class="btn*" hooks on x-button:
- x-currency-field.html  class="btn btn-primary" / "btn btn-secondary"
  -> variant="secondary" + onclick (inside <x-form>, closest("form")
  can't see the shadow form so type="submit" doesn't work).
- x-stepper.html         class="btn" dropped, Previous -> secondary
- x-toast.html           dead class="btn-primary" dropped

Dead / conflicting class="secondary|danger|primary|..." hooks:
- x-toaster.html   class="secondary"|"danger" -> variant=secondary|danger
- x-sidebar.html   dead class="primary" dropped (primary is default)
- x-popover.html   disabled trigger class="trigger-btn" -> variant=secondary

Live trigger-button CSS that integrates with the surrounding custom
element (same approach as the earlier x-menu.html revert): revert
<x-button class="..."> back to native <button class="...">:
- x-navbar.html       10x ghost-btn / primary-btn / menu-btn
- x-menu-item.html    4x  trigger-btn
- x-context-menu.html 7x  trigger-btn

x-button.html's class="brand" is intentional (theming example).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The demo wired up Card/Detail/Next/Prev/Welcome/Profile/Done/etc.
action buttons via "document.querySelectorAll('button[data-stack]')",
but after the Phase 2 sweep those elements are <x-button>, not
<button>, so the selector returned nothing and no click listeners
attached. Narrow the selector to "x-button[data-stack]".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Follow-up polish: several more demos still had inline
"style=...padding/background/border/color..." attributes on their
x-button elements, copied straight from the native <button> they
replaced. Those styles applied to the x-button HOST and rendered as
duplicate frames on top of x-button's own shadow-DOM chrome.

Fixes:
- x-slider.html         Submit/Reset: strip leftover style; Reset -> variant=secondary
- x-color-picker.html   Submit/Reset: strip padding/border-radius/cursor, keep width:fit-content; Reset -> variant=secondary
- x-timeline-item.html  Approve/Reject: strip full styling, use size=sm + variant=secondary on Reject
- x-liquid-glass.html   "Try clicking" inside liquid-glass: strip custom bg/border, keep margin-top, use variant=ghost so the button sits visibly on the frosted surface
- x-sidebar.html        Event-log Clear button: strip font-size/padding, use size=sm

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The .amber / .indigo / .emerald variants in the x-soft-body demo only
set background, border, and shadow CSS variables but no text color, so
the slotted <h3>/<p> content inherited "color: var(--page-fg)" from
body. In dark mode that resolves to near-white, leaving the text
invisible on the hardcoded light card backgrounds.

Pin an explicit dark text color on all three variants so they remain
readable regardless of OS color scheme or x-theme preset. Latent bug
that predates the dogfooding sweep; caught during spot-checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… x-switch control panel

- x-spacer.html: Docs / Sign in / Settings nav-demo x-buttons now
  render as variant=secondary to read like nav links rather than
  primary calls-to-action.

- x-switch.html: switch the four control-panel x-button handlers
  from addEventListener("click") to addEventListener("press"). The
  "Next toggle: cancel" button was not firing its handler via click
  — presumably because the native click from x-button's internal
  <button> doesn't bubble out cleanly in this specific wiring — but
  "press" (the documented canonical x-button event) works reliably.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The sticky theme-picker bar in demo-theme.js was z-index:9999, which
covered every overlay component (x-drawer at 1000/1001, x-sidebar at
999/1000, x-toaster at 9000, x-modal/x-popover/x-dropdown similar),
making those demos hard to test because the picker obscured them.

Drop the picker to z-index:100 so every component overlay naturally
stacks above it. The picker is still above regular scrolled content
(default auto/0), so its sticky behaviour is unchanged — it only
yields to intentional overlays.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@avanelsas avanelsas merged commit abed571 into main Apr 13, 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