Repository navigation
Releases: zweiundeins/starbase
Release list
v0.7.0
Ten new components, seven of them drag-and-drop and editing surfaces from derekr's PD rockets; TypeScript in the playground, with a type check on every run, and components written in TypeScript; and a licence on every component page.
Added
- Seven surfaces from derekr's PD rockets, under its Beer-Ware licence:
sb-sortable-list,sb-drag-group,sb-kanban-board,sb-sortable-tree,sb-context-menu,sb-bento-workspace(experimental) andsb-inline-edit. They work over server-rendered markup and emit what the user did (a move, a rename, a menu action) for the server to apply.go run ./cmd/vendorpdvendors a release with the fixes inpatches/pd-rocketsapplied, each ready to offer upstream, and their docs' demos are answered by the statelessGET /demo/arrange/{kind}.sb-kanban-boardalso moves lanes, by a grip, Alt and the arrows, or step buttons (sb-kanban-lane-move, with lane ids, so a server can refuse a stale move). - Three components that replace zweiundeins.gmbh's Lit components:
sb-image-compare, two pictures of the same size with a divider the reader drags, taps or moves with the keys, and an optional full screen view;sb-toc, a table of contents that marks the section being read, with reading progress and a compact bar for narrow screens; andsb-typewriter, text typed behind a blinking cursor, or a cycle of phrases, without moving anything around it. - A component can name its licence with
license:front matter; its page and its JSON-LD show it. - TypeScript in the playground: a switch turns
component.jsintocomponent.ts, which runs with its types stripped on the server, its errors reported at the lines written. TypeScript, and JavaScript that starts with// @ts-check, is checked by TypeScript on the server on every run, against the types of the patched Datastar build, and its problems are underlined in the editor. TypeScript snippets can't be submitted as components yet. The compiler, TypeScript's native port, is embedded in the binary; release archives and the container image carry its licence and notices. - Components can be written in TypeScript:
go tool task tstype-checks<slug>.tsstrictly, with helper modules and.d.tsdeclarations from its folder, and writes the committed.jsthat the site, the install snippets and the pinned versions serve.sb-code-editor,sb-data-table,sb-date-picker,sb-dropdown,sb-select,sb-treeandsb-virtual-scrollare TypeScript now (their modules are the compiler's output, so they get new versions that behave the same), and the playground opens them as TypeScript. - A pull request's preview of a component written in TypeScript opens its
.tssource with the TypeScript switch on, and the type check reads the modules it imports at that commit. sb-code-editor: highlights TypeScript (language="ts"), anddiagnosticsunderlines problems sent by the page or the server, the line under the code shows the one at the caret, and F8 and Shift+F8 move between them. A problem whose word has changed disappears, so an older list doesn't point at the wrong place.sb-code-playgroundhands its owndiagnosticsto its editors, per file.
Fixed
sb-code-playground: settingfileswhile the console had output threw, and an error thrown while the code loads now shows its line.- The playground's size line read "undefined" until the first edit when its modules came from the browser's cache.
sb-pixel-board: apalettebound to a signal (data-attr) on an element added after load overflowed the stack. It,sb-code-editor,sb-count-up,sb-gauge,sb-qr-codeandsb-relative-timeno longer make such a binding depend on their own state.sb-tree: selected rows and the open arrows stay visible in forced colours.- The gallery's "live demo" hint sits on the preview's top edge instead of covering the first item of a demo, and screen readers skip it.
v0.6.0
Time of day and time zones in the date picker; rich cells, a column picker and exports in the data table; keyboard focus that survives the virtual scroll's windows; copying without the Clipboard API; a shadow token for every overlay; and four more Rocket fixes, filed upstream.
Added
sb-relative-timetakestime-zone, an IANA name for the date on hover, the threshold date and the day count, so a page that shows its times in the server's zone says "yesterday" by the server's calendar. The date on hover followstitle-lang(autois the browser's language), andtitle-style="numeric"shows it as numbers with seconds, liketoLocaleString().- Every overlay's drop shadow comes from
--sb-shadow-overlay, so a theme can soften or drop them all:sb-popover,sb-select's list,sb-dropdown's menu,sb-date-picker's calendar,sb-data-table's column picker,sb-modal,sb-drawer,sb-tooltip,sb-toastandsb-theme-switch's menu. Its value is one shadow without spread (some of them draw it withdrop-shadow()), ornone; each keeps its own shadow when the token is unset. sb-date-pickertakestime: a time row under the calendar, insteps (minutes by default, down to seconds), and an Apply button that commits the date and the time together; a form gets the value like adatetime-localfield. Withtime-zone, it shows and reads the wall clock in an IANA zone and the value is an RFC 3339 instant with that zone's offset. Daylight saving gaps and overlaps resolve the way Temporal does.sb-data-tablecells can be rich: a link, a value with a muted suffix, or a badge ({value, text, suffix, href, tone}). Their text is never markup, and onlyhttp,httpsandmailtolinks are kept;sb-cell-activatereports a link's click.hidden-columnshides columns, which the server owns, andcolumn-pickerlets the user choose them (sb-columns, never pending).requestExport()emitssb-exportwith the order, the shown columns and the selection; the docs' star catalog answers it with a CSV or JSON download (GET /demo/data/rows/export, rate limited).
Changed
- The patched Rocket build has four more fixes, twelve in all (
patches/rocket). Each is filed upstream, with its repro page indocs/repro/.- #1224: a light component renders inside a
data-ignore-morphancestor instead of drawing nothing. - #1225: a removed element's mount root leaves the observed roots, so removed elements can be collected.
- #1226: a definition that arrives after Datastar's first pass still applies the shadow host's light children.
- #1227: the pending-host observer scans a parent once per batch, not once per moved child, so reordering 1,000 rows no longer takes seconds.
- #1224: a light component renders inside a
docs/repro/rocket-morph-ids/shows a quiet case of #1209: a Rocket element inserted ahead of keyed moves breaks them without throwing, and only 0008's restored id maps fix it. A comment for #1209 is drafted indocs/upstream/08, not posted.
Fixed
sb-copy-buttoncopies where the Clipboard API is missing or refuses (plain HTTP, a permissions policy): through a hidden textarea in its own shadow root, and as a last resort it selects the text in a panel and says which keys copy it.sb-copytells which way it went (method), andsb-copy-erroris cancelable; cancelling it keeps the old "failed" state.- Pages work over plain HTTP: the tab id comes from
crypto.getRandomValues, which non-secure contexts have, instead ofcrypto.randomUUID. sb-relative-time: where a daylight saving gap skips midnight (America/Santiago, America/Havana, Africa/Cairo), the day count turns at the jump instead of an hour before it.sb-virtual-scrollkeeps the keyboard focus on the same item when a window arrives. The morph patches the items by position, so the focused element showed another item afterwards, or the focus fell to the page when the morph removed it; now Tab and Shift+Tab walk the items one at a time across windows, also when pressed the moment a window lands, and over links in rows that have atabindexand over controls inside components (ansb-checkbox). An item scrolled out of the window leaves the focus on the scroller until a window brings it back, a new list that ends before the focused item gives the focus to its last item (an empty one to the scroller), and a control in theheaderslot takes the focus without scrolling the list.
v0.5.0
Seven new components (a drawer, a popover, checkboxes, a date picker, a virtual scroll and a data table), docs that show the real server code behind their demos, and Starbase's pages on a patched Rocket until its fixes land upstream.
Added
sb-drawer: a<dialog>panel that slides in from the start, end, top or bottom edge, modal or not (modal="false"), with aheaderslot, a scrolling body and a fixed footer. The server ownsopenas onsb-modal;show(),hide()andsb-open/sb-close {reason, value}are the page's.sb-popover: a panel anchored to a trigger, as a click dialog or a hover card (WCAG 1.4.13), in the top layer. It has 12 logical placements that flip and shift to stay on screen (CSS anchor positioning, with a JS fallback), an optional pixel arrow, and a server-ownedopen.sb-checkbox, a pixel checkbox with a label and a mixed state (indeterminate), andsb-checkbox-group, several choices as one JSON array value fromoptionsor<sb-check>children, with an optional select-all. Both follow the command contract and submit like native checkboxes.sb-date-picker: a date field with a calendar in a popover (orinline), for one ISO date or a{start, end}range committed as one value. It shows and reads dates in the page's language, starts the week on the locale's first day, takesmin,maxanddisabled-datesfrom the server, and emitssb-month, so a page can send each month's disabled dates when they are needed.sb-virtual-scroll: a list of any length that holds only the rows in view. The server renders each window as plain HTML (sb-window {offset, count}), and the list has a stickyheaderslot,columnsfor grids andscrollToIndex(). A server that answers fewer rows than asked for (a page-size cap) still fills the view.sb-data-table: a table that scrolls insb-virtual-scroll, with a sticky header, sorting, row selection as a command value (selected,confirm,revert()),sb-row-activateand the WAI-ARIA grid keyboard. A table that holds every row sorts it itself on a header click; one that holds a window asks the server (sb-sort).- The new layers close innermost first on Escape, nested in each other or in
sb-modal,sb-dropdownandsb-select. - Docs show the real server code behind their demos. A fenced block named by its source (
go source=internal/web/demo.go#Server.demoTelemetry) holds those declarations,go tool task listingsfills it, and a test fails when it no longer matches the code; on the site it links to the file. The docs ofsb-tree,sb-select,sb-gauge,sb-meter,sb-sparkline,sb-pixel-board,sb-virtual-scrollandsb-data-table, and "Commands and components" on the contribute page, have one. - Demo data for long lists: a million-star catalog computed from each star's index (
GET /demo/data/listrenders windows of it forsb-virtual-scroll), and its first 100,000 stars in SQLite, sortable by every column (GET /demo/data/rowsforsb-data-table; migration 009, seeded once at startup). - Every component pull request gets a playground link for each revision, not only the ones the submission bot opens.
Changed
- Starbase's pages run a patched build of Rocket: the v1.0.4 release with eight fixes, each filed upstream (
patches/rocket, built intostatic/vendor/datastar-rocket.jsbygo tool task vendor). Install snippets load it too, pinned at/c/datastar@<build>/datastar-rocket.jsand kept for good like component versions, so a page gets the same fixes; every component keeps working on the official release, and a patch goes once a Datastar release has its fix.- #1217: an effect that throws during disconnect no longer leaves the element dead.
- #1218: moving an element keeps its state instead of running its setup again.
- #1219:
boolprops follow HTML boolean attributes:checked="checked"reads as true, and false removes the attribute instead of writing"false". - #1220: components can be form-associated.
- #1221:
data-on:*__windowinside a shadow root goes with its element. - #1222: instances share one stylesheet per CSS text instead of parsing their own.
- #1223:
observePropshears an attribute write that decodes to the current value. - #1209: a morph that starts inside another (a Rocket element rendering while the morph connects it) no longer breaks the outer one, so elements with Rocket components inside can have ids. Upstream fixed it for its next release without a public commit, so this one is Starbase's own fix.
- Gallery cards have ids again: a sort or a search moves them with their live previews instead of morphing one card into another.
- Go modules updated, among them SQLite (modernc 1.60.1) and brotli 1.2.5.
- The README shows a screenshot of the site instead of a mockup.
Fixed
sb-modal: adata-sb-closebutton in ansb-drawernested in the modal closes only the drawer.- Long dashes are gone from the components' docs.
Known issues
- On the official v1.0.4, ids on repeated or reordered elements that contain Rocket components still break Datastar's morph (#1209). The build the install snippets load has the fix; with the official release, leave the ids off until a release has it.
- The form-like components are not form-associated yet (#1220): validity and
required,<fieldset disabled>,<label for>and theformattribute don't reach them. The patched build can declare form association, but the components keep joining forms with listeners until a release can, so they behave the same on the official release.
v0.4.0
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, themanifest:block passed torocket(), 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.jsonis now published from the readable modules. sb-code-editorloads 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-dropdownshowed 15.8 kB "with deps" for a 7.9 kB module) or a file only the docs' examples load (sb-autoloader'sdemo-badge.js). - Altogether,
/c/bundle.jsshrinks 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=""(orvalue="0",checked="false",open="false") sent to an element rendered without that attribute now wins insb-code-editor,sb-dropdown,sb-input,sb-radio-group,sb-rating,sb-select,sb-slider,sb-toggleandsb-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-switchandsb-toggle. Some of them used to vanish. - The
hiddenattribute hides every component, also on pages without a[hidden]reset: most set their owndisplay, which beat the browser's rule. disabled="false"(whatel.disabled = falsewrites) no longer dims a component or blocks its clicks:sb-button(andloading="false"),sb-dropdown,sb-radio-group,sb-rating,sb-select,sb-sliderandsb-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-toggleandsb-tooltip. For example,sb-ratingfills half units from the start side,sb-tooltipcentres top and bottom tips on the trigger, andsb-odometerkeeps its digits in left-to-right order. - Canvas components follow every theme change:
sb-gaugeandsb-sparklinerepaint 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 stillsb-nebulaorsb-starfield(speed 0 or reduced motion) repaints on a theme change or when the system turns light or dark, andsb-echarts' pie and funnel labels follow the theme too. - A removed
sb-echarts,sb-gaugeorsb-sparklineno 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 blankssb-count-up,sb-echarts,sb-odometerorsb-relative-time: an underscore is read as a hyphen (de_DEis German), and other invalid tags fall back to the browser's locale. The first three also find the page'slangfrom inside another component's shadow root, andsb-echartsfollows a changedlang. - 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-raisedinsb-dropdownandsb-toast,--sb-surface-hoverinsb-button,--sb-dangerinsb-copy-buttonand--sb-radius-sm(6px) insb-treehad 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-sliderandsb-togglesubmit their value under theirnamethe way their native counterparts do (a toggle sendsname=ononly 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'scontentType: '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 theformattribute don't reach them; each README's Forms section says what works.sb-inputandsb-code-editorget adisabledprop.sb-treestays commands-only, andsb-buttonneither submits nor resets a form. sb-tabsemitsinputon every move, andchange,sb-changeandsb-tab-changeonly on commit (the last two fired on every arrow key).sb-change's detail now carries the tab'slabel. 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-inputtakesrev: 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-busyno longer setsaria-busyon 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-modalignores a removedopenattribute, like the other server-driven attributes: to close, the server sendsopen="false". Inline panels no longer show a close button that did nothing, the close button is partclose, and the corners follow the theme's--sb-radius.sb-meter's default warn and danger thresholds are 70% and 90% of itsmin–maxrange instead of the absolute values 70 and 90 (the same on 0–100). Its tone is:state(ok|warn|danger), and its blocks are partsegment, lit ones alsolit.sb-dropdown'smenupart (withrole="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-toastidentifies a toast without anidby its message (#2,#3… for repeats), which is the idsb-dismissreports, so dismissing the first plain-string toast no longer hides the next one.sb-dismissfires after the toast is hidden locally.- New hooks:
sb-alerthas aniconslot (in place of the status light) and:state(closed), andhost.openreads and sets whether it shows;sb-qr-code's "Too much data" is anerrorslot in anerrorpart, to translate and restyle it;sb-sparklineandsb-starfieldtake alabelfor their accessible name (label=""makes a starfield decoration);sb-range's thumbs follow--sb-slider-thumband--sb-slider-thumb-edge, likesb-slider's;sb-tooltipstacks at--sb-z-tooltip(default 70).
Fixed
sb-alertcomes back when the server changes itsopenattribute 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'ssb-readyandel.readywait until every component on the page at startup has loaded or failed (they fired at the timeout, with a wrongloadedcount;timeoutnow 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, andel.load(tag)andel.discover(root)return promises.sb-busyno 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 lastsb-busy-change { busy: false }.sb-buttonno 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-labelreaches the inner button or link, so icon-only buttons have a name, and slotted icons no longer shrink to nothing.sb-cardhas a width of its own in flex rows,autogrid 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 (confirmis no longer pending on load, andrevert()no longer empties the editor), edits survive a move, and the gutter ...
v0.3.0
Nine new components (35 in total), leaner delivery (minified modules, one precompressed bundle, brotli -11 everywhere), installation as tabs with pinned integrity, a Styling section for every component, and live sizes in the playground.
Added
- 9 new components (26 → 35; 34 in the gallery, since
sb-code-playgroundis now unlisted):sb-dropdown: an actions menu whose items are commands (sb-select {name, value}), with submenus up to five levels, and radio groups for a menu that shows a current choice, also in its trigger. A nativepopoverin the top layer, placed with CSS anchor positioning.sb-radio-group: one choice out of a handful, with pixel radios, under the command contract. Choices come fromsb-radiochildren or from the server'soptions, and the keyboard stays put when the server drops the focused choice.sb-details: a disclosure that animates to its content's height, alone or as an exclusive accordion (group), built on a native<details>.openis view state the server may own.sb-toast: a toast region the server fills. It stacks, announces and dismisses the server's list, while closing, countdowns and pausing stay with the viewer, so a morph never brings back a closed toast.sb-busy: a spinner, bar or skeleton that shows itself while the Datastar requests it watches are in flight (no signal needed), or while the server saysbusy.sb-odometer: a number that rolls its digit wheels to each new value. Only the digits that change turn, each the way the number moves (over the top when it climbs), separators follow the locale, anddrumdraws the wheels as 3D cylinders. Screen readers get the value once, as text. From Libretto's live-drive readout.sb-echarts: Apache ECharts driven by an option the server sends as JSON. Colours come from the--sb-*tokens and follow a theme change without restarting, numbers and dates follow the page's language, and a slotted table is the fallback for readers without the chart. ECharts 6.1.0 is vendored unmodified from npm and loaded only when a chart comes near the screen. From Libretto, where it draws every chart.sb-count-up: counts a number up the first time it is on screen, and on from where it stands when the server sends a new one. The server's text inside is what search engines and people without JavaScript see. From libretto.ch.sb-autoloader: the site's autoloader as a component, for any web components: a tag map and/or a URL pattern, loading each tag the first time it appears (morphs included), with dependencies and an optional un-cloak.
- The playground shows what your component weighs while you type: its
component.jsminified and compressed at brotli -11 (what a page downloads), with the components it renders and the files it imports, plus the minified and raw sizes. The server measures each edit with the same esbuild and brotli settings as the catalog, so the numbers match the component pages' size tables. When the code doesn't parse, the last good numbers stay, dimmed. On a narrow screen the line keeps only the downloaded size.sb-code-playgroundtakes extra content for its top bar (slot="bar"). - Every component's docs have a Styling section, like
sb-gauge's: size, fonts, colour tokens and parts, reachable from the page's CSS (no shadowmodechanges or@imports), with a live example. A test checks that every::part()and--sb-…property a section names exists in the component's source.sb-echarts' Theming section is folded into it. - More styling hooks, each documented in its Styling section and unchanged by default:
sb-cardhas aheadingpart,sb-alertheadingandmessage; the selected tab (sb-tabs) and row (sb-tree) are also partselected, following the selection;sb-theme-switch's options are partoption(option selectedwhen chosen);--sb-code-editor-font-size,--sb-slider-thumb/--sb-slider-thumb-edge,--sb-toggle-knoband--sb-pixel-board-grid;sb-copy-button's icon scales with the button. sb-buttontakesloading: an inline pixel spinner (the same onesb-busydraws), clicks and Enter blocked against double submits,aria-busy, and focus kept. It costs no space until it is loading; bind it todata-indicator.sb-theme-switchtakes adomain, so one theme choice can cover every subdomain. A domain the browser refuses falls back to this host instead of dropping the choice silently.sb-theme-changecarriesscheme("light"or"dark"): what the page now paints in, worked out from the theme's owncolor-scheme, so code that draws needs no list of theme names. The theme switch's docs gain a section on following the theme from a canvas.
Changed
- Components follow your page's font. Where one uses a font token (
sb-gauge's value,sb-button variant="pixel",sb-echarts), it now falls back to the page's font instead of a hard-coded monospace, so outside Starbase they match the page without any configuration.sb-gauge's value and label now scale with--sb-gauge-size, and its docs have a Styling section. - Pages load every component as one file (
/c/bundle.js, 66 kB) instead of one module per component. Measured on production over slow 4G, a visit that includes the homepage is 250–450 ms faster, at the cost of about 265 ms for a visitor who only sees one component page. Once the bundle grows past 100 kB (brotli), pages go back to the autoloader on their own. - The Themes page remembers the previewed theme and the 8-bit switch for the session: they carry across pages and browser tabs, and render from the first paint. The gallery's sort is a session default too: a sort in the URL still wins (shared links show what was shared); without one, the gallery uses the last sort you chose. Search and category keep the sort and never change the default.
- Installation snippets end with a copyable
usage:instead of the gallery card'spreview:. It is a new optional front-matter field: the smallest markup that shows how you'd use the component in your own page (no card sizing, demo signals or timers, forcedopen, or images that only exist on Starbase), withpreview:as the fallback. 30 components have one; a copiedsb-theme-switch, for one, now themes the page instead of a demo attribute. A test keeps demo wiring and Starbase-only paths out of every component's installation markup. - Assets are served precompressed at brotli -11 (gzip -9 as the fallback) instead of being compressed per request at level 5: every component module, the autoloader, the CSS bundles, the vendored Datastar build and the art. The size tables now show exactly the bytes a browser receives.
- Installation on component pages is four tabs instead of one long block: Autoloader (the default), This component (its minified module pinned with its frozen integrity, plus every component it renders), Pinned (today's catalog snapshot with an integrity import map) and Self-host (each file's minified and readable link with its size, and an import map at your own Datastar). Each tab has one sentence and a snippet that is exactly what to paste, with its own copy button. The chosen tab is remembered for the session, across component pages and browser tabs, through a new kind of state: session preferences (
session_prefs), for choices that should outlive a page. - Component modules are minified (esbuild) next to the readable source: every
x.jshas anx.min.js, and the autoloader and the site load those — about 23% less over the wire across the catalog. The readable file stays the default URL for the docs and the playground. Minified bytes are frozen per version, so an esbuild upgrade can never change a pinned URL. Size tables add a minified column, and gallery cards show the minified size. sb-code-playgroundis no longer listed in the gallery: it is the machinery behind/playgroundrather than a community component. It stays served, versioned and documented, through a newunlisted:front-matter flag.- The main domain is now starbase.zweiundeins.gmbh; rocket.libretto.ch redirects there.
- Live demos stand on a pixel grid: in the gallery the preview panel is visibly the component itself (and says "live demo" on hover), which is why it is the one part of a card that isn't the link to the component page. Demo stages in the docs use the same grid.
Fixed
sb-code-editor's highlighted text no longer drifts away from the caret and the line numbers: the<code>inside it had the browser's own monospace font, a second font that made every line 1 px taller (two rows off after 40 lines). It takes the editor's font, size and line height now, and a browser test (TestCodeEditorLinesAlign) measures the first and the last line against the caret and the gutter at two sizes.sb-theme-switch's select arrow follows the theme (it was a fixed light grey, faint on light themes), andsb-pixel-board's grid lines follow the blank cell colour, so they show on a light board.sb-gauge,sb-sparklineandsb-echartsno longer pull insb-theme-switch: a comment mentioning it was read as a dependency, so the autoloader loaded it, the size tables counted it and the install tab added a script for it. Dependencies are now found in the minified module, where comments are gone.- Installation tabs: "This component" now pins everything it loads through the import map's
integrity(a script tag covers only its own file, so code-editor's Prism was unchecked), and already-minified vendored files are no longer renamed to a non-existent.min.min.js— which had dropped ECharts from the Pinned tab's integrity and broken its Self-host link. sb-echartswrote a missing value as “undefined” in tooltips and labels (its number formatter stringified whatever it was given); it now writes “-”, as ECharts does. Axis tooltips also leave out the series that have no value at the hovered point – a projection before...
v0.2.0
Eight new components (26 in total), a command contract that makes every value component CQRS-ready, continuous deployment, and a security, performance and SEO pass.
Added
- 8 new components (18 → 26):
sb-nebula: a drifting WebGL nebula in dithered pixels. The first component that came in through the submission bot.sb-theme-switch: auto, dark or light (or any themes), as radio buttons, a select or a header menu. It remembers the choice in a cookie, so the server renders the theme before the first paint (no flash). The Starbase header uses it for the site's own themes, with "auto" following the system (Deep Space or Daylight).sb-rating: pixel hearts or stars for a score, with half steps, hover preview and keys.sb-relative-time: "3 minutes ago" that stays current, in any language, with a server fallback.sb-qr-code: pixel QR codes for any text or URL, with optional brand-coloured corners (uqr, vendored and verified).sb-tree: a keyboard-friendly tree whose branches load lazily from the server;expandedandloadedare props the server can morph.sb-select: filter, pick several, or autocomplete from the server through aresultsprop.sb-range: a two-thumb slider whose{start, end}is one value, committed as one command.
- Command contract for value components (input, slider, toggle, code editor, tabs, rating, tree, select, range): a
nameprop andsb-change {name, value}on commit, ready to post as a command. Withconfirm,:state(pending)marks an edit the server hasn't confirmed, andrevert()returns to the server's value after a rejected command. The Showcase demonstrates the loop (pending, a server-normalized value, a rejected command). - Example dataset (a seeded universe of galaxies, systems, planets and moons) with generic demo endpoints,
/demo/data/childrenand/demo/data/search, for docs and playground demos (into=names the signal,delay=simulates latency). - Component sizes: gallery cards show the download size (brotli, including the components it renders), and component pages have a Size table with original, gzip and brotli sizes for each file and each rendered component, plus a total. Datastar and Rocket are not counted.
- Continuous deployment: after CI passes on
main, the Deploy workflow ships the binary over a single-purpose SSH key tostarbase-deploy, which verifies it, checks/healthzand rolls back if the new version isn't healthy. The unit, env and Caddy files are indeploy/. - Vendored libraries: a submission from a repository brings along every file the component imports relatively (up to 2 MB each), listed with its license banner in the PR. Imports are checked strictly:
'datastar'or files in the component's folder, never URLs or bare packages. - Submission pull requests get a playground preview link for every revision (
/playground?preview=<commit>/<slug>) and a list of similar existing components, to spot duplicates before merging. When the pull request step fails, the bot tells the author on the issue. - Opt out of the 8-bit look:
--sb-notchanddata-sb-style="smooth"turn off pixel corners, notched frames and the pixel display font. The Themes page has a switch. - SEO:
robots.txt, a sitemap, canonical URLs, Open Graph and Twitter cards, JSON-LD (WebSitewith search,SoftwareSourceCodeper component) and PNG icons rendered from the pixel art. - Contribute guidelines for component authors: the command contract, components with several values (one structured
valueper decision, view state and server data as separate props,sb-<verb>operations), and no optimistic updates (server results only come from the server's render; the user's own input stays, marked pending).
Changed
- The server can set every component's value: a
value/checkedattribute the server changes wins over local edits, while re-sent identical markup leaves edits alone. Removed attributes are ignored (morphs also strip reflected ones), so to clear, sendvalue=""/checked="false". The manual pre-upgrade property adoption and the dirty flags are gone (Rocket replays those writes itself). sb-alerthas anopenprop, so the server can hide and re-show it.sb-theme-switchdraws its icons as CSS masks.- The logo and the hero scene follow the site theme: they are inlined as SVG, and their themeable colours are
--sb-art-*tokens. As standalone files (favicon) they keep the original colours. - Performance: brotli, zstd or gzip for pages and text assets; one minified, hashed CSS bundle; hashed, preloaded fonts; immutable static files; pixel art drawn as one path per colour; lazy playground preview frames, and no layout shift on
/playground. - GitHub Actions updated to their Node 24 (LTS) majors.
Fixed
sb-copy-button: a refused clipboard write (no secure context, no permission, unfocused document) was silently ignored; it now shows "Copy failed" (failed-label) and emitssb-copy-error {value, error}. The result is announced through arole="status"region outside the button, and the tip is readable on light themes and no longer cut off on code blocks.- Demo endpoints answer CORS preflights, so components in the playground sandbox (opaque origin) can load demo data with
@get, e.g. the lazy tree. - Playground: the preview theme picker applied the previous choice.
- The playground can run components that import their own files (e.g.
sb-code-editorand its vendored Prism): relative imports resolve against the component's folder. - Submissions from a playground link failed validation: the link was recorded as the component's
source:, which must be a GitHub repository. - The hero scene keeps its proportions at every width;
sb-theme-switchicons line up with their labels; light-theme contrast fixes.
Security
- Versioned, immutable component URLs (
/c/<slug>@<hash>/…) and catalog snapshots (/c/@<catalog>/autoloader.js), with SHA-384 integrity for every file (/c/@<catalog>/importmap.json). Sites can pin exactly what they reviewed; browsers refuse any file that changed. Old versions keep working after later deploys. - Vendored libraries are verified:
vendor.jsonnames each file's npm release, and the bot checks it byte for byte against the registry-verified tarball. Minified code that can't be verified is refused. - The Starbase service listens on a Unix socket and is denied all of localhost (other services on a shared host), with a fully hardened systemd unit.
starbase-deployasks a new binary its version inside a network-less sandbox with a timeout. - Pull request previews are limited to the repository's own submission branches;
script-srcis limited to/static/and/c/. - Rate limits for snippet saves and painting (per session and per IP), and a cap on total snippet storage.
- CI: actions pinned to commit SHAs (Dependabot keeps them current), job timeouts, Chrome's sandbox kept on for submitted code, the bot's write-capable job re-validates what the build job produced, CODEOWNERS for vendored code, CI/CD and deploy files, and tests that keep the production environment to
deploy.yml.
Known issues
- Datastar's morph is not re-entrant with Rocket components that are reordered by id: reported upstream as starfederation/datastar#1209, with a minimal reproduction and a fix in
docs/repro/rocket-morph-reentrancy/. Starbase avoids the pattern.
v0.1.0
The first release: a community gallery of Rocket web components for Datastar.
Added
- Gallery. Categories with counts, realtime full-text search (SQLite FTS5), sorting, and live previews. Per-tab filter state is kept on the server and mirrored in the URL.
- Component pages. Rendered docs with live
html previewexamples and copyable source, API tables generated from Rocket manifests, an auto-generated props Playground, installation snippets and "Edit on GitHub". - 18 components: button, input, slider, toggle, alert, modal, tooltip, meter, card, tabs, copy-button, code-editor (Prism with Rocket-aware highlighting), code-playground, voxel (a software 3D renderer), starfield, gauge, sparkline and pixel-board.
- Code playground. Edit a component's JavaScript and HTML with a sandboxed live preview (opaque-origin iframe), a console, theme switching, share links, and "Submit as component".
- Showcase. Mission Control, a dashboard driven by server-pushed signals (
/demo/telemetry), and a multiplayer pixel board with live presence. - Themes. An Open-Props-style token system (
--sb-*), with four themes that restyle every component through shadow DOM. - Submitting components without tools. A GitHub issue form (paste code, link a repository, or paste a playground link) and a bot that validates the component, generates its manifest in headless Chrome, and opens a pull request.
- Autoloader.
/c/autoloader.jsloads<sb-*>components on first use, follows morphs, knows dependencies between components, and offersreadyandsb-cloakagainst the flash of undefined elements. - Architecture. CQRS throughout: commands answered with 204, a single SQLite writer that batches commands, one render stream per tab that re-renders the whole page, and Brotli across frames. GitHub sign-in for stars.
- Tooling. Live reload (air),
task manifests, a component scaffolder, CI (tests, templ check, manifest freshness, govulncheck, cross-platform builds, container image) and a release workflow.
Known issues
- Datastar's morph is not re-entrant with Rocket components that are reordered by id. See
docs/repro/rocket-morph-reentrancy/for a minimal reproduction and a proposed upstream fix. Starbase avoids the pattern.