Browse docs
Browse docs
Released 2026-05-17 · Foundation release
Eight layout / structural primitives added to @dashforge/tw (Typography,
Box, Stack, Grid, Container, Divider, AspectRatio, VisuallyHidden) plus an
extensive test coverage hardening pass — 460 → 592 unit tests across
32 files. End-to-end validated in the dash consumer app: mount
12.1 ms / re-render 7–8.6 ms for a page with 50+ primitive
instances, within the 60 fps frame budget without React.memo.
Single-package release: only @dashforge/tw bumps. Companion packages
(tw-theme, tw-tokens) and the bridge / MUI side stay at their current
versions per the independent-versioning commitment.
No public API change on the previously-published 16 components — strictly additive minor bump.
| Package | What changed |
|---|---|
@dashforge/tw | Eight Foundation primitives added (F9 + F10). +132 internal tests (460 → 592 across 32 files). Existing 16 components' public API byte-identical. Bundle: dist/index.esm.js 255 KB → 272 KB gz (+6.7%). |
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 | Separate ecosystem; untouched. |
The Box ≠ flex, Stack = flex 1D, Grid = flex 2D rule is the spine
of this layer: each primitive has a single, non-overlapping
responsibility so "which one do I use?" has one answer per scenario. The
rule is enforced at the TypeScript prop type level — <Box direction="row">
is a compile error.
Typography — semantic typed
text. Twelve variants (h1–h6, subtitle1/2, body1/2, caption,
overline) × nine intent colors × five weight overrides + alignment
<h1>, body1→<p>, …), overridable via as or
asChild (Radix Slot). Reads color from a parent <Box> via
color="inherit".Box — surface primitive consolidating
MUI's Box + Paper + Card + Joy Surface into one. Five variants
(plain · outlined · elevated · soft · solid) × seven intent
colors = 21 compound visuals emitted by the TV recipe + six
elevation levels (0–5) + token-scale spacing
(p/px/py/m/mx/my) + rounded scale + fullWidth /
fullHeight. Strictly no flex / no grid by design.
Stack — the only flex container
in @dashforge/tw. direction + align + justify + token-scale
gap + wrap, plus a runtime divider prop that inserts N-1
separators between children (React.Children.toArray semantics:
Fragments count as one child — documented + asserted in the test
suite).
Grid — CSS Grid container + item,
polymorphic in role. MUI v2 API surface (<Grid container> +
<Grid xs={6}>) backed by real CSS Grid (display: grid +
col-span-*), not flexbox like MUI v2's own internals.
Discriminated-union TypeScript: <Grid container xs={6}> fails
compilation. 70-entry responsive col-span mapping (xs/sm/md/lg/xl
× 1..12/auto/full).
Closes the foundation surface to match what Chakra/Mantine/Joy ship at the layout-primitive level.
Container — centered max-width
page wrapper with the canonical responsive padding ramp
(px-4 sm:px-6 lg:px-8). Six sizes (sm / md / lg / xl /
2xl / fluid) mapped to Tailwind's max-w-screen-* aliases +
centerContent opt-in for marketing / sign-in layouts.
Divider — visual separator with
two rendering modes selected by children presence. Line-only renders
<hr> with role="separator" + aria-orientation; labeled mode
renders the "OR" separator pattern as two flex segments around the
label.
AspectRatio — content-shape
primitive using the native CSS aspect-ratio property (supported
since 2021, ~98% browser coverage). Number or CSS-string ratio. Pairs
with sx="rounded-xl overflow-hidden" for the canonical clipped media
pattern.
VisuallyHidden — the a11y
primitive. Uses Tailwind's sr-only utility (WebAIM clip technique).
Hides children from sighted users while keeping them in the
accessibility tree — icon button labels, aria-live announcers, skip
links.
+132 edge case unit tests added (460 → 592 total across 32
files). Reorganised here under Internal rather than Added because
the tests are not part of the public API surface.
elevation × variant interaction;
rounded edge values.xs={12} sm={6} md={4} lg={3} xl={2}), every autoFlow value,
every cols value, spacingX / spacingY independence, empty
container + orphan item handling, deep nesting.it.each, extreme ratio values (21/9, 9/16, 0.5, 3),
nested fluid/capped Container pattern, aria-live="polite"
announcer pattern.End-to-end consumer validation via file: link from the dash
consumer app to the local monorepo dist. New page
dash/src/pages/TestFoundation.tsx mounts all eight primitives
inside <DashforgeTailwindProvider> wrapped in a React <Profiler>.
Measured: mount 12.1 ms, update 7–8.6 ms for a page with 50+
primitive instances. Foundation primitives are pure (no internal
state, no useEffect) — re-render cost is the className resolution
alone, so no React.memo is needed.
Box ≠ flex, Stack = flex 1D, Grid = flex 2D — single
responsibility per primitive, enforced at the TypeScript prop type
level. <Box direction="row"> is a compile error: there is no
direction prop on Box. The rule rules out the MUI failure mode
where every <Box display="flex" gap={2}> quietly becomes the de
facto flex container of the codebase, drowning the
surface-vs-layout distinction. When you read <Stack> in a JSX tree,
you know it's flex without reading further.No migration steps required for consumers of the previously-published 16 components. Adopt the new Foundation primitives incrementally:
pnpm add @dashforge/tw@^0.2.0-betaExisting import paths and APIs are byte-identical.
Source links: per-package CHANGELOG · top-level CHANGELOG · npm · git tag @dashforge/[email protected]