• v0.2.0 c6b850a98b

    v0.2.0
    All checks were successful
    ci / typecheck (push) Successful in 12s
    ci / release (push) Successful in 11s
    ci / lint (push) Successful in 14s
    ci / format (push) Successful in 15s
    Stable

    pienter-release-bot released this 2026-08-24 21:54:20 +00:00 | 277 commits to main since this release

    Breaking

    • Table and DataTable no longer support v-model:sort. Sort is a read-only
      sort prop plus a sort event carrying the activated column key; the consumer
      decides the next state. Migrate v-model:sort="sort" to
      :sort="sort" @sort="key => sort = toggleSort(sort, key)". See ADR 0005.
    • useUrlSort() returns { sort, onSort } instead of a writable computed:
      :sort="sort" @sort="onSort".
    • SortEvent is removed; the { key, direction } shape is now Sort.

    Changed

    • --duration-slow is now 2s (was 1s). It drives exactly one thing — the
      Icon spin animation — so this slows every spinner, including Button's
      loading state, which read as hurried.
    • Button and IconButton render Spinner instead of an inline spinning
      Icon, so every spinner in the library now shares the stepped chasing-tail
      look. Sizes are unchanged (1rem / 1.25rem), held via --pui-spinner-size.
    • The loader icon keeps its eight spokes as separate <path> elements in
      clockwise order so they can be faded individually. Visually identical on its
      own; only Spinner ramps them.
    • A Button or IconButton in its loading state no longer fades to
      --opacity-disabled. It is still disabled for interaction purposes, but a
      busy control is not an unavailable one, and the fade compounded with the
      spinner's own opacity ramp — the dial's dimmest spokes landed near 6% alpha
      and effectively disappeared.

    Added

    • Spinner (Vue + Astro) — the indeterminate "working" indicator, composing
      Icon's loader glyph. Sizes sm | md | lg | xl; supply label when the
      spinner is the only signal, omit it when the surrounding element already
      carries aria-busy. With a label the wrapper becomes a role="status"
      region and the label is rendered as visually-hidden text in a
      .pui-spinner__label span — a live region announces its text content, and one
      whose only child is aria-hidden has none.
    • Table (Vue + Astro) and DataTable take loading. The <tbody> frosts
      under an overlay and is marked inert, so its links and checkboxes are not
      merely un-clickable but genuinely out of reach; the header stays crisp and
      clickable, the table is marked aria-busy, and DataTable suppresses its
      empty state. rows is passed through untouched. The overlay is one extra
      <tr class="pui-table__loading"> inside <tbody>, rendered only while
      loading; the public DOM root is unchanged. Its spinner stays centred in the
      visible scroll port on a horizontally scrolled table, and a body with no rows
      is held open while busy by --pui-table-loading-min-h (default 9rem) so the
      overlay has a pane to sit in on a first fetch.
    • @[secure]/ui/utils exports the sort domain: Sort, SortDirection,
      SortState, toggleSort, parseSort, formatSort. The wire form is the
      column key with a - prefix for descending (title, -title), which means
      a column key must not itself start with -.
    • Table.astro gained a sort surface. Sortable headers render links to the URL
      each column would produce next, with the same aria-sort and chevron contract
      as the Vue tier, and work without JavaScript.
    Downloads