Repository navigation
release: nebula 0.2.2
Count the components the README lists
The introduction and the component list still said sixty-nine, from before
AccessibleIcon, Image, VisuallyHidden, Picture and Toolbar landed; the
atom and molecule counts and the test count under Development were stale
the same way.
Correct the last two stale parity lines
The parity section still said Aurora has no context (it has, and these
components use it) and that Toaster has four variants and no promise
toasts. Both were fixed further down the file and not here.
Say why compound components take data, correctly
Three places justified the data-driven API with "Aurora has no context".
Aurora has createContext / provide / inject, and these components use it —
Tabs provides a TabsApi that TabsTrigger and TabsContent inject. The
README used Tabs as the example while saying the opposite.
The real constraint was already written down in lib/children.ts: context
lives on a stack during a component's SETUP, which is synchronous. A part
handed in as a Slot thunk is built later, by the renderer, once that frame
has popped — so <Tabs><TabsList><TabsTrigger>, where a child reads the
parent it was nested inside, would read nothing. Taking data keeps every
part built while the context is still there.
It matters because the false premise pointed the next contributor at "just
use context" and hid the wall they would actually hit.
Give the toast an outcome: promise, update and dedup by id
shadcn ships two notification components: sonner, which wraps a
third-party library, and toast, its own, rebuilt on Base UI. The one this
follows is the second, and the gap against it was real — no way to report
an async call, which is most of what the pattern is for.
Added: toast.promise(work, { loading, success, error }), a loading variant
that has no countdown of its own because it is waiting for an outcome,
update(id, patch) which recomputes that countdown, a caller-supplied id
so showing the same toast twice updates it instead of stacking a ninth
"you are offline", and onClose.
promise() returns the SAME promise it was given, rejection included. It
reports, it does not handle: swallowing there would turn a failed call
into a red toast and nothing else, with the caller's own error path never
running. Its reporting chain handles its own rejection, so telling the
user about a failure never adds an unhandled one.
Names stay nebula's — show / dismiss / variant / duration, not add /
close / type / timeout, and variant keeps warning, which Base UI's type
does not have. variant is what every other component here calls this
(Button, Alert, Badge); renaming it in this one file would make the
package less consistent with itself than with a library it does not use.
The deviations, and the four fields left out, are named at the top of the
file rather than left to be discovered.
onClose belongs to application code and runs from a timer, a click and
clear() alike, so one that throws is caught and reported instead of
leaving the queue half updated.
show() now returns ToastId (number | string) rather than number — a
widening, so only a caller annotating : number is affected. Worth
naming at the next bump.
Close the last gaps against Radix's primitive set
nebula already covered the shadcn registry. What it did not cover was the part
of Radix that shadcn styles no equivalent for — easy to miss precisely because
nothing looks wrong without it.
Direction. floating read the writing direction off the anchor and
mirrored placement; rovingFocus did not, so ArrowRight moved to the next item
in every widget nebula ships, including in Arabic and Hebrew where the next
item is to the left. Both now ask the same helper. Vertical arrows are left
alone: dir does not turn a page upside down.
VisuallyHidden. The clip technique, in one place. Three components were
hand-writing sr-only and the next one to need it would have written a fourth
variant. It is a <span>: Radix takes asChild so a hidden heading can be a
real <h2>, and aurora fixes the tag at compile time, so the equivalent is to
put this inside the heading rather than to become one.
AccessibleIcon. An icon with no label is announced as nothing, which is how
a row of icon buttons becomes a row of unlabelled buttons. The glyph goes
aria-hidden so its own <title> is not read on top of the label.
Toolbar. The WAI-ARIA pattern: one tab stop, arrows inside. Without it a
formatting bar costs one press of Tab per control, and the keyboard user below
pays for every one. Items are found by the same tabbable selector the focus
trap uses — now exported rather than copied — so anything a keyboard could
reach on its own is reached here, with no marker attribute to remember.
Changes since v0.2.1.