Browse docs
Browse docs
Released 2026-05-19 · Sprint 4 release
Two utility primitives — <Skeleton> and <Pagination> — that
were missing from the catalog and unlock the loading-state +
paginated-list use cases ahead of Sprint 4.1 (Table + DataGrid).
Pure-UI, no bridge integration. Both ship in @dashforge/tw
only — MUI consumers continue using @mui/material/Skeleton and
@mui/material/Pagination directly. Per the Dashforge design rule
(@dashforge/ui wraps MUI only when adding bridge / RBAC /
validation / custom-behavior value), wrappers for these would add
zero value and would only block evolution of the upstream
components.
Zero new runtime deps. The "no external libraries" constraint
is honored — Skeleton uses Tailwind's built-in animate-pulse,
Pagination is hand-rolled with a standard "siblings + boundaries +
ellipsis fill" range algorithm.
Strictly additive minor bump. Drop-in upgrade from
0.4.0-beta — zero breaking changes; existing usages keep
working byte-identical.
| Package | What changed |
|---|---|
@dashforge/tw | +2 utility components (Skeleton · Pagination). 681/681 unit tests passing across 39 files (+47 new tests). Bundle: 336 KB raw / 73.9 KB gzipped (+7.3% gz vs 0.4.0-beta; documented in PERFORMANCE.md regression budget). |
Unchanged (independent versioning):
| Package | Version (unchanged) | Why |
|---|---|---|
@dashforge/tw-theme | 0.1.0-beta | No source change — peer dep stays at ^0.1.0-beta. |
@dashforge/tw-tokens | 0.1.0-beta | No source change — peer dep stays at ^0.1.0-beta. |
Bridge (forms, rbac, ui-core) | 0.2.3-beta | Shared with MUI side; no source change. |
MUI side (ui, theme-mui, theme-core, tokens) | 0.2.3-beta | No new wrappers — MUI consumers use @mui/material/{Skeleton, Pagination} directly. |
import { Skeleton } from '@dashforge/tw';
<Skeleton variant="text" width="200px" />
<Skeleton variant="rectangle" width="100%" height="120px" />
<Skeleton variant="circle" width="48px" />text (default — h-[1em], full width),
rectangle (rounded-md), circle (rounded-full — height
defaults to width when omitted).pulse (default — Tailwind's built-in
animate-pulse), wave (gradient overlay), none.prefers-reduced-motion: reduce in their OS. No extra config.<span aria-hidden="true" role="presentation"> — screen readers skip the placeholder
entirely. The surrounding container is responsible for
aria-busy / aria-live announcements when relevant.<Skeleton>s inside a flex
or grid layout to mimic card / row / avatar shapes during fetch.sx + slotProps.root for the standard customization escape
hatches.import { Pagination } from '@dashforge/tw';
import { useState } from 'react';
function MyList() {
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(20);
return (
<Pagination
page={page}
pageSize={pageSize}
totalCount={1437}
onPageChange={setPage}
onPageSizeChange={setPageSize}
/>
);
}default (full kit) — summary "Showing X-Y of Z" · page
numbers with ellipsis · first/prev/next/last · page-size
selector · direct page-jump inputcompact — page numbers + first/prev/next/last onlyminimal — "Page X of Y" + prev/next (mobile-friendly)sm · md (default) · lg — drives padding +
text size on every interactive element.siblingCount
(default 1) + boundaryCount (default 1). Small totals
(≤ 2·boundary + 2·sibling + 3 pages = 7 with defaults)
short-circuit to the full range — no ellipsis when it's
unnecessary.labels prop. Pass the subset you need; the
rest fall back to English defaults
(first · prev · next · last · page · of · showing
· perPage · goToPage).<nav aria-label="Pagination"> landmark.
Active button marked with aria-current="page". Every
interactive element carries an aria-label. Page-size selector
is a real <select>; jump input is a real
<input type="number"> with min / max matching the page
range, commit on Enter or blur, out-of-range values
silently clamped.onPageSizeChange is
omitted. Jump input toggle via showJumpInput.sx + slotProps (root, summary, list, pageButton,
activeButton, navButton, ellipsis, pageSizeSelector, jumpInput).This is the first release that exercised the new Dashforge
workflow policy: every component is validated end-to-end in the
dash consumer (/test-utilities) BEFORE its docs page is
written. Catches integration / theme / animation / a11y issues
that unit tests miss — and avoids docs that fossilize a
not-yet-validated API.
TestUtilities.tsx in dash exercises:
React.ProfilerA design decision worth documenting once for future reference:
@dashforge/ui wraps MUI when… | @dashforge/ui does NOT wrap MUI when… |
|---|---|
The wrapper adds bridge integration (form schema, useDashFieldMeta) | The component is pure visual / interaction with no Dashforge-side state |
The wrapper adds RBAC (access prop, useAccessState) | No security-relevant access decisions |
| The wrapper adds validation gating (Form Closure v1) | No validation surface |
| The wrapper adds custom behavior (loading state, asChild, etc.) | MUI already covers the behavior fully |
Skeleton + Pagination fall entirely in the right column. A
wrapper would only add a layer of indirection that blocks MUI's
own evolution (peer-dep version pins, prop-passthrough churn, etc.)
without giving consumers anything they can't get from
@mui/material directly.
Above the 5% threshold defined in PERFORMANCE.md, but under
10% — falls in the "CHANGELOG justification only, no reviewer
sign-off" category.
| Metric | 0.4.0-beta | 0.5.0-beta | Δ |
|---|---|---|---|
| Raw | 312 KB | 336 KB | +24 KB |
| Gzipped | 68.85 KB | 73.9 KB | +5.05 KB / +7.3% |
Per-component contribution (gzipped, estimated):
Within the projected envelope. The next bundle alarm to watch is
Sprint 4.1 (DataGrid — virtualization homemade, no @tanstack),
projected ~+10-15% gz.
pnpm up @dashforge/tw@^0.5.0-betaNo code changes required. Adopt the new components by importing
them from @dashforge/tw:
import { Skeleton, Pagination } from '@dashforge/tw';MUI consumers: continue using @mui/material/Skeleton and
@mui/material/Pagination directly. @dashforge/ui will NOT
ship wrappers for these (design rule above).
| Compatibility axis | Pre-0.5.0 | Post-0.5.0 |
|---|---|---|
| Public API surface | 29 components | + 2 (Skeleton · Pagination) + their *Props / *SlotProps types + *Variants recipes + PaginationLabels i18n type (additive — opt-in, zero impact on existing usages) |
| Peer deps | react ^18 || ^19, tw-theme workspace, tw-tokens workspace | unchanged |
| Bridge deps | forms / rbac / ui-core workspace:* | unchanged |
| New runtime deps | — | none (no @tanstack/*, no other libs — constraint honored) |
| Breaking changes | — | Zero. |
| Bundle size | 312 KB raw / 68.85 KB gzipped | 336 KB raw / 73.9 KB gzipped (+7.3% gz; above 5% threshold, motivated above) |
| Tests passing | 634/634 (37 files) | 681/681 (39 files) — +47 new tests (Skeleton 19 + Pagination 28) |
| Sprint | Release | Theme |
|---|---|---|
| Sprint 4.1 | @dashforge/[email protected] | Table + DataGrid. Native <table> body, homemade virtualization (IntersectionObserver-based, ~150-200 LOC), RBAC per-column, row selection. Skeleton + Pagination compose here as loading state + companion. |
| Sprint 5 | @dashforge/[email protected] + starter kits v1 | Two separate repos dashforge-starter-mui + dashforge-starter-tw — Auth + RBAC + form CRUD + dashboard with DataGrid admin views. Unlocks revenue model "kits paid + lib free". |
| Sprint 6 | @dashforge/[email protected] → 1.0.0 | Final A11Y audit (axe / lighthouse CI), bundle lockdown, beta freeze 4 weeks, then cut 1.0.0. |