Forty component modules registering 43 light-DOM custom elements — the shared
sixteen plus the ported catalog entries. No build step, no runtime
dependency, no shadow root — <mue-*> upgrades the markup you write and applies
the same classes the vanilla previews use, so the one stylesheet styles every framework.
mue-button renders a real <button>. variant,
size, state, block and icon map to the
modifier classes in the sheet; disabled is the native attribute.
mue-input owns the label/helper/error structure: label is joined by
for/id, state="error" sets aria-invalid and
points aria-describedby at the message. Emits mue-change.
mue-textarea is the same field contract as the input, plus rows.
mue-select upgrades the authored <option>s into a native
<select>, so OS type-ahead and screen readers keep working untouched.
mue-checkbox keeps a native input inside its own label, so Space toggles and the
checked state is announced; each change emits mue-change.
mue-radio-group wraps mue-radio options in a labelled
<fieldset> and shares one name, so arrow keys move the choice.
mue-switch is a native checkbox given role="switch" and
aria-checked; the visible track is decorative. Space flips it.
Author children stay in the light DOM, so any markup that works in the vanilla preview works
here; interactive and selected add the modifier and the aria state.
The element adds markup and behaviour. It never adds a stylesheet.
A card with interactive takes role="button", so it
cannot also hold a link — that nesting is a nested-interactive failure. A card that
owns an action keeps it in a plain card like this one; a card that is the action uses
interactive and carries no controls inside.
mue-badge conveys state through text, not colour alone; the optional dot is
aria-hidden.
mue-avatar renders an image (keeps alt) or real-text initials;
mue-avatar-group stacks them under a labelled role="group".
mue-dialog traps focus, closes on Escape and returns it to the trigger;
[slot="footer"] children move into the dialog footer.
Nothing is claimed to work until the headless check has actually run against it. This dialog is that element, with its own title, close control and focus trap.
mue-dropdown follows the menu pattern: the trigger carries
aria-haspopup/aria-expanded, arrows walk the rows, and choosing one
emits mue-select then closes.
mue-tabs pairs each mue-tab with the mue-tabpanel at the
same index and marks its tablist data-mue-bound, so the vanilla script never
double-wires it. Arrow keys move focus, Home/End jump, Enter/Space activate.
The element writes the same class names the vanilla preview does.
Every class is defined in styles/components.css — the parity audit rejects any that are not.
Colour, radius, spacing and motion come from design-tokens.
mue-accordion-item triggers are real buttons with aria-expanded and
aria-controls; single-open by default, add multiple for several.
mue-table upgrades an authored <table>, wraps it in
.table-wrap and sorts the columns that carry data-sort, emitting
mue-sort.
| Component | Binding | Status |
|---|---|---|
| Button | mue-button | verified |
| Dialog | mue-dialog | verified |
| Tabs | mue-tabs | verified |
mue-notification keeps the sheet's two shapes: a persistent alert and
a toast shown by setting open, dismissed by its own button.
mue-breadcrumbs carries .breadcrumbs and its aria-label
on the host itself and lays the trail out as real <a> links split by
<span class="separator" aria-hidden>; the last entry becomes
<span aria-current="page">. Feed it items (each
Label or Label::href) or author the links as children.
mue-pagination carries .pagination + aria-label on the
host and renders real <button> cells. The ‹ / › arrows use the native
disabled attribute at the bounds, the active page carries
aria-current="true", and a range wider than the window inserts
<span class="ellipsis" aria-hidden>. Choosing a page emits
mue-page-change.
Three tags compose it: mue-sidebar carries .sidebar +
role="complementary" and groups its children into .sidebar-section
blocks, mue-sidebar-heading replaces itself with
<div class="sidebar-heading"> and opens a new section, and
mue-sidebar-link replaces itself with <a class="sidebar-link">
holding a decorative icon plus <span class="sidebar-label">. Clicking a link
moves aria-current="page" to it and emits mue-navigate — the one
behaviour the static sidebar had none of.
mue-datatable takes the authored toolbar, selection bar, table and footer you write
inside it — it adds no markup of its own beyond the pager cells — and wires search, sort cycling
none → asc → desc with aria-sort, the status filter dropdown, row expansion into a
.descriptions panel, select-all with a truthful indeterminate head checkbox, page
size, refresh (the loading tbody is the sheet's skeleton cells), the empty state and the sticky
.is-scrolled rule. It only ever toggles classes the sheet already defines.
| Expand row | Order |
|
Actions | |||
|---|---|---|---|---|---|---|
| Kai Tanaka kai.tanaka@meridianlabs.jp | ORD-98214 | Paid | $11,480.00 | |||
|
||||||
| Amara Okafor amara@brightline.co | ORD-98207 | In transit | $2,340.00 | |||
|
||||||
| Lucas Meyer l.meyer@northwind.example | ORD-98199 | Disputed | $8,910.50 | |||
|
||||||
| Priya Raman priya.raman@fieldstack.io | ORD-98186 | Paid | $460.00 | |||
|
||||||
| Sofia Almeida sofia@harborworks.pt | ORD-98175 | In transit | $14,200.00 | |||
|
||||||
| Noah Berg noah.berg@fjordtech.no | ORD-98163 | Paid | $3,125.75 | |||
|
||||||
| Elena Rossi elena.rossi@ateliermi.it | ORD-98152 | Disputed | $780.00 | |||
|
||||||
| Marcus Bell marcus@northline.dev | ORD-98141 | Paid | $649.00 | |||
|
||||||
|
No orders match those filters Clear the search or the status filter to see the full list again. |
||||||
mue-date-picker owns the .field wrapper — .field-label
joined by for/id, .field-helper or
.field-msg, and state="error" writing .is-error +
aria-invalid="true" + aria-describedby. The calendar surface is the
platform's own: min/max are forwarded untouched so the browser greys
out-of-range days. It emits mue-change.
mue-dropzone renders the .dropzone target around a real
<input type="file" class="dropzone-input">, labels it with an
.sr-only <label>, and lists each chosen file as a
.file-row (icon, .truncated name, size, remove button) in a
.dropzone-list. .is-dragging is the only state class it toggles, on
dragover. Choosing files emits mue-change; removing one emits
mue-remove.
mue-progress renders the sheet's .progress-group →
.progress-label (span + .percent) → .track
(role="progressbar", aria-valuenow/min/max) → .bar. Only
the --value custom property is set inline, exactly as the vanilla bar does;
variant and striped add .progress-success /
.progress-warning / .progress-striped, and
indeterminate renders the bare .progress-indeterminate track.
Press Nudge build to set a new value on the first bar and watch
mue-change.
mue-skeleton sets the host to the atomic shape
(.skeleton.skeleton-text|title|avatar|img|btn) or, for row and
card, composes the sheet's .skeleton-row / .skeleton-body
with .stack. Every placeholder is aria-hidden="true" — the shimmer and
its reduced-motion pause are the sheet's.
mue-empty carries .empty + role="status" +
aria-label on the host, paints the decorative .empty-icon,
.empty-title and .empty-desc, and moves the action children you author
into the sheet's .row — so the buttons stay yours and keep their focus order.
mue-error carries .error-state + role="alert" on the
host, adds .error-inline for the card variant, and paints an
aria-hidden .error-icon beside an .error-body that reuses
.empty-title/.empty-desc plus a .row of your recovery
actions. The message is text plus an icon, never colour alone.
mue-stepper takes the <ol class="stepper"> you author and adds
one <span class="stepper-dot" aria-hidden> per step plus the single state class
the sheet knows — .is-done (check glyph), .is-current (+
aria-current="step") or .is-upcoming. current is 1-based and
clamped; changing it emits mue-step. The rail itself is a status display: the wizard
buttons beside it own Back / Next.
mue-rating renders read-only as <p class="rating" role="img">
with an aria-label that spells the score, .rating-stars of
.star.is-filled/.is-half (the half fill is the sheet's gradient) and a
.rating-value. With interactive it is a real
<fieldset class="rating-input"> of .rating-radio inputs plus
.rating-star labels, each carrying an .sr-only name — so the arrow keys
are the platform's radio-group path. Choosing a star emits mue-change.
mue-popover builds <span class="popover-wrap"> around a real
.popover-trigger button (aria-describedby → the surface) and moves your
children into <span class="popover" role="note">, so the
.popover-title/.popover-body/.popover-list markup stays
yours. placement adds .popover-below/.popover-start/.popover-end.
Reveal is pure CSS (:hover, :focus-within) plus the one state class
.is-open it toggles on click; Escape closes it and refocuses the trigger.
mue-modals renders .overlay → .modal
(role="dialog", aria-modal="true", aria-labelledby) with
.modal-header/.modal-title/.modal-close, your children in
.modal-body and [slot="footer"] buttons in .modal-footer.
A <button slot="trigger"> stays outside the overlay and gets
aria-haspopup="dialog". Escape closes, Tab wraps inside the panel, focus returns to
the opener, and width is the only inline property (--modal-w).
Shown on the card.
The element sets --modal-w on the panel only when the attribute is present,
mirroring the vanilla page's override.
mue-charts renders <figure class="chart"> with a
.chart-title figcaption and an <svg viewBox="0 0 700 180" role="img">
carrying <title>/<desc> joined by
aria-labelledby: bars are <rect class="bar bar-primary|bar-success|bar-warning">
over <text class="axis"> labels, a line is
<polyline class="line"> with <circle class="point"> markers,
and legend builds .legend / .legend-item / .legend-dot. Only the dot
colour is inline, as in the vanilla page; the optional summary caption is emitted as
a plain <p> because the class the vanilla page writes there
(.value) is defined by no shared stylesheet.
mue-guidance keeps the surface you author inside a .tour-stage.card,
adds a .row with a .btn-outline.btn-sm replay control and a
.badge.badge-info status, then moves the single state class .is-open
between the <span class="tour" data-step> stops you wrote — each one a
.tour-target around the real control plus a .tour-card
(role="dialog", .tour-arrow, .tour-step,
.tour-title, .tour-body, .tour-actions).
Next / Back / Skip / Finish drive it, Escape closes it, and current is 1-based.
mue-fab carries .float-btn-group around the
.float-btn children you author: static shows the dock in place, without
it the dock gets .is-visible only past the reveal scroll threshold.
Forecast workspace — the tour spotlights each control it is talking about.
Step 1 of 3
Recalculate reads the live ledger
Every forecast is recomputed from the order ledger on click.
Step 2 of 3
Export carries the stamp
CSV, XLSX or a shareable link, each stamped with the run time.
Step 3 of 3
Nothing changes silently
Every edit is written here with who made it and when. Finish closes the tour.
Remove static and the same dock stays hidden until the page has
scrolled past reveal (420px by default).
Every custom element emits on the host with a detail payload. Clicking, typing or
toggling anything above appends the real event here — nothing in this log is invented.
Waiting for interaction…