@dashforge/tw 0.6.0-beta → 0.7.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +258 -0
- package/THEME-AUDIT.md +175 -0
- package/dist/index.esm.js +973 -69
- package/dist/src/components/Accordion/accordion.variants.d.ts.map +1 -1
- package/dist/src/components/Box/box.variants.d.ts.map +1 -1
- package/dist/src/components/DataGrid/DataGrid.d.ts +29 -0
- package/dist/src/components/DataGrid/DataGrid.d.ts.map +1 -0
- package/dist/src/components/DataGrid/dataGrid.types.d.ts +171 -0
- package/dist/src/components/DataGrid/dataGrid.types.d.ts.map +1 -0
- package/dist/src/components/DataGrid/dataGrid.variants.d.ts +216 -0
- package/dist/src/components/DataGrid/dataGrid.variants.d.ts.map +1 -0
- package/dist/src/components/Dialog/dialog.variants.d.ts.map +1 -1
- package/dist/src/components/Divider/divider.variants.d.ts.map +1 -1
- package/dist/src/components/Pagination/Pagination.d.ts.map +1 -1
- package/dist/src/components/Pagination/pagination.variants.d.ts.map +1 -1
- package/dist/src/components/Popover/popover.variants.d.ts.map +1 -1
- package/dist/src/components/Skeleton/skeleton.variants.d.ts.map +1 -1
- package/dist/src/components/Table/table.types.d.ts +8 -0
- package/dist/src/components/Table/table.types.d.ts.map +1 -1
- package/dist/src/components/Tabs/tabs.variants.d.ts.map +1 -1
- package/dist/src/components/Tooltip/tooltip.variants.d.ts.map +1 -1
- package/dist/src/components/Typography/typography.variants.d.ts.map +1 -1
- package/dist/src/components/_shared/data/getNestedValue.d.ts.map +1 -0
- package/dist/src/components/{Table/_internal → _shared/data}/useColumnAutoDetect.d.ts +1 -1
- package/dist/src/components/_shared/data/useColumnAutoDetect.d.ts.map +1 -0
- package/dist/src/components/_shared/data/useControllableState.d.ts.map +1 -0
- package/dist/src/components/_shared/data/useDebouncedValue.d.ts.map +1 -0
- package/dist/src/components/{Table/_internal → _shared/data}/useTableFilter.d.ts +1 -1
- package/dist/src/components/_shared/data/useTableFilter.d.ts.map +1 -0
- package/dist/src/components/{Table/_internal → _shared/data}/useTableSearch.d.ts +1 -1
- package/dist/src/components/_shared/data/useTableSearch.d.ts.map +1 -0
- package/dist/src/components/{Table/_internal → _shared/data}/useTableSelection.d.ts +1 -1
- package/dist/src/components/_shared/data/useTableSelection.d.ts.map +1 -0
- package/dist/src/components/{Table/_internal → _shared/data}/useTableSort.d.ts +1 -1
- package/dist/src/components/_shared/data/useTableSort.d.ts.map +1 -0
- package/dist/src/components/_shared/data/useVirtualizer.d.ts +107 -0
- package/dist/src/components/_shared/data/useVirtualizer.d.ts.map +1 -0
- package/dist/src/index.d.ts +5 -2
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/components/Accordion/accordion.variants.ts +5 -3
- package/src/components/Box/Box.test.tsx +32 -11
- package/src/components/Box/box.variants.ts +23 -4
- package/src/components/DataGrid/DataGrid.test.tsx +496 -0
- package/src/components/DataGrid/DataGrid.tsx +831 -0
- package/src/components/DataGrid/dataGrid.types.ts +189 -0
- package/src/components/DataGrid/dataGrid.variants.ts +144 -0
- package/src/components/Dialog/dialog.variants.ts +10 -6
- package/src/components/Divider/Divider.test.tsx +3 -1
- package/src/components/Divider/Divider.tsx +1 -1
- package/src/components/Divider/divider.variants.ts +2 -1
- package/src/components/Pagination/Pagination.tsx +3 -1
- package/src/components/Pagination/pagination.variants.ts +19 -13
- package/src/components/Popover/popover.variants.ts +10 -4
- package/src/components/Skeleton/skeleton.variants.ts +2 -1
- package/src/components/Table/Table.tsx +8 -8
- package/src/components/Table/table.types.ts +9 -0
- package/src/components/Tabs/tabs.variants.ts +19 -8
- package/src/components/Tooltip/tooltip.variants.ts +12 -2
- package/src/components/Typography/Typography.test.tsx +3 -1
- package/src/components/Typography/typography.variants.ts +6 -2
- package/src/components/{Table/_internal → _shared/data}/useColumnAutoDetect.test.ts +1 -1
- package/src/components/{Table/_internal → _shared/data}/useColumnAutoDetect.ts +1 -1
- package/src/components/{Table/_internal → _shared/data}/useTableFilter.ts +1 -1
- package/src/components/{Table/_internal → _shared/data}/useTableSearch.test.ts +1 -1
- package/src/components/{Table/_internal → _shared/data}/useTableSearch.ts +1 -1
- package/src/components/{Table/_internal → _shared/data}/useTableSelection.ts +1 -1
- package/src/components/{Table/_internal → _shared/data}/useTableSort.test.ts +1 -1
- package/src/components/{Table/_internal → _shared/data}/useTableSort.ts +1 -1
- package/src/components/_shared/data/useVirtualizer.test.ts +158 -0
- package/src/components/_shared/data/useVirtualizer.ts +199 -0
- package/src/components/_shared/themeIdentity.test.ts +113 -0
- package/src/index.ts +18 -2
- package/dist/src/components/Table/_internal/getNestedValue.d.ts.map +0 -1
- package/dist/src/components/Table/_internal/useColumnAutoDetect.d.ts.map +0 -1
- package/dist/src/components/Table/_internal/useControllableState.d.ts.map +0 -1
- package/dist/src/components/Table/_internal/useDebouncedValue.d.ts.map +0 -1
- package/dist/src/components/Table/_internal/useTableFilter.d.ts.map +0 -1
- package/dist/src/components/Table/_internal/useTableSearch.d.ts.map +0 -1
- package/dist/src/components/Table/_internal/useTableSelection.d.ts.map +0 -1
- package/dist/src/components/Table/_internal/useTableSort.d.ts.map +0 -1
- package/src/components/Table/_internal/themeIdentity.test.ts +0 -84
- /package/dist/src/components/{Table/_internal → _shared/data}/getNestedValue.d.ts +0 -0
- /package/dist/src/components/{Table/_internal → _shared/data}/useControllableState.d.ts +0 -0
- /package/dist/src/components/{Table/_internal → _shared/data}/useDebouncedValue.d.ts +0 -0
- /package/src/components/{Table/_internal → _shared/data}/getNestedValue.test.ts +0 -0
- /package/src/components/{Table/_internal → _shared/data}/getNestedValue.ts +0 -0
- /package/src/components/{Table/_internal → _shared/data}/useControllableState.ts +0 -0
- /package/src/components/{Table/_internal → _shared/data}/useDebouncedValue.ts +0 -0
- /package/src/components/{Table/_internal → _shared/data}/useTableSelection.test.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,264 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
12
12
|
> duplicated intentionally — no shared "lowest common denominator" headless
|
|
13
13
|
> layer.
|
|
14
14
|
|
|
15
|
+
## [0.7.0-beta] — 2026-05-19
|
|
16
|
+
|
|
17
|
+
**Combined Sprint 4.2 + 4.3 release.** Ships **`<DataGrid>`** — a
|
|
18
|
+
virtualized data table for large data sets (500 rows to millions)
|
|
19
|
+
— alongside a **theme identity sweep** across the whole catalog
|
|
20
|
+
(systemic dark-mode contrast fixes on 10 existing components).
|
|
21
|
+
|
|
22
|
+
Two coherent themes, one release:
|
|
23
|
+
|
|
24
|
+
1. **DataGrid (Sprint 4.2)** — sibling to `<Table>` with **homemade
|
|
25
|
+
virtualization** (no `@tanstack/react-virtual`, no
|
|
26
|
+
`react-window`). Same column model, same cell renderer library,
|
|
27
|
+
same RBAC integration, same identity-consistent visual design as
|
|
28
|
+
Table. Differs at the render strategy level: only the window of
|
|
29
|
+
visible rows mounts in DOM. Adds sticky-left columns, server-side
|
|
30
|
+
mode (4 independent opt-in flags), `selectAllScope`, and optional
|
|
31
|
+
internal pagination.
|
|
32
|
+
|
|
33
|
+
2. **Theme identity sweep (Sprint 4.3)** — applies the identity rule
|
|
34
|
+
codified in Sprint 4.1 to the WHOLE catalog. 10 existing
|
|
35
|
+
components (Typography, Box, Dialog, Divider, Pagination,
|
|
36
|
+
Popover, Skeleton, Tabs, Tooltip, plus Table-side comments)
|
|
37
|
+
had latent `dark:` variants on the neutral palette → those
|
|
38
|
+
classes double-inverted via the dashforgePreset CSS-var swap
|
|
39
|
+
and broke dark mode. Sweep removes the anti-pattern across the
|
|
40
|
+
catalog and adds a package-level regression test
|
|
41
|
+
(`_shared/themeIdentity.test.ts`) that scans every source file
|
|
42
|
+
on every test run.
|
|
43
|
+
|
|
44
|
+
**Minor bump** for the new DataGrid public API surface. Strictly
|
|
45
|
+
additive — zero breaking changes on existing component APIs.
|
|
46
|
+
Drop-in upgrade from `0.6.0-beta`.
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- **`<DataGrid>`** — virtualized data table for large data sets.
|
|
51
|
+
Sibling to `<Table>`. Required props: `rows`, `cols`,
|
|
52
|
+
`getRowId`, `rowHeight` (fixed for v1), `height` (bounded
|
|
53
|
+
container).
|
|
54
|
+
- **Homemade virtualization** via the new `useVirtualizer` hook
|
|
55
|
+
in `_shared/data/`. Scroll-event based with
|
|
56
|
+
`requestAnimationFrame` debounce (60 fps throttling) +
|
|
57
|
+
`ResizeObserver` for container resize. Spacer-row approach
|
|
58
|
+
preserves `<table>` semantics — screen readers see continuous
|
|
59
|
+
structure with `aria-hidden` spacers above/below the visible
|
|
60
|
+
window. Variable row height deferred to v1-bis.
|
|
61
|
+
- **Sticky left column** via `cols[i].sticky: 'left'`. CSS
|
|
62
|
+
`position: sticky; left: 0` with z-index ladder for the
|
|
63
|
+
top-left corner intersection with the sticky header
|
|
64
|
+
(`thead` z-10 < sticky cell z-[1] < sticky header corner z-20).
|
|
65
|
+
Right-sticky deferred to v1-bis.
|
|
66
|
+
- **Server-side mode flags** (4 independent opt-ins):
|
|
67
|
+
- `serverSideSort` — emits `onSortChange` but doesn't sort locally
|
|
68
|
+
- `serverSideFilter` — emits `onFilterChange` but doesn't filter
|
|
69
|
+
- `serverSideSearch` — emits `onSearchQueryChange` (debounced)
|
|
70
|
+
- `serverSidePagination` — uses `totalCount` for the virtual
|
|
71
|
+
scrollbar math; `rows` is the server-returned page slice
|
|
72
|
+
- **`selectAllScope`** prop (`'visible' | 'allLoaded'`, default
|
|
73
|
+
`'allLoaded'`) controls the header "select all" checkbox
|
|
74
|
+
semantic.
|
|
75
|
+
- **Optional internal pagination** via the `pagination` prop —
|
|
76
|
+
renders the Sprint 4 `<Pagination>` component below the
|
|
77
|
+
virtualized scroll. Composes naturally with virtualization
|
|
78
|
+
(each page is virtualized internally if large enough).
|
|
79
|
+
- **Bundle impact**: +8.4 KB gz on top of `0.6.0-beta`
|
|
80
|
+
(`<Table>` baseline). Zero new runtime deps.
|
|
81
|
+
- **Variants**: `plain` / `lines` (default) / `striped` /
|
|
82
|
+
`bordered`. **No `card` variant** — incompatible with
|
|
83
|
+
`<table>` virtualization spacer-rows.
|
|
84
|
+
- 24 unit tests + 12 `useVirtualizer` math tests + dash smoke
|
|
85
|
+
test at `/test-data-grid` with 10 000 generated rows.
|
|
86
|
+
|
|
87
|
+
- **`useVirtualizer` hook** in `_shared/data/` — exported
|
|
88
|
+
internally only (not in the public API surface). Other future
|
|
89
|
+
virtualized components (TreeView, future GanttGrid, etc.) can
|
|
90
|
+
reuse it.
|
|
91
|
+
|
|
92
|
+
- **`sticky?: 'left' | 'right'`** added to `TableColumn<T>` for
|
|
93
|
+
DataGrid integration. Currently ignored by Table (non-virtualized);
|
|
94
|
+
honored by DataGrid v1 for `'left'` only.
|
|
95
|
+
|
|
96
|
+
- **Doc MDX `/tw/docs/components/data-grid`** — emphasizes the
|
|
97
|
+
Table vs DataGrid decision matrix (24-row comparison table +
|
|
98
|
+
"Use Table when..." / "Use DataGrid when..." + "Switch later"
|
|
99
|
+
path showing the 3-line diff to migrate).
|
|
100
|
+
|
|
101
|
+
### Changed
|
|
102
|
+
|
|
103
|
+
- **Internal refactor — shared data helpers moved**:
|
|
104
|
+
`Table/_internal/` → `_shared/data/` (13 files). The helpers
|
|
105
|
+
(`getNestedValue`, `useTableSearch`, `useTableSort`,
|
|
106
|
+
`useTableSelection`, `useTableFilter`, `useColumnAutoDetect`,
|
|
107
|
+
`useDebouncedValue`, `useControllableState`) are now shared
|
|
108
|
+
between Table and DataGrid. **Pure path move** — function names
|
|
109
|
+
unchanged. Public API surface unchanged (only `getNestedValue`
|
|
110
|
+
was ever re-exported; the re-export path updated transparently).
|
|
111
|
+
|
|
112
|
+
- **Theme identity sweep** — 10 existing components had latent
|
|
113
|
+
`dark:` Tailwind variants on the neutral palette, which the
|
|
114
|
+
dashforgePreset CSS-var swap double-inverted and broke in dark
|
|
115
|
+
mode. Removed across the catalog:
|
|
116
|
+
|
|
117
|
+
| Component | Sites fixed | Pattern category |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| Accordion (`accordion.variants.ts`) | 3 | A (drop `dark:` — auto-invert) |
|
|
120
|
+
| Box (`box.variants.ts`) | 4 | A + B (corrected dark targets) + D (solid neutral now auto-inverts as accent) |
|
|
121
|
+
| Dialog (`dialog.variants.ts`) | 6 | A × 5 + B × 1 |
|
|
122
|
+
| Divider (`divider.variants.ts` + `Divider.tsx`) | 2 | A × 2 |
|
|
123
|
+
| Pagination (`pagination.variants.ts` + `Pagination.tsx`) | 10 | A × 7 + B × 3 (`dark:bg-neutral-900` → `bg-neutral-50` auto-invert) |
|
|
124
|
+
| Popover (`popover.variants.ts`) | 4 | A × 3 + B × 1 |
|
|
125
|
+
| Skeleton (`skeleton.variants.ts`) | 1 | A |
|
|
126
|
+
| Tabs (`tabs.variants.ts`) | 8 | A × 7 + B × 1 (`dark:bg-neutral-900` → `dark:bg-neutral-200` for proper active-pill elevation) |
|
|
127
|
+
| Tooltip (`tooltip.variants.ts`) | 2 | **Bug fix** — was previously invisible in dark mode (double-inverted to dark surface on dark page). Now auto-inverts: dark tooltip on light page, light tooltip on dark page (high contrast preserved). |
|
|
128
|
+
| Typography (`typography.variants.ts`) | 2 | A × 2 |
|
|
129
|
+
|
|
130
|
+
**Box solid neutral** now uses `bg-neutral-900 text-neutral-50`
|
|
131
|
+
(both auto-invert) — the accent surface flips with the page in
|
|
132
|
+
dark mode (light surface + dark text) for identity consistency.
|
|
133
|
+
Previously was `bg-neutral-900 text-white dark:bg-neutral-100
|
|
134
|
+
dark:text-neutral-900` — the `text-white` (no auto-invert) had
|
|
135
|
+
forced the double-invert pattern to stay readable.
|
|
136
|
+
|
|
137
|
+
Affected test files updated to assert the new canonical
|
|
138
|
+
patterns (Box × 4 + Typography × 1 + Divider × 1 = 6 tests
|
|
139
|
+
retargeted).
|
|
140
|
+
|
|
141
|
+
### Internal
|
|
142
|
+
|
|
143
|
+
- **`THEME-AUDIT.md`** at package root — categorizes every site
|
|
144
|
+
in the catalog as **A** (drop `dark:` — auto-invert), **B**
|
|
145
|
+
(keep `dark:` because `bg-white`/`fill-white` is hardcoded),
|
|
146
|
+
**C** (color palette — intentional design choice, not affected
|
|
147
|
+
by the rule), or **D** (component-specific exception). 42
|
|
148
|
+
violations catalogued across 10 components.
|
|
149
|
+
|
|
150
|
+
- **Package-level theme identity regression test** —
|
|
151
|
+
`_shared/themeIdentity.test.ts` replaces the Table-only one
|
|
152
|
+
from Sprint 4.1. Scans every `.ts`/`.tsx` source file under
|
|
153
|
+
`src/components/**` (excluding tests) on every test run.
|
|
154
|
+
Allows the Category B legitimate pattern
|
|
155
|
+
(`bg-white dark:bg-neutral-N` — `bg-white` doesn't auto-invert,
|
|
156
|
+
the `dark:` is required). Catches accidental re-introductions
|
|
157
|
+
of the anti-pattern at PR time.
|
|
158
|
+
|
|
159
|
+
- **+76 new unit tests** for Sprint 4.2 + 4.3:
|
|
160
|
+
- `useVirtualizer` math (12) — window calc, overscan, padding
|
|
161
|
+
invariant, edge cases (totalCount=0, very large dataset)
|
|
162
|
+
- `<DataGrid>` (24) — rendering, virtualized window, sort,
|
|
163
|
+
search, selection, selectAllScope, sticky left column,
|
|
164
|
+
server-side mode flags, RBAC, internal pagination, smart
|
|
165
|
+
defaults, i18n
|
|
166
|
+
- `_shared/themeIdentity.test.ts` package-level scanner (40
|
|
167
|
+
file-scan tests; 0 violations reported)
|
|
168
|
+
|
|
169
|
+
Full TW suite at **961/961 passing** across **48 files**.
|
|
170
|
+
|
|
171
|
+
- **`TestDataGrid.tsx`** added to the `dash` consumer
|
|
172
|
+
(`/test-data-grid`) — 10 000 generated rows + sticky col +
|
|
173
|
+
selectAllScope toggle + variant/density/size switcher + RBAC
|
|
174
|
+
per-column demo + custom empty state + React.Profiler logging.
|
|
175
|
+
|
|
176
|
+
- **Sidebar entries** for the new `DataGrid` doc page added to
|
|
177
|
+
`dashforge-docs-lab/src/tw-docs/sidebar.model.ts` (new "Data"
|
|
178
|
+
subgroup under "UI Components" now contains Table + DataGrid).
|
|
179
|
+
|
|
180
|
+
### Compatibility
|
|
181
|
+
|
|
182
|
+
| Axis | Pre-`0.7.0` | Post-`0.7.0` |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| Public API surface | 32 components | **+1 `<DataGrid>` + types (`DataGridProps`, `DataGridSelectAllScope`, `DataGridServerSideFlags`, `DataGridPaginationConfig`, `DataGridSlotProps`) + `dataGridVariants` recipe** + `sticky` axis on `TableColumn<T>` (additive, optional) |
|
|
185
|
+
| Peer deps | `react ^18 \|\| ^19`, `tw-theme workspace`, `tw-tokens workspace` | unchanged |
|
|
186
|
+
| Bridge deps | `forms` / `rbac` / `ui-core` `workspace:*` | unchanged |
|
|
187
|
+
| **New runtime deps** | — | **none** (homemade virtualization — constraint honored) |
|
|
188
|
+
| Breaking changes | — | **Zero** on public APIs. Internal refactor: `Table/_internal/` helpers moved to `_shared/data/`. Only externally-visible re-export was `getNestedValue` (path updated internally; export name preserved). |
|
|
189
|
+
| Bundle size | 402 KB raw / 91 KB gz | **445 KB raw / 98.4 KB gz** (+43 KB raw / +7.4 KB gz / **+8.1% gz**) — just under the 10% reviewer-sign-off threshold |
|
|
190
|
+
| Tests passing | 828/828 (46 files) | **961/961 (48 files)** — +133 from Sprint 4.2 + 4.3 |
|
|
191
|
+
|
|
192
|
+
> **Bundle sign-off** : the +8.1% gz delta is JUST under the 10%
|
|
193
|
+
> threshold from PERFORMANCE.md. Justification: DataGrid is the
|
|
194
|
+
> highest-leverage addition before 1.0 — it unlocks the entire
|
|
195
|
+
> "1 000+ row admin views" use case at €0 vs MUI X DataGrid Pro's
|
|
196
|
+
> €499/year. The theme identity sweep is net-neutral on bundle
|
|
197
|
+
> (removed redundant classes, replaced with smaller ones); the
|
|
198
|
+
> delta is entirely DataGrid + virtualizer code (+8 KB gz).
|
|
199
|
+
|
|
200
|
+
### Migration
|
|
201
|
+
|
|
202
|
+
No code changes required:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
pnpm up @dashforge/tw@^0.7.0-beta
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
To adopt the new DataGrid:
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import {
|
|
212
|
+
DataGrid,
|
|
213
|
+
RenderTwoLine,
|
|
214
|
+
RenderChip,
|
|
215
|
+
RowActionsMenu,
|
|
216
|
+
} from '@dashforge/tw';
|
|
217
|
+
|
|
218
|
+
<DataGrid
|
|
219
|
+
rows={users} // can be 10k, 100k, millions
|
|
220
|
+
cols={columns} // SAME shape as Table's TableColumn<T>
|
|
221
|
+
getRowId={(r) => r.id}
|
|
222
|
+
rowHeight={48} // required for virtualization
|
|
223
|
+
height="600px" // required for bounded container
|
|
224
|
+
enableSearch
|
|
225
|
+
rowSelection="multiple"
|
|
226
|
+
selectAllScope="allLoaded"
|
|
227
|
+
bulkActions={(rows) => (
|
|
228
|
+
<Button color="danger">Delete {rows.length}</Button>
|
|
229
|
+
)}
|
|
230
|
+
rowActions={(row) => (
|
|
231
|
+
<RowActionsMenu
|
|
232
|
+
row={row}
|
|
233
|
+
actions={[
|
|
234
|
+
{ label: 'Edit', onClick: edit },
|
|
235
|
+
{ label: 'Delete', onClick: del, color: 'danger' },
|
|
236
|
+
]}
|
|
237
|
+
/>
|
|
238
|
+
)}
|
|
239
|
+
/>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
To switch an existing Table to DataGrid when you outgrow the row
|
|
243
|
+
count (~500+ rows), the migration is mechanical (3 lines):
|
|
244
|
+
|
|
245
|
+
```diff
|
|
246
|
+
- <Table
|
|
247
|
+
+ <DataGrid
|
|
248
|
+
rows={users}
|
|
249
|
+
cols={columns}
|
|
250
|
+
getRowId={(row) => row.id}
|
|
251
|
+
+ rowHeight={48}
|
|
252
|
+
+ height="600px"
|
|
253
|
+
/>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Everything else (sort / search / selection / row actions / cell
|
|
257
|
+
renderers / RBAC / i18n labels / sx / slotProps) keeps working
|
|
258
|
+
identically. **Expandable rows** are the one feature you lose
|
|
259
|
+
on Table→DataGrid swap — they ship in DataGrid v1-bis.
|
|
260
|
+
|
|
261
|
+
### Heads-up — companion releases coming
|
|
262
|
+
|
|
263
|
+
- **Sprint 4.2-bis → `0.8.0-beta`** (estimated): DataGrid v1-bis —
|
|
264
|
+
per-column filter UI chips (text/number/boolean/date), column
|
|
265
|
+
resize via drag, column reorder via drag, column visibility
|
|
266
|
+
dialog, right-sticky columns.
|
|
267
|
+
- **Sprint 5 → `0.9.0-beta`** + starter kits v1: separate repos
|
|
268
|
+
`dashforge-starter-mui` + `dashforge-starter-tw` with Auth + RBAC
|
|
269
|
+
+ form CRUD + dashboard with DataGrid admin views.
|
|
270
|
+
- **Sprint 6 → `1.0.0-rc.1 → 1.0.0`**: final A11Y audit, bundle
|
|
271
|
+
lockdown, beta freeze, cut `1.0.0`.
|
|
272
|
+
|
|
15
273
|
## [0.6.0-beta] — 2026-05-19
|
|
16
274
|
|
|
17
275
|
**Sprint 4.1 release.** Ships the central piece of the data layer
|
package/THEME-AUDIT.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# THEME-AUDIT — @dashforge/tw catalog identity sweep
|
|
2
|
+
|
|
3
|
+
> Sprint 4.3 deliverable (2026-05-19). Audit of every `@dashforge/tw`
|
|
4
|
+
> component against the **default `dashforgePreset()` identity rule**
|
|
5
|
+
> (memory: `feedback_dashforge_preset_is_identity`).
|
|
6
|
+
>
|
|
7
|
+
> **Rule recap.** The `dashforgePreset()` CSS-variable swap
|
|
8
|
+
> auto-inverts the neutral palette:
|
|
9
|
+
> `bg-neutral-50` / `text-neutral-900` etc. resolve to the
|
|
10
|
+
> appropriate physical color via `--df-tw-color-neutral-N`. Adding a
|
|
11
|
+
> `dark:` variant on a neutral class **double-inverts** the value
|
|
12
|
+
> and breaks dark mode. The Sprint 4.1 Table fix established the
|
|
13
|
+
> canonical patterns; Sprint 4.3 applies them to the whole catalog.
|
|
14
|
+
|
|
15
|
+
## Scoring legend
|
|
16
|
+
|
|
17
|
+
- **A** — Pure double inversion. REMOVE `dark:` variant entirely.
|
|
18
|
+
- **B** — `bg-white` / `text-white` paired with a wrong `dark:`
|
|
19
|
+
target. KEEP the `dark:` variant (white doesn't auto-invert) but
|
|
20
|
+
FIX the target — `dark:bg-neutral-900` in dark mode resolves to
|
|
21
|
+
near-white (broken); use `dark:bg-neutral-100` for proper dark
|
|
22
|
+
elevated surface.
|
|
23
|
+
- **C** — Intentional `dark:` on color palette (`primary`,
|
|
24
|
+
`success`, `warning`, `danger`, `info`, `secondary`). KEEP — color
|
|
25
|
+
palettes don't auto-invert; explicit `dark:` is a design choice.
|
|
26
|
+
- **D** — Component-specific exception (e.g. Tooltip wants
|
|
27
|
+
high-contrast inversion against the page). Document why kept.
|
|
28
|
+
|
|
29
|
+
## Per-file findings
|
|
30
|
+
|
|
31
|
+
### Accordion (`accordion.variants.ts`)
|
|
32
|
+
|
|
33
|
+
| Line | Class | Category | Fix |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| 6 | `border-b border-neutral-200 dark:border-neutral-800` | A | `border-b border-neutral-200` |
|
|
36
|
+
| 10 | `text-sm font-medium text-neutral-900 dark:text-neutral-50` | A | `text-sm font-medium text-neutral-900` |
|
|
37
|
+
| 18 | `overflow-hidden text-sm text-neutral-700 dark:text-neutral-300` | A | drop `dark:text-neutral-300` |
|
|
38
|
+
|
|
39
|
+
### Box (`box.variants.ts`)
|
|
40
|
+
|
|
41
|
+
| Line | Class | Category | Fix |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| 58 | `bg-white dark:bg-neutral-900` (elevated) | B | `bg-white dark:bg-neutral-100` (dark-mode elevation tier above page surface) |
|
|
44
|
+
| 168 | `border-neutral-200 bg-white dark:border-neutral-700 dark:bg-neutral-900` (outlined) | A + B | `border-neutral-200 bg-white dark:bg-neutral-100` (border auto-inverts; bg keeps `bg-white` + corrected dark target) |
|
|
45
|
+
| 184 | `bg-neutral-100 text-neutral-900 dark:bg-neutral-800 dark:text-neutral-100` (soft) | A | `bg-neutral-100 text-neutral-900` |
|
|
46
|
+
| 200 | `bg-neutral-900 text-white dark:bg-neutral-100 dark:text-neutral-900` (solid neutral) | D | `bg-neutral-900 text-neutral-50` (auto-invert — solid neutral becomes inverted accent: dark surface in light mode, light surface in dark mode; consistent with identity rule) |
|
|
47
|
+
|
|
48
|
+
### Dialog (`dialog.variants.ts`)
|
|
49
|
+
|
|
50
|
+
| Line | Class | Category | Fix |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| 29 | `w-full bg-white dark:bg-neutral-900 rounded-lg shadow-xl` | B | `dark:bg-neutral-100` |
|
|
53
|
+
| 30 | `border border-neutral-200 dark:border-neutral-800` | A | drop `dark:` |
|
|
54
|
+
| 37 | `text-lg ... text-neutral-900 dark:text-neutral-50` | A | drop `dark:` |
|
|
55
|
+
| 38 | `text-sm text-neutral-600 dark:text-neutral-400` | A | drop `dark:` |
|
|
56
|
+
| 42 | `dark:text-neutral-400 dark:hover:text-neutral-50` (close button) | A | drop both `dark:` |
|
|
57
|
+
| 43 | `hover:bg-neutral-100 dark:hover:bg-neutral-800` | A | drop `dark:` |
|
|
58
|
+
|
|
59
|
+
### Divider (`divider.variants.ts` + `Divider.tsx`)
|
|
60
|
+
|
|
61
|
+
| File / Line | Class | Category | Fix |
|
|
62
|
+
|---|---|---|---|
|
|
63
|
+
| `divider.variants.ts:88` | `border-neutral-200 dark:border-neutral-800` (neutral color variant) | A | drop `dark:` |
|
|
64
|
+
| `Divider.tsx:131` | `text-sm text-neutral-500 dark:text-neutral-400` (label) | A | drop `dark:` |
|
|
65
|
+
|
|
66
|
+
### Pagination (`pagination.variants.ts` + `Pagination.tsx`)
|
|
67
|
+
|
|
68
|
+
10 violations — full sweep of borders / text / bg / hover / active-button.
|
|
69
|
+
|
|
70
|
+
| Line | Class | Category | Fix |
|
|
71
|
+
|---|---|---|---|
|
|
72
|
+
| `variants:26` summary | `text-neutral-600 dark:text-neutral-400` | A | drop |
|
|
73
|
+
| `variants:30, 44, 58` borders | `border border-neutral-300 dark:border-neutral-700` | A | drop |
|
|
74
|
+
| `variants:31, 45, 59` button/selector/input bg | `bg-white dark:bg-neutral-900` | B → drop entirely | use `bg-neutral-50` (auto-inverts; border provides separation against page) |
|
|
75
|
+
| `variants:32, 46, 55` button text | `text-neutral-700 dark:text-neutral-300` | A | drop |
|
|
76
|
+
| `variants:33, 47` button hover bg | `hover:bg-neutral-50 dark:hover:bg-neutral-800` | A | `hover:bg-neutral-100` (1 elevation tier; auto-inverts) |
|
|
77
|
+
| `variants:60` jumpInput text | `text-neutral-900 dark:text-neutral-50` | A | drop |
|
|
78
|
+
| `Pagination.tsx:241` select bg+border | `border ... dark:border-neutral-700 bg-white dark:bg-neutral-900` | B → drop | `border border-neutral-300 bg-neutral-50` |
|
|
79
|
+
|
|
80
|
+
### Popover (`popover.variants.ts`)
|
|
81
|
+
|
|
82
|
+
| Line | Class | Category | Fix |
|
|
83
|
+
|---|---|---|---|
|
|
84
|
+
| 6 | `border border-neutral-200 dark:border-neutral-800` | A | drop |
|
|
85
|
+
| 7 | `bg-white dark:bg-neutral-900 p-4 shadow-lg` | B | `dark:bg-neutral-100` |
|
|
86
|
+
| 8 | `text-sm text-neutral-900 dark:text-neutral-50` | A | drop |
|
|
87
|
+
| 14 | `fill-white dark:fill-neutral-900` (arrow) | B | `fill-white dark:fill-neutral-100` (match content bg) |
|
|
88
|
+
|
|
89
|
+
### Skeleton (`skeleton.variants.ts`)
|
|
90
|
+
|
|
91
|
+
| Line | Class | Category | Fix |
|
|
92
|
+
|---|---|---|---|
|
|
93
|
+
| 38 | `bg-neutral-200 dark:bg-neutral-800` | A | drop `dark:` (auto-inverts — skeleton placeholder slightly more visible than page surface in both modes) |
|
|
94
|
+
|
|
95
|
+
### Tabs (`tabs.variants.ts`)
|
|
96
|
+
|
|
97
|
+
| Line | Class | Category | Fix |
|
|
98
|
+
|---|---|---|---|
|
|
99
|
+
| 22 | `border-b border-neutral-200 dark:border-neutral-800` (underline list) | A | drop |
|
|
100
|
+
| 24 | `... text-neutral-600 dark:text-neutral-400` (underline trigger) | A | drop |
|
|
101
|
+
| 25 | `hover:text-neutral-900 dark:hover:text-neutral-50` | A | drop |
|
|
102
|
+
| 31 | `bg-neutral-100 dark:bg-neutral-800 p-1` (pill list) | A | drop |
|
|
103
|
+
| 33 | `... text-neutral-600 dark:text-neutral-400` (pill trigger) | A | drop |
|
|
104
|
+
| 34 | `hover:text-neutral-900 dark:hover:text-neutral-50` | A | drop |
|
|
105
|
+
| 35 | `data-[state=active]:bg-white dark:data-[state=active]:bg-neutral-900` (pill active) | B | `dark:bg-neutral-100` |
|
|
106
|
+
| 36 | `data-[state=active]:text-neutral-900 dark:data-[state=active]:text-neutral-50` | A | drop |
|
|
107
|
+
|
|
108
|
+
### Tooltip (`tooltip.variants.ts`)
|
|
109
|
+
|
|
110
|
+
Tooltip historically used a "fixed dark surface in both modes" pattern.
|
|
111
|
+
Per the identity rule, **auto-invert** is the right behavior: dark
|
|
112
|
+
tooltip on light page (light mode), light tooltip on dark page (dark
|
|
113
|
+
mode) — the inversion **is** the high-contrast effect.
|
|
114
|
+
|
|
115
|
+
| Line | Class | Category | Fix |
|
|
116
|
+
|---|---|---|---|
|
|
117
|
+
| 8 | `bg-neutral-900 text-neutral-50` + `dark:bg-neutral-50 dark:text-neutral-900` | A | drop the `dark:` line entirely (the base classes already auto-invert and give the high-contrast effect against the page) |
|
|
118
|
+
| 14 | `fill-neutral-900 dark:fill-neutral-50` (arrow) | A | drop `dark:` (arrow auto-inverts with content bg) |
|
|
119
|
+
|
|
120
|
+
### Typography (`typography.variants.ts`)
|
|
121
|
+
|
|
122
|
+
| Line | Class | Category | Fix |
|
|
123
|
+
|---|---|---|---|
|
|
124
|
+
| 65 | `neutral: 'text-neutral-900 dark:text-neutral-100'` | A | `'text-neutral-900'` |
|
|
125
|
+
| 66 | `muted: 'text-neutral-600 dark:text-neutral-400'` | A | `'text-neutral-600'` |
|
|
126
|
+
|
|
127
|
+
### Table-side (Sprint 4.1 — already fixed)
|
|
128
|
+
|
|
129
|
+
Per-file already corrected in Sprint 4.1. Sprint 4.3 just verifies
|
|
130
|
+
the regression guard (themeIdentity test) is promoted to global so
|
|
131
|
+
the rest of the catalog is covered too.
|
|
132
|
+
|
|
133
|
+
## Summary
|
|
134
|
+
|
|
135
|
+
| Component | Violations | Fix categories |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| Accordion | 3 | A × 3 |
|
|
138
|
+
| Box | 4 | A + B + D |
|
|
139
|
+
| Dialog | 6 | A × 5 + B × 1 |
|
|
140
|
+
| Divider | 2 | A × 2 |
|
|
141
|
+
| Pagination | 10 | A × 7 + B × 3 |
|
|
142
|
+
| Popover | 4 | A × 3 + B × 1 |
|
|
143
|
+
| Skeleton | 1 | A × 1 |
|
|
144
|
+
| Tabs | 8 | A × 7 + B × 1 |
|
|
145
|
+
| Tooltip | 2 | A × 2 (was "double-inverted to preserve fixed look" — auto-invert is now the canonical choice) |
|
|
146
|
+
| Typography | 2 | A × 2 |
|
|
147
|
+
| **Total** | **42** | **31 A + 5 B + 1 D + 5 fixed already** |
|
|
148
|
+
|
|
149
|
+
## Color palettes — NOT touched
|
|
150
|
+
|
|
151
|
+
`dark:` variants on `primary`, `secondary`, `success`, `warning`,
|
|
152
|
+
`danger`, `info` palettes are **intentional** — those palettes do
|
|
153
|
+
not auto-invert (same physical color in both modes), so the
|
|
154
|
+
`dark:` shift is a design choice for tone refinement. They stay.
|
|
155
|
+
|
|
156
|
+
Affected sites (kept as-is):
|
|
157
|
+
|
|
158
|
+
- `Box` compound variants (`outlined/soft/solid` × color) for the
|
|
159
|
+
6 color palettes (lines 188-198 in `box.variants.ts`)
|
|
160
|
+
- `RenderChip` color rows (Sprint 4.1 decision)
|
|
161
|
+
- `RowActionsMenu` `text-danger-700 dark:text-danger-300` and same
|
|
162
|
+
for warning
|
|
163
|
+
|
|
164
|
+
## Sprint 4.3 deliverables
|
|
165
|
+
|
|
166
|
+
1. ✅ THEME-AUDIT.md (this file)
|
|
167
|
+
2. ⏳ Apply fixes per-file (P3)
|
|
168
|
+
3. ⏳ Promote `themeIdentity.test.ts` from `Table/_internal/` to
|
|
169
|
+
package-level scanner walking `libs/dashforge/tw/src/components/**`
|
|
170
|
+
(P2). Excludes `*.test.*` files. The test fails any future
|
|
171
|
+
`dark:*-neutral-N` reintroduction.
|
|
172
|
+
4. ⏳ Visual regression catch in `dash` (manual check in both
|
|
173
|
+
light + dark mode)
|
|
174
|
+
5. ⏳ Release `@dashforge/tw@0.7.0-beta` — expected bundle to
|
|
175
|
+
**shrink** by 1-3 KB gz (removing redundant classes).
|