Changelog
Release history for @zazz-ui/core — scoped per primitive, breaking changes flagged. Single source is the kit's CHANGELOG.md, embedded below.
Notable changes to @zazz-ui/core, grouped by primitive or base scope under each version. The grouping is load-bearing: the zazz-ui CLI's update and diff print only the slice that touches the files you've vendored. Breaking entries are flagged BREAKING with a one-line migration note. During 0.x, a minor bump means at least one breaking entry (ADR-0010 has the full definition of "breaking").
0.4.1 (2026-09-04)
Housekeeping on top of 0.4.0: the vestigial anchor-size() @supports gates come out, and the anchor-positioning support notes in the CSS headers and ADR-0011 are corrected. No rendered output changes in any browser — see the reasoning on the gate entry below.
base
- The four
@supports (inline-size: anchor-size(width))gates are removed (autocomplete panel, combobox panel, select::picker(select), multiselect panel);min-inline-size: anchor-size(width)now applies unconditionally. Not a browser-floor raise, and not breaking: in a browser withoutanchor-size()the function is unknown, so the declaration is dropped at parse time and theinline-size: max-contentabove it still stands — exactly what the gate produced.anchor-size()is also Baseline 2026 (Chrome 125, Firefox 147, Safari 26.0), inside the support floor either way. Migration: none. - The
anchor-namegates in popover, tooltip, select, and tabs are deliberately kept. Unlikeanchor-size(), dropping those would change rendering: fullanchor-namesupport starts at Safari 27, and on Safari 26.x the gate is what keeps a popover UA-centered instead of an unpositioned top-left box.tabs.cssalso keeps its@supports notbranch, which is a real designed fallback (indicator hidden, filled label instead).popover.cssrecords when to retire them. - Corrected support notes in
popover.css,autocomplete.css,combobox.css, andselect.css: anchor positioning reached Baseline in January 2026 (Chrome 151, Firefox 147, Safari 27), so the headers no longer claim Firefox lacks it. ADR-0011's support matrix is corrected the same way, and it now records why no anchor-positioning polyfill is adopted (@oddbird/css-anchor-positioningimplements noanchor-size(), no dynamic anchors, and wraps the target in a way that disturbs custom-element lifecycles).
0.4.0 (2026-09-03)
One theme: the polyfill set is corrected and cut to one. The Popover API and Invoker Commands are native across the kit's browser floor and are no longer polyfilled; interestfor — which is Chromium-only and drives every tooltip — was never actually polyfilled despite appearing to be, and now is. Net effect: tooltips and hover-open menus start working in Firefox and Safari, and the head's polyfill payload drops from ~228 KB to 15 KB. See ADR-0011.
base
- BREAKING The head contract ships one polyfill instead of two:
invokers@2.2.2/dist/esm/production/interest.js(Interest Invokers).@oddbird/popover-polyfillis removed — the Popover API is native in Chrome 114+, Firefox 125+, Safari 17+, iOS Safari 18.3+, all below the support floor — and theinvokerscompatiblebuild (Invoker Commands: Chrome 135+, Firefox 144+, Safari 26.2+) is replaced by theinterestbuild from the same package. Migration: re-runzazz-ui update(vendored) or re-renderbuildHead/ regenerate yourhead.html; if you hand-maintain your head, replace both old script tags with the singleinterest.jstag and itssha384-hR2BVNtsS7fIIMwOm+f7MY0JZfuByGhQQfevCBB6evFATKzBsTw0JzH/RxD94z2Thash. - BREAKING Browser floor raised to match: the Popover API and Invoker Commands are now assumed native rather than polyfilled. Browsers below the floor no longer get popover behavior at all (a closed
[popover]keeps the UA'sdisplay: none) instead of degrading through the polyfill. Migration: none if you were already on the documented floor (latest Chrome/Firefox/Safari, two versions back). - BREAKING The
.\:popover-openclass hack is gone from every selector andmatches()guard — 18 sites across popover, tooltip, menu, navigation-menu, command, select, combobox, autocomplete, toaster, andbase/typeahead. Selectors are plain:popover-opennow.command.csskeeps:where(:popover-open, [open]); that branch is the dialog form of the panel, not the polyfill. Migration: if your own CSS matched.\:popover-opento hook Zazz popovers, switch to:popover-open.
tooltip
- Fix: tooltips now work in Firefox and Safari. The trigger is
interestfor, and the previously loaded polyfill (invokers/compatible) contained nointerestforimplementation at all, so tooltips were silently Chromium-only. Theinvokers/interestbuild supplies it. - Header comment corrected: the Popover API is native across the floor, and
interestfor's implicit anchors still are not polyfilled (explicitanchor-nameis why the styling holds).
menu
- Fix: the optional hover/focus-open path on menu, menubar, and navigation-menu triggers (
interestforalongsidepopovertarget) now works outside Chromium, for the same reason as tooltip.
0.3.0 (2026-09-02)
Three themes: (1) theming is single-source — every theme role is declared once as a light-dark() pair, and .dark/.light/[data-theme] are pure color-scheme pins that re-resolve the same tokens (the inverted-popover feature is removed); (2) utility classes go fully logical — every physical-side declaration becomes its logical equivalent, every side-named class gains a logical-name alias, and border widths and dividers arrive; (3) the cascade layer contract is reworked around vendors, a grouped legacy, zazz.plugins, and overrides. In LTR horizontal writing with system theming, default rendering is unchanged except where flagged below.
base
- Theme roles are now declared exactly once as
light-dark()pairs on:root;.dark/.lightand[data-theme="dark"|"light"]are purecolor-schemepins that also re-assertcolor. Scopes nest — a.lightisland inside a.darksection re-lightens its subtree. Override a role once (--primary: light-dark(…, …)) and every mode picks it up. Visible change: the manual/toggled dark palette had drifted from the system-dark arms and is now reconciled to them —--border(tint-100),--popover(neutral-950),--primary/--secondary(800),--tertiary(800) replace the stale tint-200/shade-950/500/400 copies, so toggled dark now matches system dark exactly. - BREAKING The inverted-popover feature is removed: the
--use-inverted-popoversproperty and thedata-use-inverted-menuattribute no longer exist. Popovers follow the page's color scheme. Migration: delete both from your markup/CSS; to force a dark popover, setcolor-scheme: darkon it (tokens re-resolve automatically). - New text-decoration utilities:
underline,overline,line-through,no-underline. - New border width utilities:
border-1toborder-4(all sides), per-sideborder-{t|b}-{1-4},border-{l|s}-{1-4},border-{r|e}-{1-4}. Compose with border color classes. Not responsive. - New divider utilities:
divide-x/divide-ydraw a 1px border on the trailing logical edge of every direct child except the last. Children inherit--_border-color, so border color classes on the container recolor the dividers. - New logical-name aliases (identical values to their physical-name twins):
inline-*/block-*forw-*/h-*(plusmin-/max-variants),start-*/end-*forleft-*/right-*,ps-*/pe-*forpl-*/pr-*,ms-*/me-*forml-*/mr-*(negative margins included),border-s/border-eforborder-l/border-r,rounded-s/e/ss/se/ee/es-*forrounded-l/r/tl/tr/br/bl-*, andtext-start/text-endfortext-left/text-right(responsive variants included). - Positioning, border, radius, and text-align utilities converted from physical to logical properties/values:
left:→inset-inline-start:,border-right:→border-inline-end:, physical corner radii →border-start-start-radiusetc.,text-align: left|right→start|end. Rendering only changes in RTL/vertical writing modes, where utilities now mirror with the text direction. - BREAKING Internal radius composition variables renamed to logical corners:
--_radius-top-left|top-right|bottom-right|bottom-left→--_radius-start-start|start-end|end-end|end-start. Migration: rename in any custom CSS that reads or sets the--_radius-*vars (they are internal, so most consumers are unaffected). - Fix:
z-isolatedeclared invalidz-index: isolate(a no-op); nowisolation: isolateas documented. - Fix:
--ui-prose-figcaption-gapwas consumed by.ui-prose figurebut never declared; now declared (0px, preserving current spacing) so the hook works. - Fix: the
global-view-transition--oldkeyframes read--view-transition-opacity--new; they now read--view-transition-opacity--old, making that documented hook functional (defaults are identical, so default rendering is unchanged). - Fix: article containers'
data-container="full"|"bleed"referenced undeclared--article-full/--article-bleedtokens (the rules were invalid and fell back to auto width); they now implement band-container semantics —fullspans the available width keeping gutters,bleedruns edge to edge. - BREAKING Cascade layer contract reworked: top-level order is now
variables, reset, vendors, legacy, zazz, overrides. Newvendorslayer for third-party CSS that does not build on Zazz (aboveresetso the reset can't clobber library widgets, belowlegacy). Themigrationstop-level layer moved to alegacy.migrationssublayer —legacynow nestsimports, components, utilities, migrations, so deleting a finished migration is dropping one layer; shims still beat all other legacy CSS but no longer beat Zazz (useoverridesfor that). Newzazz.pluginssublayer betweencomponentsandutilitiesfor Zazz-dependent extensions (added variants can restyle the primitives they extend; utilities still win). Newoverridestop-level layer as the structured app-override slot — only unlayered CSS outranks it. Migration: import whole legacy files withlayer(legacy.imports)(never barelayer(legacy)— un-sublayered rules form an implicit final sublayer that beats the shims), movemigrations.csstolayer(legacy.migrations), and move any shim that had to override Zazz into@layer overrides.
button
- Buttons paint with
background-clip: padding-box, and filled variants (primary,secondary,muted, …) now default their border color to the variant background (--ui-button-border-color: var(--ui-button-background)and the--hover/--activepairs) instead oftransparent. Same rendered look with cleaner edges on translucent backgrounds; if you overrode a variant background, its border now follows automatically.
badge
- Same treatment as buttons:
background-clip: padding-box, and variant border colors default to the variant background instead oftransparent.
carousel
- Example markup: dropped a redundant
font-headingclass from the example heading (text-h5already applies the heading weight).
tooltip
- Example markup no longer carries
data-use-inverted-menu="false"— the attribute was removed with the inverted-popover feature (see base).
0.2.0 (2026-08-31)
Token naming consistency pass + shared field-family inheritance. Two themes: (1) every token is now named after the logical CSS property it feeds (-block-size, never -height), and interactive controls decompose borders into -border-width / -border-style / -border-color parts; (2) buttons, toggles, tabs, checkboxes, radios, and badge borders now default to the shared --ui-field-* family, so one --ui-field-radius or --ui-field-block-size override retunes every control that sits on a line together. Default rendering is unchanged — the new aliases resolve to the same values.
base
- BREAKING
fields.cssnow registers first among primitives inindex.css(it owns the shared--ui-field-*family);CSS_CASCADE_ORDERand thebutton/badge/tabs/checkboxmanifest entries gained afieldsdependency. Migration: re-runzazz-ui update(vendored) or re-emit your import order from the manifest; hand-maintained subsets must loadfields.cssbefore its consumers. - CONVENTIONS.styles.md §5/§6 now document the token-naming rules: logical property names, full property names (no abbreviations), the border width/style/color decomposition (composed at the usage site, never into a
:roottoken),-foregroundvs-{part}-color, and sanctioned cross-component token defaults. - Physical
top/left/width/heightdeclarations converted to logical equivalents in dialog, lightbox, mobile-menu, tooltip, carousel, and badge; remaining physical uses are commented exceptions (anchor()side keywords, centering idioms). - BREAKING
group-hover:scale-*now matches the regularscale-*set (0,25,50,75,90,98,99,100,101,102,110,125,150). Dropped95/105. Migration:group-hover:scale-95→group-hover:scale-98(or90);group-hover:scale-105→group-hover:scale-102(or110).
fields
- BREAKING
--ui-field-height→--ui-field-block-size. Migration: rename the token in your overrides. - BREAKING
--ui-field-border(color-valued) →--ui-field-border-color;--ui-field-border--hover/--focus→--ui-field-border-color--hover/--focus. New--ui-field-border-width(1px) and--ui-field-border-style(solid) parts. Migration: rename color overrides; width/style are now their own hooks.
input
- BREAKING
--ui-input-border-radius→--ui-input-radius;--ui-input-border(shorthand) replaced by--ui-input-border-width/-style/-coloraliases of the field parts. Migration: rename, or override the--ui-field-border-*parts to move the whole family. --ui-input-calendar-picker-radiusnow defaults to--ui-field-inset-child-radius(same value).
textarea
- BREAKING
--ui-textarea-border-radius→--ui-textarea-radius;--ui-textarea-borderreplaced by--ui-textarea-border-width/-style/-color.--ui-textarea-font-sizenow defaults to--ui-field-font-size(same value).
select
- BREAKING
--ui-select-border-radius→--ui-select-radius;--ui-select-borderreplaced by--ui-select-border-width/-style/-color;--ui-option-height→--ui-option-block-size;--ui-option-border(--hover/--active)replaced by--ui-option-border-width/-style/-color(+-color--hover/--active).
otp
- BREAKING
--ui-otp-slot-borderreplaced by--ui-otp-slot-border-width/-style/-color.
combobox
- BREAKING
--ui-combobox-border-radius→--ui-combobox-radius;--ui-combobox-borderreplaced by--ui-combobox-border-width/-style/-color(aliasing the select parts);--ui-combobox-control-align→--ui-combobox-control-align-items;--ui-combobox-control-wrap→--ui-combobox-control-flex-wrap; removed the dead--ui-combobox-displaytoken (nothing consumed it).
input-group
- BREAKING
--ui-input-group-text-size/-text-weight/-text-color→--ui-input-group-font-size/-font-weight/-foreground;--ui-input-group-align→-align-items;--ui-input-group-wrap→-flex-wrap.
password-group
- BREAKING same renames as input-group (
-text-*→-font-*/-foreground,-align→-align-items,-wrap→-flex-wrap).
button
- Button metrics now default to the shared field family:
--ui-button-block-size/-font-size/-line-height/-padding/-radius/-icon-size/-ring-coloralias--ui-field-*, and--ui-button-border-width/-stylealias the field border parts (same values as before; retune--ui-field-*and buttons follow, remap--ui-button-*to diverge). - BREAKING
--ui-button-height→--ui-button-block-size;--ui-button-border(--hover/--active)(shorthands) replaced by--ui-button-border-color(--hover/--active)color parts. Migration: rename; to restyle the whole border override the width/style/color parts.
toggle
- BREAKING
--ui-toggle-height→--ui-toggle-block-size;--ui-toggle-border*shorthands replaced by--ui-toggle-border-width/-style/-color(+-color--hover/--active/--checked/--checked-hover/--checked-active), defaulting to the button parts.
badge
- BREAKING
--ui-badge-height→--ui-badge-block-size;--ui-badge-border(--hover/--active)replaced by--ui-badge-border-width/-style/-colorparts. Border + ring now default to--ui-field-*(same values); metrics stay an independent smaller scale.
tabs
- BREAKING
--ui-tabs-label-min-height→--ui-tabs-label-min-block-size. - Track/label metrics now default to the field family:
--ui-tabs-track-radius(--ui-field-radius),--ui-tabs-indicator-radius(--ui-field-inset-child-radius), label font-size/line-height/padding/ring-color (same values as before).
checkbox
- BREAKING
--ui-checkbox-border(--hover/--checked)→--ui-checkbox-border-color(--hover/--checked), plus new width/style parts. Tokens moved from the element onto:rootin@layer variables(they are now overridable from:rootlike every other hook); surface + border default to--ui-field-*.
radio
- BREAKING
--ui-radio-border(--hover/--checked)→--ui-radio-border-color(--hover/--checked), plus new width/style parts; surface + border default to--ui-field-*.
switch
- BREAKING
--ui-switch-track-width/-track-height→--ui-switch-track-inline-size/-track-block-size;--ui-switch-thumb→--ui-switch-thumb-background.
dialog
- BREAKING
--ui-dialog-width/-height→--ui-dialog-inline-size/-block-size; removed the dead--ui-dialog-displaytoken (nothing consumed it).
alert-dialog
- BREAKING
--ui-alert-dialog-width→--ui-alert-dialog-inline-size.
progress
- BREAKING
--ui-progress-height→--ui-progress-block-size.
meter
- BREAKING
--ui-meter-height→--ui-meter-block-size.
slider
- BREAKING
--ui-slider-track-height→--ui-slider-track-block-size.
table
- BREAKING
--ui-table-head-height→--ui-table-head-block-size.
toaster
- BREAKING
--ui-toaster-width→--ui-toaster-inline-size;--ui-toaster-description-color→--ui-toaster-description-foreground;--ui-toaster-border→--ui-toaster-border-color(it held a color).
carousel
- BREAKING
--ui-carousel-slide-min-width→--ui-carousel-slide-min-inline-size.
kbd
- BREAKING
--ui-kbd-color→--ui-kbd-foreground.
mobile-menu
- BREAKING
--ui-mobile-menu-backdrop→--ui-mobile-menu-backdrop-color.
accordion
- BREAKING
--ui-accordion-icon-transform-open→--ui-accordion-icon-transform--open(double-dash state convention).
popover
- BREAKING
--ui-popover-border→--ui-popover-border-color(it held a color).
tooltip
- BREAKING
--ui-tooltip-border→--ui-tooltip-border-color(it held a color).
lightbox
- BREAKING Removed dead tokens nothing consumed:
--ui-lightbox-thumb-border(--active)(the thumb border comes from--ui-lightbox-img-border; active/focus feedback is the inset ring + opacity),--ui-lightbox-radius(slides/thumbs use--ui-lightbox-slide-radius/--ui-lightbox-thumb-radius), and--ui-lightbox-thumb-aspect-ratio. Migration: delete overrides of these; retune the named replacements instead.
0.1.0 (2026-08-28)
First public release. One package, consumed three ways: the dist/zazz.css + dist/zazz.js bundles for a two-tag CDN drop-in, per-file CDN URLs into the readable src/ tree, or copying the code into your project and owning it.
base
- Cascade-layer architecture:
_layers,_variables,_reset,_typography,_view-transitions, then primitives, then_utilitiesand_layout. Alayer(legacy)slot lets a migrating stylesheet ride below the kit. - Core runtime (
utils,signals,zazz-element,dialog-lifecycle), opt-in page behaviors (reveal,navigation), and the shared engines behind the typeahead family (typeahead,command-score,hotkeys) and the Embla carousel adapter. head.ts: the canonical<head>contract (fonts, stylesheet, pinned and SRI-checked import map, polyfills, theme persistence), in a local mode for vendored copies and a CDN mode (cdn: { version, primitives?, sri? }).manifest.ts: the distribution manifest.PRIMITIVES(files, dependencies, and examples for every primitive),CSS_CASCADE_ORDER,resolveClosure(), andMANIFEST_VERSION.dist/sri.json: sha384 hashes of every published css/js file, regenerated each release.
primitives
- 43 primitives: accordion, alert-dialog, autocomplete, avatar, badge, breadcrumbs, button, button-group, card, carousel, checkbox, combobox, command, dialog, fields, input, input-group, kbd, lightbox, menu, menubar, meter, mobile-menu, navigation-menu, otp, password-group, popover, progress, prose, radio, reveal, select, separator, slider, switch, table, tabs, textarea, toaster, toggle, toggle-group, toolbar, tooltip, utilities.