Skip to content

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 04:48
· 202 commits to main since this release

A review of all 35 components: smaller downloads (the bundle every page loads is 22% smaller), about a hundred bugs fixed, form participation for the form-like components, and browser tests for the value contract, forms and shrinking.

Changed

  • Component modules are shrunk before they are minified: the .docs() text, the manifest: block passed to rocket(), and comments and needless whitespace in CSS and HTML templates no longer ship. Modules are 18% smaller and the bundle 16%. Every component gets a new version once (the shrinking is part of the version hash), so pinned URLs keep working and the new files replace nothing. manifest.json is now published from the readable modules.
  • sb-code-editor loads Prism only when an editor highlights, so the bundle every page loads is 7.2 kB smaller; code shows uncoloured until it arrives. Prism is rebuilt from its language components (0.9 kB less, identical highlighting).
  • Size tables and the playground's size line no longer count a component as its own dependency (sb-dropdown showed 15.8 kB "with deps" for a 7.9 kB module) or a file only the docs' examples load (sb-autoloader's demo-badge.js).
  • Altogether, /c/bundle.js shrinks from 67.1 kB to 52.7 kB (brotli) and the catalog's modules from 86.2 kB to 75.1 kB, while fixing everything below and adding form participation.
  • The server can clear a value it never set: value="" (or value="0", checked="false", open="false") sent to an element rendered without that attribute now wins in sb-code-editor, sb-dropdown, sb-input, sb-radio-group, sb-rating, sb-select, sb-slider, sb-toggle and sb-tree; Rocket used to ignore it, since the decoded value didn't change. A removed attribute is still ignored, and a browser test checks both for every value component.
  • Forced colours (Windows High Contrast): focus rings, carets, checks, tracks, icons, status lights and the chosen item stay visible in sb-alert, sb-busy, sb-button, sb-card, sb-code-playground, sb-copy-button, sb-details, sb-dropdown, sb-input, sb-meter, sb-pixel-board, sb-radio-group, sb-select, sb-slider, sb-tabs, sb-theme-switch and sb-toggle. Some of them used to vanish.
  • The hidden attribute hides every component, also on pages without a [hidden] reset: most set their own display, which beat the browser's rule.
  • disabled="false" (what el.disabled = false writes) no longer dims a component or blocks its clicks: sb-button (and loading="false"), sb-dropdown, sb-radio-group, sb-rating, sb-select, sb-slider and sb-toggle.
  • Right-to-left pages: fills, carets, focus bars, close buttons, tips and arrow keys follow the reading direction in sb-alert, sb-busy, sb-copy-button, sb-details, sb-dropdown, sb-input, sb-odometer, sb-radio-group, sb-range, sb-rating, sb-select, sb-slider, sb-tabs, sb-theme-switch, sb-toast, sb-toggle and sb-tooltip. For example, sb-rating fills half units from the start side, sb-tooltip centres top and bottom tips on the trigger, and sb-odometer keeps its digits in left-to-right order.
  • Canvas components follow every theme change: sb-gauge and sb-sparkline repaint whenever a colour token changes, from any cause (a theme scoped to a container, a class, light-dark(), forced colours), not only on a theme-switch pick. A still sb-nebula or sb-starfield (speed 0 or reduced motion) repaints on a theme change or when the system turns light or dark, and sb-echarts' pie and funnel labels follow the theme too.
  • A removed sb-echarts, sb-gauge or sb-sparkline no longer throws on every later theme change: their window listeners outlived the element.
  • An invalid lang (en_US, de_DE, a stray space) no longer blanks sb-count-up, sb-echarts, sb-odometer or sb-relative-time: an underscore is read as a hyphen (de_DE is German), and other invalid tags fall back to the browser's locale. The first three also find the page's lang from inside another component's shadow root, and sb-echarts follows a changed lang.
  • A token's fallback (what a component uses on a page without Starbase's tokens) is the same in every component, and a test keeps it so: --sb-surface-raised in sb-dropdown and sb-toast, --sb-surface-hover in sb-button, --sb-danger in sb-copy-button and --sb-radius-sm (6px) in sb-tree had their own.
  • Form-like components take part in the <form> they sit in: sb-code-editor, sb-input, sb-radio-group, sb-range, sb-rating, sb-select, sb-slider and sb-toggle submit their value under their name the way their native counterparts do (a toggle sends name=on only when on, a multiple select one entry per pick, a radio group nothing until a choice is picked, a range its {start, end} JSON), so native submits, new FormData(form) and Datastar's contentType: 'form' include them, and a form reset brings back the server's value (a reset the page cancels leaves them alone). They are not form-associated elements yet, because Rocket can't declare one, so validity, <fieldset disabled>, <label for> and the form attribute don't reach them; each README's Forms section says what works. sb-input and sb-code-editor get a disabled prop. sb-tree stays commands-only, and sb-button neither submits nor resets a form.
  • sb-tabs emits input on every move, and change, sb-change and sb-tab-change only on commit (the last two fired on every arrow key). sb-change's detail now carries the tab's label. A click commits at once, the arrow keys, Home and End after a 250 ms pause, so running through the tabs sends one command instead of one per tab.
  • sb-input takes rev: when it changes, the server's value wins even if it is the same, so a command the server normalises back to the stored value (or ignores) no longer leaves the field pending. The Showcase call sign uses it.
  • sb-busy no longer sets aria-busy on itself: on an ancestor of its status region it could hold the announcement back, and a morph stripped it. Set it on the content that is loading; :state(busy) stays the styling hook.
  • sb-modal ignores a removed open attribute, like the other server-driven attributes: to close, the server sends open="false". Inline panels no longer show a close button that did nothing, the close button is part close, and the corners follow the theme's --sb-radius.
  • sb-meter's default warn and danger thresholds are 70% and 90% of its min–max range instead of the absolute values 70 and 90 (the same on 0–100). Its tone is :state(ok|warn|danger), and its blocks are part segment, lit ones also lit.
  • sb-dropdown's menu part (with role="menu") is now the panel inside each level's popover, which casts the drop shadow. Its Styling docs set tokens on the element, since a token set on ::part(menu) never reached the panel.
  • sb-toast identifies a toast without an id by its message (#2, #3… for repeats), which is the id sb-dismiss reports, so dismissing the first plain-string toast no longer hides the next one. sb-dismiss fires after the toast is hidden locally.
  • New hooks: sb-alert has an icon slot (in place of the status light) and :state(closed), and host.open reads and sets whether it shows; sb-qr-code's "Too much data" is an error slot in an error part, to translate and restyle it; sb-sparkline and sb-starfield take a label for their accessible name (label="" makes a starfield decoration); sb-range's thumbs follow --sb-slider-thumb and --sb-slider-thumb-edge, like sb-slider's; sb-tooltip stacks at --sb-z-tooltip (default 70).

Fixed

  • sb-alert comes back when the server changes its open attribute after a dismissal (unchanged markup keeps the dismissal), and a closed alert no longer leaves an empty gap in a grid or flex stack. Headings meet WCAG AA contrast on light themes, and long URLs wrap instead of pushing the close button out of the box.
  • sb-autoloader's sb-ready and el.ready wait until every component on the page at startup has loaded or failed (they fired at the timeout, with a wrong loaded count; timeout now only lifts the cloak), once per loader, also after a morph moves it. A module that doesn't define its tag is reported (sb-load-error) instead of blocking that tag for good, a failed module is fetched again when its tag reappears, and el.load(tag) and el.discover(root) return promises.
  • sb-busy no longer stays up forever when the element that fired a request is replaced or removed before the response (a Delete button whose row the response removes). Its label is announced when a wait starts, and one removed while up emits a last sb-busy-change { busy: false }.
  • sb-button no longer traps the keyboard while loading (Tab, Escape and page shortcuts work; clicks, Enter and Space stay blocked). A disabled button ignores clicks, el.click() included, and they no longer reach the element behind it. aria-label reaches the inner button or link, so icon-only buttons have a name, and slotted icons no longer shrink to nothing.
  • sb-card has a width of its own in flex rows, auto grid columns and fit-content layouts (it collapsed to 0 px), keeps inline markup in the flow of the text (block children now flow like a <div>, with their own margins), and collapses an empty body. It no longer clips tooltips that reach outside it or makes photos jagged, and in a linked card, footer actions and body links and buttons get their own clicks, and keyboard focus rings the whole card.
  • sb-code-editor: Shift+Tab without a selection no longer selects the start of the line (the next keystroke deleted it), and Enter or Tab that confirm IME input are left to the IME. Code from a child <script type="text/plain"> is the server's value (confirm is no longer pending on load, and revert() no longer empties the editor), edits survive a move, and the gutter keeps its width when a line overflows, so the code no longer jumps sideways while typing.
  • sb-code-playground: an endless loop no longer kills the preview (on a page with several playgrounds, every preview); the runner stops it after a second and names the line. Runs no longer add browser history, 2000 console.log lines settle in well under a second instead of about 10 s, a fresh console no longer shows the previous run's lines, the editors' change and sb-change no longer escape, and the file tabs work with the keyboard.
  • sb-copy-button re-renders only when label changes, not on every new value (a bound value can change on every keystroke), stays quiet when it is removed during a pending clipboard write, and its hover edge no longer fades on light themes.
  • sb-count-up no longer counts again from from when it is moved, prints and saves to PDF with its final value instead of 0, and keeps room for its final value, so the text around it stays put while it counts.
  • sb-details keeps its open state when app code moves it, like a native <details>, and panels in one group are exclusive from the first paint. Where interpolate-size is unsupported it opens and closes at once instead of stalling and then snapping.
  • sb-dropdown: clicking a parent whose submenu opened on hover (or pressing Enter on it) keeps the submenu open and moves the focus into it, the focus follows the pointer so Enter chooses the row under it, type-ahead keeps the whole word and cycles on a repeated letter, and the drop shadow shows. A menu moved or removed while open sends sb-close { reason: "api" }.
  • sb-echarts: defineChartKind and setNumberFormat work from any copy of the module (the README recipe failed with every install method), the unversioned /c/echarts/ route serves the vendored ECharts file the readable module imports, and a chart whose kind isn't defined yet shows its fallback until it is. Axis tooltips show each series' own value for dataset options, the default legend and a slider dataZoom no longer cover the x-axis labels, resizing no longer breaks multi-grid layouts, polar, calendar and geo charts lose their phantom axes, and an option ECharts can't draw no longer blocks every later one. Charts without a fallback can be used from the keyboard: the arrow keys step through items with their tooltips, and Enter fires sb-chart-click.
  • sb-gauge stops animating offscreen (live gauges repainted at 60 fps after you scrolled away), follows new min and max at once with aria-valuenow kept in range, and lights the bottom row at both ends of its arc.
  • sb-input: Enter commits the value (change and sb-change), so a command runs on Enter and not only on blur, but not the Enter that confirms an input-method word. A new server value, revert() or a property write re-checks the field (a stale error stayed up), and a move keeps unsaved text with its pending and error state. Only the label names the field; the hint and the error describe it, and errors are announced politely.
  • sb-meter's cascade starts at the edge that moves, so a long bar no longer lags and a falling or fast-changing one leaves no lit blocks behind; any value above min lights a block, and only max lights them all. It is the meter itself for assistive technology, named by a host aria-label, and its label and number are no longer read twice.
  • sb-modal no longer turns into an invisible modal that blocks the whole page when the server opens it after the first render or a prop changes while it is open, and switching inline at runtime no longer crashes. A data-sb-close button in a nested modal closes only its own dialog, a text selection released over the backdrop no longer closes it, and the server can close a dialog opened with show().
  • sb-nebula stays alive when it is moved in the DOM (it went blank for good), no longer flashes black while resized, and shows its still glow after losing its WebGL context. Any seed gives a sky (seeds wrap at 101; 0 to 100 are unchanged), light themes keep a dark sky instead of black blotches, and label="" makes it decoration.
  • sb-odometer: below zero the wheels turn the way the digits count (−15 → −13 rolls back two instead of on through eight), a value that arrives mid-turn rolls on from where the wheel is instead of jumping, copying a selection copies the value instead of every wheel's digits, and drum="false" draws flat wheels.
  • sb-pixel-board keeps working, drawing included, after a morph or re-attach (removing it threw, and so did every later paint). It paints only with the primary button of one pointer (a right click painted, and two fingers drew a line), sends at most 60 cells per sb-paint, lets a finger scroll the page over a readonly board, and its palette is one tab stop. The README's example uses requestCancellation: 'disabled', so a copy no longer drops pixels mid-stroke.
  • sb-qr-code with accent caps the corner squares' lightness, so codes scan on themes with a light brand colour (Terminal's green decoded in 0 of 8 tries). Text with a lone surrogate (cut mid-emoji) encodes instead of showing "Too much data", and a fractional border is rounded to whole modules instead of clipping the code.
  • sb-radio-group no longer takes the focus back (and scrolls the page to itself) after a click elsewhere, and its disabled choices can no longer take the tab stop from a click. It is the radiogroup itself, so a page's aria-label or aria-labelledby names it, and arrow keys with Alt, Ctrl or Meta are left to the browser.
  • sb-range's track and fill no longer disappear after a prop change or a confirmed command. Values snap to step like the thumbs, a server update of min/max with value keeps the server's range in either attribute order, a null start or end means min/max (it collapsed to 0), thumbs that meet can be dragged apart either way, and the focused thumb shows a ring.
  • sb-rating with clearable no longer clears when an arrow key or End is pressed at the maximum, and a change shows at once while the pointer rests on the row. On touch a vertical swipe scrolls the page and a drag along the row picks a value, a server update that raises max with value keeps the value, and a readonly rating announces its value ("4.5 of 5").
  • sb-relative-time no longer goes stale on future times ("in 1 hour" stayed until only half an hour was left), counts days by calendar date in the viewer's time zone (so "yesterday" is the date before today), switches at threshold in both directions, says "1 year ago" from about 350 days, and reads datetime="2026" as a year, not Unix seconds.
  • sb-select: a disabled select is out of the tab order and ignores keys, an empty one shows no clear button, and reading its value no longer makes it pending. A remote select no longer lists old results under "Type to search" or spins forever when an answer changes nothing. Without searchable or remote it opens and picks with Space and jumps to typed letters, its status messages are announced, and removing a focused select no longer throws.
  • sb-slider no longer loses its track after a re-render (a server echo, a change to unit, disabled or ticks), and with confirm a held arrow key no longer loses steps to older echoes. It has an accessible name (label or aria-label), its value text and fill follow unit, step, min and max, and server values snap to step like the thumb.
  • sb-sparkline's height no longer depends on length (the line is --sb-sparkline-height tall, 2.25rem by default, with square pixels), its area sits under the stepped line, and toggling show-value no longer blanks it. Push mode ignores a removed or re-sent value, keeps its history across a move, and trims when length shrinks.
  • sb-starfield's still frame (speed 0 or reduced motion) shows every star and no longer adds stars on each repaint, and it no longer blanks while it is resized.
  • sb-tabs shows its focus ring on the selected tab, and a selected past the last tab selects the last one (none was selected, which took the tab list out of the tab order). el.selected is the live index and a write no longer reflects to the attribute (a morph reset it), so data-bind works. Panel slots keep letters and digits in any script, the strip no longer overflows its container by 10 px, and the host's aria-label names the tab list.
  • sb-theme-switch: removing or moving a switch no longer throws, the menu's arrow keys apply each theme without closing it, and focus goes back to the button. A switch whose list lacks the saved theme no longer resets the page to auto, it follows later themes and labels changes, and the menu opens centred instead of in the top-left corner in browsers without anchor positioning.
  • sb-toast's countdown bar shows the real remaining time, new toasts enter at top placements and when the stack is full, and a dismissed toast fades out even when the page drops it at once. Dismissing with the mouse no longer pauses every other countdown, keyboard focus stays with its toast as the stack changes, clear() also dismisses toasts waiting behind max, and the drop shadow shows.
  • sb-toggle's label takes the colour of the text around it (it was unreadable on light pages without Starbase's tokens), an on toggle's knob uses --sb-text-on-brand and stays readable on the brand track (Terminal: 1.28:1 → 12.46:1), aria-label names the switch, and a moved toggle keeps its state and pending mark.
  • sb-tooltip's hidden tips are no longer read as stray text, and a shown tip is announced. Escape dismisses it wherever the focus is, the pointer passing over a focused trigger no longer hides its tip, and a shown tip takes the pointer, so it can be hovered (WCAG 1.4.13) and covers what is under it. A tooltip without content no longer shows an empty box.
  • sb-tree no longer takes the focus back (and scrolls the page to itself) when the server updates it after a click elsewhere, puts Tab on the selected item instead of the first row, and no longer stays "Loading" for good when a lazy branch's children arrive in items. In multiple mode the same selection in another order is no longer pending, and choosing the selected item again sends nothing.
  • sb-voxel: a new yaw or pitch from the page replaces the user's turn, so sliders and a write-back of sb-orbit stay in step. Only the first finger and the main mouse button drag (a pinch or a right click spun the model), sb-orbit fires once per turn, the pitch stops at ±89° without a dead zone, and the planet draws about 30% faster. In its docs and its Playground, the angle sliders follow a drag (a new sync: option in a component's playground: front matter).
  • Component pages: switching a boolean control off in the Playground switches the preview off too (e.g. sb-toggle's checked), so it matches the markup shown.
  • The site's autoloader no longer waits forever for a module that loads but never defines its tag: after 10 s it reports it, so ready and the cloak don't hang on it. Mission Control's altitude chart has an accessible name, and its starfield is decoration for screen readers.

Known issues

  • Datastar's morph is not re-entrant with Rocket components that are reordered by id (starfederation/datastar#1209, still open). Starbase avoids the pattern.
  • The form-like components are not form-associated yet (Rocket can't declare it): validity and required, <fieldset disabled>, <label for> and the form attribute don't reach them.
  • Rocket's observeProps ignores an attribute write whose decoded value is unchanged; the value components watch their value attribute themselves. A reproduction is in docs/repro/rocket-observeprops-equal-value/.