0.5.0 is the first element quality release, covering every A, B and C element. Components rebuild cleanly when moved or re-populated, keyboard and screen-reader access is fixed across the C components, and several XSS paths are closed. Themes gain a --btn-border-width hook, reach 4.5:1 contrast for links and outline buttons, and paint their own dark-mode backgrounds. It includes breaking changes, listed under Changed: C-component API cleanups, <cite data-variant="quoted">, removed legacy link and abbr classes, and the brand demo and stale per-accent theme files are no longer in the package.
--btn-border-width button hook. Themes set the border width through it
(plus border-color/border-style) instead of a border shorthand. The
padding gives the width back, so primary, secondary and ghost buttons are the
same height in every theme. All bordered themes and packs use it.
Stop buttons and history for the AI components. <ai-chat> and
<ai-summary> show a Stop button while a reply streams (plus a public
stop()); endpoint mode sends prior turns as messages[]
(ai-page-tools v1.1 — servers that ignore it still work); the
{content} fallback-URL token is filled (capped at 1,500 characters;
<ai-chat> uses only its context region); format="plain-text"
renders as text; <ai-summary> warns when input exceeds the on-device
quota.
heading and heading-level on <analytics-panel> and
<author-index>. <analytics-panel title> is renamed heading (the
global title put a tooltip on the whole panel).
<activity-feed> load-more contract. load-more carries
detail.done(), the feed is aria-busy while loading, and removing
data-infinite ends it. Authored <time datetime> works as the
timestamp without JavaScript.
Analytics.getStatus() — a read-only snapshot of the analytics
runtime: whether it is running, why it is off (user, GPC, DNT, consent),
the fields an event carries, and per-page send counts. A
vb:analytics-status event fires when it changes.
<markdown-viewer sanitize> — parsed markdown now goes through the
shared sanitizer by default; sanitize="off" opts out for trusted
content.
data-accent for colour accents. Accent presets (ocean, forest,
sunset, …) were inline seeds ThemeManager wrote on <html>, and the
themes page's data-theme="forest" examples did nothing. data-accent
now applies them on <html> or any element, as a separate axis from
data-theme: accents tint the default and personality themes (modern,
minimal, classic); fixed-palette themes keep their colours.
ThemeManager.setAccent() sets the attribute. The CSS is generated from
src/lib/theme-data.js (scripts/generate-accent-css.mjs).
VB.store and VB.service are public. The storage and fetch layers
VB's components already used are now on window.VB and exported by name
from the main, core and autoload bundles (import { VBStore, VBService, VBServiceError } from 'vanilla-breeze/js'). VB.service.configure({ baseUrl }) points every /go/ role (notify, email, newsletter) at your
own backend; VB.store.clearAll() clears every vb:* key. Their state
is shared across bundles, so configuration reaches autoloaded components
too. Documented at /docs/concepts/vb-service/ and
/docs/concepts/vb-store/.
Brand demo themes are no longer in the package. themes/anthropic.css,
themes/ibm.css, themes/mcdonalds.css and themes/starbucks.css were
demonstrations of building a theme from a brand, not library themes. They
now live with the alpenglow demo (/docs/alpenglow/themes/) and are no
longer in dist/cdn/themes/ or the theme manifest.
Stale per-accent theme files removed from the package.
themes/{ocean,sunset,forest,rose,lavender,coral,amber,indigo,slate,emerald}.css
were leftovers from before accents were bundled. Use data-accent="ocean"
(and so on) with the main bundle instead.
Breaking API changes from the C-element review.
<comment-thread>: addComment()/updateComment() take bodyText (set as text) or bodyHtml (sanitized) instead of body. Author links are kept only for http(s) and relative URLs.
<comment-box> submit: the event detail offers waitUntil(promise). The box stays busy, clears when the promise resolves and keeps the text when it rejects. comment-thread:reply follows the same pattern and is now cancelable.
<chat-input>:
- Inside a form it now submits the form (Enter or the send button) instead of firing
chat-input:send.
- The send detail is
{ message }.
--chat-input-radius is removed.
<chat-window>: user messages are always shown as plain text. resolveTyping() fills a typing placeholder.
<card-list>:
setItems()/getItems() are removed; use .items.
- URL attributes bound from data must be safe URLs, and
on*/style are never written.
- It follows the global
data-layout system.
<popularity-index>: the analytics endpoint moves to analytics-src.
- Change events:
<compare-surface> and <content-lens> no longer fire them during setup or when changed from script.
<content-swap>: without explicit triggers, it adds a real toggle button instead of turning the container into a button.
<color-picker>: required is removed. name and disabled on the inner input move to the host.
<context-menu>: select detail is { item, value }.
<change-set>: gains controls="none".
<calendar-wc>: month titles are plain text unless data-heading-level is set.
<calendar-event>: is a block (titles ellipsize). It shows a pointer only when it contains a link or button.
- Overlapping clusters:
data-layout-overlap keeps source order (the "+N" chip comes last).
<cite>: the quoted variant is data-variant="quoted" (was class="quoted"), matching a and abbr. Its quotation marks follow the language, as <q> does: «…» in French, „…" in German.
<brand-mark> never modifies its content. Authored children (an
<img>, an <svg>, text, a tagline) are left exactly as written. src
adds one owned <picture>; image-only vs wordmark is CSS, with the
authored text kept as the accessible name. alt and height now work,
src-compact is implemented (plus compact-media), and the host no
longer gets an aria-label. New api.json.
Blockquote attribution belongs outside the quote. figure.quote +
figcaption is the documented pattern; blockquote > footer /
> cite still style as a legacy attribution line, and only as direct
children.
<analytics-panel> rebuilt on the runtime's real state. It used to
read storage keys nothing writes, so it always said "No data stored"
while page views were being sent, and it reported "active" to visitors
whose Global Privacy Control or Do Not Track kept analytics off. It now
shows what is actually sent and why analytics is off, Pause/Resume go
through setConsent(), "Clear my data" is gone (there was nothing local
to clear), it renders once and keeps focus, and it uses VB tokens, so it
is readable in dark mode. The unused --font-size-0/--font-size-1
Open Props aliases are removed.
Legacy link and abbr classes removed. a.muted, a.plain and
a.external are gone (use data-variant="muted" / "plain" and
rel="external"), and abbr.plain is now abbr[data-variant="plain"],
matching the link API.
vanilla-breeze-autoload.js ships in the npm package. The
autoloader was documented as a loading strategy but excluded from the
package. It finds its components/ chunks next to its own script, so it
works from unpkg.
Buttons follow the theme's hover and active colours. Themes set
--color-primary-hover / --color-primary-active, but button
computed its own states, so those overrides never showed. A button with
no custom colour now uses --color-interactive-hover and the new
--color-interactive-active (both follow primary); a button with
--btn-color still derives its own states.
nav.pills and nav.pagination pick readable text for the current
item with contrast-color() instead of a fixed white, which failed in
dark themes that lighten their primary colour. The current-page tokens
(--color-active-text, --color-active-bg) are now documented on the
nav page.
Themes.
- Links, outline buttons and primary-button labels reach 4.5:1 contrast in every theme and accent, in both modes (art-deco, claymorphism, cyber, journal, nord, solarized, super2026, tokyo-night, dusk, bauhaus, high-noon, dawn, cottagecore; forest, sunset, coral and amber accents). Brand primaries keep their brand value; only
--color-interactive changed.
- Dark mode paints each theme's own
--color-background as the page background. A fixed VB blue-black used to cover it in most themes (solarized dark showed blue-black, not #002b36). The light-only a11y-dyslexia palette declares color-scheme: light, so in dark mode it is a readable cream page instead of dark text on a dark page.
- Theme blockquote glyphs are silent to screen readers and sit on the inline-start side in right-to-left text, with logical padding.
- A breadcrumb's linked current crumb is plain current text, not the nav current-page pill (starbucks pill, neumorphism inset, wrong colour elsewhere).
Range sliders. The value bubble and datalist labels are positioned in CSS, so they track the thumb after late layout changes and resizes. The chat [data-bubble] shape no longer narrows range inputs to 75%.
Security and data loss.
<card-list> no longer writes javascript: URLs from data.
<comment-thread> no longer injects unsanitized HTML or javascript: author links.
<chat-window> no longer parses typed text as HTML.
ConsentBanner.getConsent() honours expiry and the docs await it. Before, the sample never started analytics.
Components that broke when moved or re-populated. These now rebuild cleanly on reconnect and on data setters: <calendar-wc>, <color-picker>, <command-palette>, <context-menu>, <comment-thread>, <comment-box>, <carousel-wc> and <card-list>. Before, they duplicated their UI, doubled their listeners, or left actions dead.
Keyboard and screen-reader access in the C components.
<content-swap>: explicit triggers work.
<chat-input>:
- Confirming input-method composition with Enter no longer sends.
- It has a focus ring.
disabled and maxlength are enforced.
<context-menu>:
- Opens with Shift+F10 or the Menu key.
- Reports the chosen item.
- Uses correct menu roles.
<command-palette>: announces the active option.
<color-picker>:
- Its button is named by the field's label.
- Escape closes it.
<calendar-wc>:
- Arrow keys move from the focused day.
- The day detail opens from the keyboard.
- Multi-day events no longer lose a day across daylight-saving changes.
data-year-range sets how many years the year select lists either side of the current year (default 10).
<carousel-wc>:
- Autoplay has a pause/play button.
- It stops announcing every slide while rotating.
<chart-wc>:
- The fallback table stays available to screen readers.
- Charts print as a table.
- Charts built from data get a generated table.
CSS for the C elements.
- Invalid rule removed: the
translate: no rule did nothing; use the HTML translate="no" attribute.
<pre>:
- Bare
<pre> no longer wraps ASCII art.
<pre><samp> gets the code-block treatment.
- Inline
<code>: wraps long identifiers.
- Editorial theme: no longer turns inline citations into block lines.
- Cover hero: headers span the full width.
<compare-surface>: works in right-to-left layouts and reacts to position changes.
<content-lens>: chips show keyboard focus.
C element doc pages.
- Removed false screen-reader claims (cite, code, colgroup, caption, chat-thread, comment-thread).
- Layout pages show the
data-layout attribute form.
- Canvas demos follow the theme.
- The carousel transitions demo meets 4.5:1 text contrast on every slide.
- colgroup documents
scope="colgroup".
- Every C component's
api.json now matches its runtime.
<bdo dir="rtl"> reverses text again. VB's own unicode-bidi
rules outranked the element and weakened the browser's isolation on
every [dir] element; they are removed and the UA defaults apply.
data-loading buttons keep the submitter. The button was disabled
inside the submit event, dropping its name/value from the form data;
it now uses aria-disabled and swallows repeat activations, only the
button that submitted shows the spinner, and spinner-only keeps its
label as hidden text.
Buttons. a.button follows --btn-color (a gold link button was
white on gold); input[type=submit|button|reset] match <button>; all
variants share a transparent border (equal heights, visible in forced
colours); disabled buttons ignore hover, hover is skipped on touch;
aria-disabled and aria-pressed are styled; icon-only buttons are
square; the focus ring follows the --focus-ring-* tokens.
<bread-crumb> and nav.breadcrumb. Separators are silent to
screen readers, data-truncated shows an ellipsis, collapsed trails get
a real "Show full path" button, <nav aria-label="Breadcrumb"> is the
canonical markup (no duplicate landmark), JSON-LD resolves relative
hrefs, and path segments are decoded.
B-element doc pages. <base> and <body> are written (they were
placeholders); <bdi>/<bdo> guidance and demos corrected; <b> and
<strong> no longer claim screen readers announce them; <br> and
button pages updated.
A-element review, batch 5.
<audio-player>: the seek slider announces "0:13 of 0:30", mute
reports aria-pressed, load errors show a message, shuffle plays each
track once per cycle, scrubbing no longer jumps, and the player and the
.audio-standalone utility share one track helper (both auto-advance).
<audio-visualizer>: caches its canvas and colours instead of doing
the work every frame, draws a baseline instead of a blank box (and a
single frame under reduced motion), and follows for/data-mode
changes.
<activity-feed>: follows the APG feed pattern (Page keys move one
article; feed children are all articles; aria-setsize is -1 while
infinite), and avatars render (they never did).
<author-index>: a failed fetch keeps the filter on authored markup,
ids are unique for any script, unsafe hrefs are dropped, result counts
are announced, and moving it doesn't refetch.
- Article highlights: every article paints its own palette (only the
first did), two articles sharing a colour no longer overwrite each
other, and custom colours work in the
<mark> fallback.
<comment-thread> avatars render.
<area> focus uses the theme's interactive colour.
<ai-summary> XSS. Model or endpoint output rendered through
<markdown-viewer> was inserted as raw HTML; it is now sanitized.
<activity-feed> froze grouped feeds. Any child change on a
data-group feed started an endless regroup loop. Moving the feed no
longer stacks infinite-scroll sentinels, and keyboard navigation now
works (a single Tab stop with a roving tabindex; keys work from links
inside an entry).
<audio-player> keyboard. Shortcuts were bound on the host and
could not see past the shadow boundary, so arrow keys on the volume
slider seeked the track and Space on the playlist started playback.
Focused controls now keep their own keys. loop now loops a single
track.
<audio-visualizer> restarts after a DOM move, no longer silences
cross-origin audio (it skips hooking and sets data-state), no longer
doubles the volume when two visualizers share an <audio>, and clamps
bad data-fft-size values instead of throwing.
<ai-chat> / <ai-summary> no longer duplicate their UI when
moved; <ai-chat> ignores a send while a reply is streaming (Escape to
stop keeps working), and streaming output is aria-busy.
<accordion-wc> panels setter bound listeners twice when assigned
before connection (with transition, a click never opened the panel)
and dropped data-upgraded when assigned later. A lone <p> or <ul>
answer is now wrapped instead of being turned into role="region".
Link auto-icons. A download link to a PDF shows the download
arrow instead of the PDF badge; links in nav, header and footer and
button-styled links suppress every auto-icon (some slipped through
before); an authored <icon-wc> replaces the auto glyph instead of
doubling it; the glyphs carry empty alternative text, so screen readers
read only the link text. <abbr> gets the dotted underline and help
cursor only when it has a title.
A-element doc pages corrected after a quality review: article
highlights (the toolbar is <selection-menu>; no initHighlights
export; real colour defaults and events), article.compact no longer
collapses its text column, data-drop-cap no longer applies twice,
address.inline sample is valid HTML and .compact separators are
CSS-generated and silent, aside.float un-floats below 40rem, wrong
landmark/screen-reader claims fixed on article, aside and address,
area (shape is optional; coordinates; document order), audio
captions, the accordion custom-indicator escapes and flush wording,
the author-index JSON sample, and stray parsed placeholders on the AI
component pages.
Theme text contrast on real pages. A new themed site-pages suite
(5 doc pages × 10 aggressive themes × light/dark, with an AA check on
every text node) found 37 failures; all are fixed. Kawaii dark inline
code was 1.6:1 (the kawaii pack hardcoded the light-mode colour);
brutalist light code inside <pre> sat on the inline-code chip (2.4:1)
and its links deepen a step; vaporwave light links and code use a
deeper --color-interactive; win9x dark links, muted text and the
active nav link are lighter on the dark window.
win9x puts page content in a window. Text sat directly on the teal
desktop (body ~3:1, links and muted text under 2:1); main now has the
window surface, framed by the teal desktop.
Themes no longer frame every form and blockquote. Under the ~28
themes that set a border style (swiss, genai, kawaii, organic, …),
data-border-style auto-applied a full border to bare form and
blockquote with no padding, so controls and quote text touched the
frame. Those rules were inert until 0.4.0 moved them out of the tokens
layer. Auto-apply now targets padded containers only (.card, the
preview panels).
tab-set panels span the full width. The open panel sat in the
first tab's column, which stretched that tab across the component and
pushed the others to the far edge (off-screen on phones). Chromium and
Safari wrap <details> content in a ::details-content box that became
the grid item; tab-set now flattens it and gives the grid one explicit
column per tab.
data-page-layout="sidebar-right" stacks on phones. The mobile stack
template had no body-aside area, so the aside got an implicit column
beside the stack and overflowed the viewport.
Print styles and two themes match the current effect syntax. They
still targeted [data-gradient-text], [data-shimmer] and friends,
which became data-effect tokens.
Component transitions animate again. tab-set, accordion-wc and
carousel-wc animate their transition modes with vt-*
keyframes that only existed in the opt-in utils/view-transitions.css,
which neither bundle imports, so the transitions silently did nothing.
The keyframes now ship in vanilla-breeze.css and
vanilla-breeze-core.css; cross-document @view-transition stays
opt-in.
data-transition presets animate. slide, fade, scale and
none played the browser's plain crossfade, for two reasons: the
runtime processed elements before the presets registered, so they never
got their view-transition-class, and the rules for those classes lived
only in the opt-in utils/view-transitions.css. Late registrations now
re-wire waiting elements, like effects and triggers. The class rules,
the --view-transition-duration tokens and the reduced-motion rule now
ship in every bundle, so content-swap, image-gallery and
version-switcher transitions also respect reduced motion.
combo-box exposes one combobox, the W3C way. The combobox role
went through ElementInternals, which put it on the host in Chromium
while aria-expanded, aria-controls and aria-activedescendant sat on
the inner input (Firefox put everything on the input). The role is now a
plain role="combobox" on the input in every browser, as the docs
already described.
data-effect="stamp" is visible on dark surfaces. It blended with
multiply and dark ink, which nearly vanished on dark backgrounds. On
dark surfaces (nearest data-mode="dark", or the system preference) it
now uses screen with lighter ink; --vb-stamp-blend overrides it for
custom stages.