@exxatdesignux/ui 0.5.10 → 0.5.12
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 +37 -0
- package/bin/cli.mjs +70 -1
- package/bin/init.mjs +18 -4
- package/bin/sync-extras.mjs +28 -4
- package/consumer-extras/README.md +41 -5
- package/consumer-extras/cursor-rules/exxat-accessibility.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-board-cards.mdc +5 -3
- package/consumer-extras/cursor-rules/exxat-breadcrumbs-no-back.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-card-vs-list-rows.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +4 -2
- package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-command-menu.mdc +3 -2
- package/consumer-extras/cursor-rules/exxat-data-tables.mdc +5 -3
- package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +7 -0
- package/consumer-extras/cursor-rules/exxat-drawer-vs-dialog.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +13 -2
- package/consumer-extras/cursor-rules/exxat-fontawesome-icons.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-hub-supported-views.mdc +6 -4
- package/consumer-extras/cursor-rules/exxat-kbd-shortcuts.mdc +6 -5
- package/consumer-extras/cursor-rules/exxat-kpi-flat-band.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-kpi-max-four.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-kpi-trends.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-library-hub-header.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-list-page-connected-views.mdc +6 -2
- package/consumer-extras/cursor-rules/exxat-list-page-view-shells.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-nav-single-active.mdc +4 -3
- package/consumer-extras/cursor-rules/exxat-no-image-pixel-copy.mdc +25 -14
- package/consumer-extras/cursor-rules/exxat-no-slds-leakage.mdc +8 -2
- package/consumer-extras/cursor-rules/exxat-no-toast.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-no-vaul.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-page-header-actions.mdc +6 -4
- package/consumer-extras/cursor-rules/exxat-page-vs-drawer.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-sidebar-shell.mdc +13 -7
- package/consumer-extras/cursor-rules/exxat-table-properties-drawer.mdc +5 -3
- package/consumer-extras/cursor-rules/exxat-table-row-preview.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-tabs-chrome.mdc +6 -4
- package/consumer-extras/cursor-rules/exxat-token-discipline.mdc +6 -0
- package/consumer-extras/cursor-rules/exxat-ux-discovery-protocol.mdc +92 -12
- package/consumer-extras/cursor-rules/exxat-ux-principles.mdc +1 -0
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +1 -1
- package/consumer-extras/cursor-skills/exxat-kpi-trends/SKILL.md +5 -3
- package/consumer-extras/cursor-skills/exxat-senior-ux/SKILL.md +70 -17
- package/consumer-extras/patterns/command-menu-pattern.md +2 -2
- package/consumer-extras/patterns/consumer-upgrade-checklist.md +1 -1
- package/consumer-extras/patterns/jobs/README.md +1 -1
- package/consumer-extras/patterns/perf-memory-pattern.md +115 -150
- package/consumer-extras/scripts/dev-guard.mjs +156 -0
- package/consumer-extras/templates/README.md +23 -0
- package/consumer-extras/templates/handoff.md +190 -0
- package/dist/hooks/use-app-theme.d.ts +1 -1
- package/package.json +2 -2
- package/{template → template-vite}/.claude/skills/exxat-ds-skill/SKILL.md +184 -23
- package/template-vite/.cursor/rules/exxat-accessibility.mdc +40 -0
- package/template-vite/.cursor/rules/exxat-board-cards.mdc +28 -0
- package/template-vite/.cursor/rules/exxat-breadcrumbs-no-back.mdc +22 -0
- package/template-vite/.cursor/rules/exxat-card-vs-list-rows.mdc +22 -0
- package/template-vite/.cursor/rules/exxat-centralized-list-dataset.mdc +46 -0
- package/template-vite/.cursor/rules/exxat-collaboration-access.mdc +33 -0
- package/{template → template-vite}/.cursor/rules/exxat-command-menu.mdc +5 -5
- package/template-vite/.cursor/rules/exxat-data-tables.mdc +47 -0
- package/template-vite/.cursor/rules/exxat-dedicated-search-surfaces.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-drawer-vs-dialog.mdc +23 -0
- package/template-vite/.cursor/rules/exxat-ds-agents.mdc +87 -0
- package/template-vite/.cursor/rules/exxat-fontawesome-icons.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-hub-supported-views.mdc +56 -0
- package/{template → template-vite}/.cursor/rules/exxat-kbd-shortcuts.mdc +1 -0
- package/template-vite/.cursor/rules/exxat-kpi-flat-band.mdc +29 -0
- package/template-vite/.cursor/rules/exxat-kpi-max-four.mdc +22 -0
- package/template-vite/.cursor/rules/exxat-kpi-trends.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-library-hub-header.mdc +29 -0
- package/template-vite/.cursor/rules/exxat-list-page-connected-views.mdc +28 -0
- package/template-vite/.cursor/rules/exxat-list-page-view-shells.mdc +32 -0
- package/{template → template-vite}/.cursor/rules/exxat-mono-ids.mdc +1 -0
- package/template-vite/.cursor/rules/exxat-nav-single-active.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-no-image-pixel-copy.mdc +46 -0
- package/template-vite/.cursor/rules/exxat-no-slds-leakage.mdc +84 -0
- package/{template → template-vite}/.cursor/rules/exxat-no-toast.mdc +2 -2
- package/template-vite/.cursor/rules/exxat-no-vaul.mdc +26 -0
- package/template-vite/.cursor/rules/exxat-page-header-actions.mdc +33 -0
- package/{template → template-vite}/.cursor/rules/exxat-page-vs-drawer.mdc +5 -3
- package/template-vite/.cursor/rules/exxat-person-identity-display.mdc +48 -0
- package/template-vite/.cursor/rules/exxat-primary-nav-secondary-panel.mdc +53 -0
- package/template-vite/.cursor/rules/exxat-reuse-before-custom.mdc +37 -0
- package/template-vite/.cursor/rules/exxat-sidebar-shell.mdc +41 -0
- package/template-vite/.cursor/rules/exxat-table-properties-drawer.mdc +79 -0
- package/template-vite/.cursor/rules/exxat-table-row-preview.mdc +25 -0
- package/template-vite/.cursor/rules/exxat-tabs-chrome.mdc +33 -0
- package/template-vite/.cursor/rules/exxat-token-discipline.mdc +109 -0
- package/template-vite/.cursor/rules/exxat-ux-discovery-protocol.mdc +202 -0
- package/template-vite/.cursor/rules/exxat-ux-principles.mdc +187 -0
- package/template-vite/.cursor/skills/exxat-accessibility/SKILL.md +282 -0
- package/template-vite/.cursor/skills/exxat-board-cards/SKILL.md +68 -0
- package/template-vite/.cursor/skills/exxat-card-vs-list-rows/SKILL.md +20 -0
- package/template-vite/.cursor/skills/exxat-centralized-list-dataset/SKILL.md +99 -0
- package/template-vite/.cursor/skills/exxat-collaboration-access/SKILL.md +35 -0
- package/template-vite/.cursor/skills/exxat-dedicated-search-surfaces/SKILL.md +45 -0
- package/template-vite/.cursor/skills/exxat-drawer-vs-dialog/SKILL.md +20 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/SKILL.md +893 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/references/accessibility.md +142 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/references/coach-marks.md +169 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/references/data-table-pattern.md +392 -0
- package/template-vite/.cursor/skills/exxat-fontawesome-icons/SKILL.md +31 -0
- package/template-vite/.cursor/skills/exxat-kpi-flat-band/SKILL.md +38 -0
- package/template-vite/.cursor/skills/exxat-kpi-max-four/SKILL.md +19 -0
- package/template-vite/.cursor/skills/exxat-kpi-trends/SKILL.md +29 -0
- package/template-vite/.cursor/skills/exxat-list-page-view-shells/SKILL.md +36 -0
- package/template-vite/.cursor/skills/exxat-mono-ids/SKILL.md +56 -0
- package/template-vite/.cursor/skills/exxat-primary-nav-secondary-panel/SKILL.md +49 -0
- package/template-vite/.cursor/skills/exxat-senior-ux/SKILL.md +198 -0
- package/template-vite/.cursor/skills/exxat-token-economy/SKILL.md +287 -0
- package/template-vite/.cursor/skills/exxat-ux-audit/SKILL.md +303 -0
- package/{template → template-vite}/components/ask-leo-sidebar.tsx +10 -8
- package/{template → template-vite}/components/command-menu.tsx +1 -1
- package/{template → template-vite}/components/data-views/library-folder-tree-branch.tsx +1 -1
- package/{template → template-vite}/components/dedicated-search-recents.tsx +1 -1
- package/{template → template-vite}/components/dedicated-search-url-composer.tsx +1 -1
- package/{template → template-vite}/components/library-client.tsx +1 -1
- package/{template → template-vite}/components/library-hub-client.tsx +2 -2
- package/{template → template-vite}/components/library-secondary-nav.tsx +2 -2
- package/{template → template-vite}/components/library-table.tsx +35 -27
- package/{template → template-vite}/components/new-library-item-form.tsx +1 -1
- package/{template → template-vite}/components/page-breadcrumb-trail.tsx +1 -1
- package/{template → template-vite}/components/settings-client.tsx +1 -1
- package/{template → template-vite}/components/sidebar/app-sidebar.tsx +2 -2
- package/{template → template-vite}/components/sidebar/nav-main.tsx +1 -1
- package/{template → template-vite}/components/sidebar/nav-user.tsx +1 -1
- package/{template → template-vite}/components/sidebar/secondary-nav.tsx +1 -1
- package/{template → template-vite}/components/system-banner-slot.tsx +1 -1
- package/{template → template-vite}/components/templates/discovery-hub-template.tsx +2 -2
- package/{template → template-vite}/components/templates/new-focus-template.tsx +1 -1
- package/{template → template-vite}/components/tokens-secondary-nav.tsx +2 -2
- package/{template → template-vite}/components/tokens-themes-client.tsx +1 -1
- package/{template → template-vite}/hooks/use-secondary-panel-hub-nav.ts +1 -1
- package/template-vite/index.html +49 -0
- package/template-vite/lib/next-compat.tsx +98 -0
- package/{template → template-vite}/package.json +15 -27
- package/template-vite/scripts/port-next-imports.mjs +70 -0
- package/template-vite/src/App.tsx +103 -0
- package/template-vite/src/main.tsx +50 -0
- package/{template/app/(app)/error.tsx → template-vite/src/pages/_error.tsx} +12 -24
- package/{template/app/(app)/loading.tsx → template-vite/src/pages/_loading.tsx} +4 -2
- package/template-vite/src/pages/_not-found.tsx +17 -0
- package/template-vite/src/pages/dashboard.tsx +48 -0
- package/{template/app/(app)/help/page.tsx → template-vite/src/pages/help.tsx} +3 -2
- package/{template/app/(app)/library/layout.tsx → template-vite/src/pages/library/_layout.tsx} +18 -16
- package/{template/app/(app)/library/all/page.tsx → template-vite/src/pages/library/all.tsx} +1 -1
- package/{template/app/(app)/library/new/page.tsx → template-vite/src/pages/library/new.tsx} +12 -18
- package/template-vite/src/routes.tsx +72 -0
- package/{template/app → template-vite/src/styles}/globals.css +6 -2
- package/{template → template-vite}/tsconfig.json +5 -14
- package/template-vite/vite.config.ts +52 -0
- package/consumer-extras/cursor-rules/exxat-dashboard-view-charts.mdc +0 -53
- package/template/.agents/skills/shadcn/SKILL.md +0 -242
- package/template/.agents/skills/shadcn/agents/openai.yml +0 -5
- package/template/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/template/.agents/skills/shadcn/assets/shadcn.png +0 -0
- package/template/.agents/skills/shadcn/cli.md +0 -257
- package/template/.agents/skills/shadcn/customization.md +0 -202
- package/template/.agents/skills/shadcn/evals/evals.json +0 -47
- package/template/.agents/skills/shadcn/mcp.md +0 -94
- package/template/.agents/skills/shadcn/rules/base-vs-radix.md +0 -306
- package/template/.agents/skills/shadcn/rules/composition.md +0 -195
- package/template/.agents/skills/shadcn/rules/forms.md +0 -192
- package/template/.agents/skills/shadcn/rules/icons.md +0 -101
- package/template/.agents/skills/shadcn/rules/styling.md +0 -162
- package/template/.cursor/rules/exxat-accessibility.mdc +0 -33
- package/template/.cursor/rules/exxat-data-tables.mdc +0 -32
- package/template/.cursor/rules/exxat-ds-agents.mdc +0 -26
- package/template/.cursor/rules/exxat-list-page-connected-views.mdc +0 -16
- package/template/.cursor/rules/exxat-table-properties-drawer.mdc +0 -40
- package/template/.nvmrc +0 -1
- package/template/.prettierignore +0 -7
- package/template/Logo/Exxat_Prism.svg +0 -39
- package/template/Logo/Exxat_one.svg +0 -36
- package/template/app/(app)/dashboard/loading.tsx +0 -18
- package/template/app/(app)/dashboard/page.tsx +0 -36
- package/template/app/(app)/layout.tsx +0 -77
- package/template/app/global-error.tsx +0 -63
- package/template/app/layout.tsx +0 -133
- package/template/app/page.tsx +0 -9
- package/template/docs/HANDBOOK.md +0 -187
- package/template/docs/blueprints/README.md +0 -86
- package/template/docs/blueprints/_template.md +0 -91
- package/template/docs/blueprints/board-card.md +0 -123
- package/template/docs/blueprints/data-table.md +0 -139
- package/template/docs/blueprints/key-metrics.md +0 -128
- package/template/docs/blueprints/list-page-template.md +0 -123
- package/template/docs/blueprints/page-header.md +0 -130
- package/template/docs/card-vs-rows-pattern.md +0 -36
- package/template/docs/collaboration-access-pattern.md +0 -116
- package/template/docs/command-menu-pattern.md +0 -45
- package/template/docs/component-selection-guide.md +0 -224
- package/template/docs/components-audit-2026-05.md +0 -158
- package/template/docs/consumer-upgrade-checklist.md +0 -52
- package/template/docs/data-views-pattern.md +0 -185
- package/template/docs/drawer-vs-dialog-pattern.md +0 -50
- package/template/docs/glossary.md +0 -59
- package/template/docs/hub-supported-views-pattern.md +0 -53
- package/template/docs/jobs/README.md +0 -59
- package/template/docs/jobs/record-detail.md +0 -177
- package/template/docs/kpi-flat-band-pattern.md +0 -57
- package/template/docs/kpi-strip-max-four-pattern.md +0 -30
- package/template/docs/kpi-trend-pattern.md +0 -58
- package/template/docs/large-dataset-strategy.md +0 -155
- package/template/docs/library-hub-header-pattern.md +0 -25
- package/template/docs/migrations/0001-brand-deep-alias-stabilization.md +0 -95
- package/template/docs/migrations/0002-exxat-token-namespace.md +0 -154
- package/template/docs/migrations/0003-globals-css-canonical.md +0 -110
- package/template/docs/migrations/README.md +0 -100
- package/template/docs/migrations/_template.md +0 -64
- package/template/docs/modern-saas-patterns.md +0 -165
- package/template/docs/perf-memory-pattern.md +0 -206
- package/template/docs/reference-implementations.md +0 -153
- package/template/docs/shell-surface-elevation-pattern.md +0 -52
- package/template/docs/token-taxonomy.md +0 -416
- package/template/docs/voice-and-tone.md +0 -262
- package/template/ecosystem.config.cjs +0 -32
- package/template/next.config.mjs +0 -216
- package/template/postcss.config.mjs +0 -8
- package/template/public/favicon/favicon.ico +0 -0
- package/template/tests/setup.ts +0 -26
- package/template/vitest.config.ts +0 -18
- /package/{template → template-vite}/.cursor/rules/exxat-dashboard-view-charts.mdc +0 -0
- /package/{template → template-vite}/.prettierrc +0 -0
- /package/{template → template-vite}/AGENTS.md +0 -0
- /package/{template → template-vite}/README.md +0 -0
- /package/{template → template-vite}/components/.gitkeep +0 -0
- /package/{template → template-vite}/components/ask-leo-composer.tsx +0 -0
- /package/{template → template-vite}/components/brand-color-picker.tsx +0 -0
- /package/{template → template-vite}/components/chart-area-interactive.tsx +0 -0
- /package/{template → template-vite}/components/charts-overview.tsx +0 -0
- /package/{template → template-vite}/components/collaboration-access-flow.tsx +0 -0
- /package/{template → template-vite}/components/columns-client.tsx +0 -0
- /package/{template → template-vite}/components/columns-showcase.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-promo-banner.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-quota-progress-card.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-report-charts.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-section-heading.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-tabs.tsx +0 -0
- /package/{template → template-vite}/components/data-table/filter-date-calendar.tsx +0 -0
- /package/{template → template-vite}/components/data-table/filter-text-value-input.tsx +0 -0
- /package/{template → template-vite}/components/data-table/index.tsx +0 -0
- /package/{template → template-vite}/components/data-table/pagination.tsx +0 -0
- /package/{template → template-vite}/components/data-table/types.ts +0 -0
- /package/{template → template-vite}/components/data-table/use-table-state.test.ts +0 -0
- /package/{template → template-vite}/components/data-table/use-table-state.ts +0 -0
- /package/{template → template-vite}/components/data-views/board-card-primitives.tsx +0 -0
- /package/{template → template-vite}/components/data-views/data-row-list.tsx +0 -0
- /package/{template → template-vite}/components/data-views/finder-panel-view.tsx +0 -0
- /package/{template → template-vite}/components/data-views/folder-grid-view.tsx +0 -0
- /package/{template → template-vite}/components/data-views/hub-table.tsx +0 -0
- /package/{template → template-vite}/components/data-views/index.ts +0 -0
- /package/{template → template-vite}/components/data-views/list-page-board-card.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-board-template.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-connected-view-body.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-split-details-placeholder.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-split-hub-chrome.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-split-hub-tokens.ts +0 -0
- /package/{template → template-vite}/components/data-views/list-page-tree-column-header.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-tree-panel-shell.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-view-frame.tsx +0 -0
- /package/{template → template-vite}/components/data-views/os-folder-glyph.tsx +0 -0
- /package/{template → template-vite}/components/data-views/outline-tree-menu.tsx +0 -0
- /package/{template → template-vite}/components/data-views/table-cells.tsx +0 -0
- /package/{template → template-vite}/components/dev-chunk-load-recovery.tsx +0 -0
- /package/{template → template-vite}/components/export-drawer.test.tsx +0 -0
- /package/{template → template-vite}/components/export-drawer.tsx +0 -0
- /package/{template → template-vite}/components/exxat-product-logo.tsx +0 -0
- /package/{template → template-vite}/components/folder-details-shell.tsx +0 -0
- /package/{template → template-vite}/components/form-layout-01.tsx +0 -0
- /package/{template → template-vite}/components/hub-tree-panel-view.tsx +0 -0
- /package/{template → template-vite}/components/invite-collaborators-drawer.tsx +0 -0
- /package/{template → template-vite}/components/key-metrics-ask-leo-bridge.tsx +0 -0
- /package/{template → template-vite}/components/key-metrics.tsx +0 -0
- /package/{template → template-vite}/components/leo-insight-indicator.tsx +0 -0
- /package/{template → template-vite}/components/leo-typing-dots.tsx +0 -0
- /package/{template → template-vite}/components/library-board-view.tsx +0 -0
- /package/{template → template-vite}/components/library-dashboard-charts.tsx +0 -0
- /package/{template → template-vite}/components/library-favorite-button.tsx +0 -0
- /package/{template → template-vite}/components/library-new-folder-sheet.tsx +0 -0
- /package/{template → template-vite}/components/library-os-folder-view.tsx +0 -0
- /package/{template → template-vite}/components/library-page-header.tsx +0 -0
- /package/{template → template-vite}/components/library-panel-activator.tsx +0 -0
- /package/{template → template-vite}/components/list-hub-status-badge.tsx +0 -0
- /package/{template → template-vite}/components/list-page-dashboard-charts.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/getting-started.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/index.ts +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-01.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-02.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-03.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-04.tsx +0 -0
- /package/{template → template-vite}/components/page-header.tsx +0 -0
- /package/{template → template-vite}/components/product-switcher.tsx +0 -0
- /package/{template → template-vite}/components/product-wordmark.tsx +0 -0
- /package/{template → template-vite}/components/settings-appearance-card.tsx +0 -0
- /package/{template → template-vite}/components/settings-form-row.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/app-sidebar-dynamic.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/index.ts +0 -0
- /package/{template → template-vite}/components/sidebar/nav-documents.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/nav-secondary.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/secondary-panel.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/sidebar-auto-collapse.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/sidebar-auto-open.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/sidebar-shell.tsx +0 -0
- /package/{template → template-vite}/components/site-header.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/column-row.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/draggable-list.ts +0 -0
- /package/{template → template-vite}/components/table-properties/drawer-button.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/drawer.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/filter-card.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/index.ts +0 -0
- /package/{template → template-vite}/components/table-properties/sort-card.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/types.ts +0 -0
- /package/{template → template-vite}/components/task-list-panel.tsx +0 -0
- /package/{template → template-vite}/components/task-priority-badge.tsx +0 -0
- /package/{template → template-vite}/components/templates/dedicated-search-landing-template.tsx +0 -0
- /package/{template → template-vite}/components/templates/dedicated-search-results-template.tsx +0 -0
- /package/{template → template-vite}/components/templates/list-page.tsx +0 -0
- /package/{template → template-vite}/components/templates/nested-secondary-panel-shell.tsx +0 -0
- /package/{template → template-vite}/components/templates/primary-page-template.tsx +0 -0
- /package/{template → template-vite}/components/templates/secondary-panel-hub-template.tsx +0 -0
- /package/{template → template-vite}/components/theme-color-sync.tsx +0 -0
- /package/{template → template-vite}/components/theme-provider.tsx +0 -0
- /package/{template → template-vite}/components/tinted-icon-disc.tsx +0 -0
- /package/{template → template-vite}/components/tokens-hub-auxiliary-views.tsx +0 -0
- /package/{template → template-vite}/components/tokens-themes-section.tsx +0 -0
- /package/{template → template-vite}/components/ui/accordion.tsx +0 -0
- /package/{template → template-vite}/components/ui/ai-thinking-surface.tsx +0 -0
- /package/{template → template-vite}/components/ui/alert-dialog.tsx +0 -0
- /package/{template → template-vite}/components/ui/avatar.tsx +0 -0
- /package/{template → template-vite}/components/ui/badge.tsx +0 -0
- /package/{template → template-vite}/components/ui/banner.tsx +0 -0
- /package/{template → template-vite}/components/ui/breadcrumb.tsx +0 -0
- /package/{template → template-vite}/components/ui/button.tsx +0 -0
- /package/{template → template-vite}/components/ui/calendar.tsx +0 -0
- /package/{template → template-vite}/components/ui/card.tsx +0 -0
- /package/{template → template-vite}/components/ui/chart.tsx +0 -0
- /package/{template → template-vite}/components/ui/checkbox.tsx +0 -0
- /package/{template → template-vite}/components/ui/coach-mark.tsx +0 -0
- /package/{template → template-vite}/components/ui/collapsible.tsx +0 -0
- /package/{template → template-vite}/components/ui/command.tsx +0 -0
- /package/{template → template-vite}/components/ui/context-menu.tsx +0 -0
- /package/{template → template-vite}/components/ui/date-picker-field.tsx +0 -0
- /package/{template → template-vite}/components/ui/dialog.tsx +0 -0
- /package/{template → template-vite}/components/ui/dot-pattern.tsx +0 -0
- /package/{template → template-vite}/components/ui/drag-handle-grip.tsx +0 -0
- /package/{template → template-vite}/components/ui/dropdown-menu.tsx +0 -0
- /package/{template → template-vite}/components/ui/field.tsx +0 -0
- /package/{template → template-vite}/components/ui/form.tsx +0 -0
- /package/{template → template-vite}/components/ui/hover-card.tsx +0 -0
- /package/{template → template-vite}/components/ui/input-group.tsx +0 -0
- /package/{template → template-vite}/components/ui/input-mask.tsx +0 -0
- /package/{template → template-vite}/components/ui/input.tsx +0 -0
- /package/{template → template-vite}/components/ui/kbd.tsx +0 -0
- /package/{template → template-vite}/components/ui/label.tsx +0 -0
- /package/{template → template-vite}/components/ui/leo-icon.tsx +0 -0
- /package/{template → template-vite}/components/ui/payment-card-fields.tsx +0 -0
- /package/{template → template-vite}/components/ui/popover.tsx +0 -0
- /package/{template → template-vite}/components/ui/radio-group.tsx +0 -0
- /package/{template → template-vite}/components/ui/resizable.tsx +0 -0
- /package/{template → template-vite}/components/ui/scroll-area.tsx +0 -0
- /package/{template → template-vite}/components/ui/select.tsx +0 -0
- /package/{template → template-vite}/components/ui/selection-tile-grid.tsx +0 -0
- /package/{template → template-vite}/components/ui/separator.tsx +0 -0
- /package/{template → template-vite}/components/ui/sheet.tsx +0 -0
- /package/{template → template-vite}/components/ui/sidebar.tsx +0 -0
- /package/{template → template-vite}/components/ui/skeleton.tsx +0 -0
- /package/{template → template-vite}/components/ui/slider.tsx +0 -0
- /package/{template → template-vite}/components/ui/sonner.tsx +0 -0
- /package/{template → template-vite}/components/ui/status-badge.tsx +0 -0
- /package/{template → template-vite}/components/ui/table.tsx +0 -0
- /package/{template → template-vite}/components/ui/tabs.tsx +0 -0
- /package/{template → template-vite}/components/ui/textarea.tsx +0 -0
- /package/{template → template-vite}/components/ui/tip.tsx +0 -0
- /package/{template → template-vite}/components/ui/toggle-group.tsx +0 -0
- /package/{template → template-vite}/components/ui/toggle-switch.tsx +0 -0
- /package/{template → template-vite}/components/ui/toggle.tsx +0 -0
- /package/{template → template-vite}/components/ui/tooltip.tsx +0 -0
- /package/{template → template-vite}/components/ui/view-segmented-control.tsx +0 -0
- /package/{template → template-vite}/components.json +0 -0
- /package/{template → template-vite}/contexts/chart-variant-context.tsx +0 -0
- /package/{template → template-vite}/contexts/command-menu-context.tsx +0 -0
- /package/{template → template-vite}/contexts/dashboard-view-context.tsx +0 -0
- /package/{template → template-vite}/contexts/product-context.tsx +0 -0
- /package/{template → template-vite}/contexts/system-banner-context.tsx +0 -0
- /package/{template → template-vite}/eslint.config.mjs +0 -0
- /package/{template → template-vite}/fontawesome-subset.manifest.json +0 -0
- /package/{template → template-vite}/hooks/.gitkeep +0 -0
- /package/{template → template-vite}/hooks/use-app-theme.ts +0 -0
- /package/{template → template-vite}/hooks/use-coach-mark.ts +0 -0
- /package/{template → template-vite}/hooks/use-location-hash.ts +0 -0
- /package/{template → template-vite}/hooks/use-mobile.ts +0 -0
- /package/{template → template-vite}/hooks/use-mod-key-label.ts +0 -0
- /package/{template → template-vite}/hooks/use-sidebar-reflow-zoom.ts +0 -0
- /package/{template → template-vite}/lib/.gitkeep +0 -0
- /package/{template → template-vite}/lib/ask-leo-route-context.ts +0 -0
- /package/{template → template-vite}/lib/chart-keyboard-selection.test.ts +0 -0
- /package/{template → template-vite}/lib/chart-keyboard-selection.ts +0 -0
- /package/{template → template-vite}/lib/chart-line-dash.ts +0 -0
- /package/{template → template-vite}/lib/chunk-load-error.ts +0 -0
- /package/{template → template-vite}/lib/coach-mark-registry.ts +0 -0
- /package/{template → template-vite}/lib/collaborator-access.ts +0 -0
- /package/{template → template-vite}/lib/command-menu-config.ts +0 -0
- /package/{template → template-vite}/lib/command-menu-search-data.ts +0 -0
- /package/{template → template-vite}/lib/conditional-rule-match.ts +0 -0
- /package/{template → template-vite}/lib/dashboard-customize-coach-mark.ts +0 -0
- /package/{template → template-vite}/lib/dashboard-layout-merge.ts +0 -0
- /package/{template → template-vite}/lib/data-list-display-options.ts +0 -0
- /package/{template → template-vite}/lib/data-list-persistence.ts +0 -0
- /package/{template → template-vite}/lib/data-list-view-registry.ts +0 -0
- /package/{template → template-vite}/lib/data-list-view-surface.ts +0 -0
- /package/{template → template-vite}/lib/data-list-view.ts +0 -0
- /package/{template → template-vite}/lib/data-view-dashboard-storage.ts +0 -0
- /package/{template → template-vite}/lib/date-filter.ts +0 -0
- /package/{template → template-vite}/lib/dedicated-search-recents.ts +0 -0
- /package/{template → template-vite}/lib/dedicated-search-url.ts +0 -0
- /package/{template → template-vite}/lib/dev-log.test.ts +0 -0
- /package/{template → template-vite}/lib/dev-log.ts +0 -0
- /package/{template → template-vite}/lib/discovery-hub.ts +0 -0
- /package/{template → template-vite}/lib/editable-target.ts +0 -0
- /package/{template → template-vite}/lib/exxat-palette.json +0 -0
- /package/{template → template-vite}/lib/exxat-palette.ts +0 -0
- /package/{template → template-vite}/lib/floating-sheet-panel.ts +0 -0
- /package/{template → template-vite}/lib/full-hub-supported-views.ts +0 -0
- /package/{template → template-vite}/lib/hub-connected-view-renderers.ts +0 -0
- /package/{template → template-vite}/lib/initials-from-name.ts +0 -0
- /package/{template → template-vite}/lib/library-authoring.ts +0 -0
- /package/{template → template-vite}/lib/library-dedicated-search.ts +0 -0
- /package/{template → template-vite}/lib/library-hub-search.ts +0 -0
- /package/{template → template-vite}/lib/library-nav.ts +0 -0
- /package/{template → template-vite}/lib/library-recent-searches.ts +0 -0
- /package/{template → template-vite}/lib/library-supported-views.ts +0 -0
- /package/{template → template-vite}/lib/list-hub-supported-views.ts +0 -0
- /package/{template → template-vite}/lib/list-page-table-properties.ts +0 -0
- /package/{template → template-vite}/lib/list-status-badges.ts +0 -0
- /package/{template → template-vite}/lib/logo-dev.ts +0 -0
- /package/{template → template-vite}/lib/mailto.ts +0 -0
- /package/{template → template-vite}/lib/mock/dashboard.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-folders.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-header-collaborators.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-inspector.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-kpi.ts +0 -0
- /package/{template → template-vite}/lib/mock/library.ts +0 -0
- /package/{template → template-vite}/lib/mock/navigation.tsx +0 -0
- /package/{template → template-vite}/lib/motion-ui.ts +0 -0
- /package/{template → template-vite}/lib/product-brand.ts +0 -0
- /package/{template → template-vite}/lib/raf-throttle.ts +0 -0
- /package/{template → template-vite}/lib/row-height.ts +0 -0
- /package/{template → template-vite}/lib/sidebar-state-cookie.ts +0 -0
- /package/{template → template-vite}/lib/stock-portrait.ts +0 -0
- /package/{template → template-vite}/lib/table-state-lifecycle.ts +0 -0
- /package/{template → template-vite}/lib/utils.test.ts +0 -0
- /package/{template → template-vite}/lib/utils.ts +0 -0
- /package/{template → template-vite}/public/.gitkeep +0 -0
- /package/{template → template-vite}/public/Illustration/Rotation.svg +0 -0
- /package/{template → template-vite}/public/avatars/user.svg +0 -0
- /package/{template/public → template-vite/public/favicon}/favicon.ico +0 -0
- /package/{template/app → template-vite/public}/favicon.ico +0 -0
- /package/{template → template-vite}/public/folders/icons8-folder-windows-11.svg +0 -0
- /package/{template → template-vite}/public/logos/exxat-one.svg +0 -0
- /package/{template → template-vite}/public/logos/exxat-prism.svg +0 -0
- /package/{template → template-vite}/public/mock-schools/emory.svg +0 -0
- /package/{template → template-vite}/public/mock-schools/rush.svg +0 -0
- /package/{template → template-vite}/scripts/fontawesome-subset-audit.mjs +0 -0
- /package/{template → template-vite}/scripts/pm2-startup-macos.sh +0 -0
- /package/{template → template-vite}/skills-lock.json +0 -0
- /package/{template/app/(app)/columns/page.tsx → template-vite/src/pages/columns.tsx} +0 -0
- /package/{template/app/(app)/library/find/page.tsx → template-vite/src/pages/library/find.tsx} +0 -0
- /package/{template/app/(app)/library/page.tsx → template-vite/src/pages/library/index.tsx} +0 -0
- /package/{template/app/(app)/library/list/page.tsx → template-vite/src/pages/library/list.tsx} +0 -0
- /package/{template/app/(app)/settings/page.tsx → template-vite/src/pages/settings.tsx} +0 -0
- /package/{template/app/(app)/tokens-themes/page.tsx → template-vite/src/pages/tokens-themes.tsx} +0 -0
- /package/{template → template-vite}/stores/app-store.ts +0 -0
- /package/{template → template-vite}/types/react-payment-inputs.d.ts +0 -0
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
# 0003 — `packages/ui/src/globals.css` is now the only CSS source of truth
|
|
2
|
-
|
|
3
|
-
**Status:** Landed
|
|
4
|
-
**Date:** 2026-05-19
|
|
5
|
-
**Author:** Exxat DS hardening sweep
|
|
6
|
-
**Breaking?** Yes for any external consumer of `@exxatdesignux/ui` that
|
|
7
|
-
imported `@exxatdesignux/ui/theme.css` directly. The package is **not yet
|
|
8
|
-
shipped**, so customer impact is zero. Internal consumers updated as part
|
|
9
|
-
of this PR.
|
|
10
|
-
|
|
11
|
-
## Why
|
|
12
|
-
|
|
13
|
-
Until now, the design system kept the same theme tokens duplicated across
|
|
14
|
-
**four** CSS files:
|
|
15
|
-
|
|
16
|
-
| File | Purpose | Status |
|
|
17
|
-
|---|---|---|
|
|
18
|
-
| `packages/ui/src/theme.css` | "thin" package CSS without Tailwind imports | dead — never `@import`-ed by code |
|
|
19
|
-
| `packages/ui/src/globals.css` | "full" package CSS (Tailwind imports + tokens) | drifted (older `--input` value, missing `--header-height`) |
|
|
20
|
-
| `apps/web/app/globals.css` | consumer of the package | up-to-date copy |
|
|
21
|
-
| `packages/ui/template/app/globals.css` | starter template (auto-synced from apps/web) | up-to-date copy |
|
|
22
|
-
|
|
23
|
-
Each token change required up to **three** manual mirror edits, and the
|
|
24
|
-
files had **already silently drifted**: `--input` value differed between
|
|
25
|
-
the package and apps/web (one was `oklch(0.62 0.01 264.52)`, the other
|
|
26
|
-
`oklch(0.6694 0.0063 264.52)`).
|
|
27
|
-
|
|
28
|
-
This is exactly the maintenance trap SLDS deliberately avoids by having
|
|
29
|
-
one source file. Now that the package is not yet shipped, the breaking
|
|
30
|
-
change is free.
|
|
31
|
-
|
|
32
|
-
## What changed
|
|
33
|
-
|
|
34
|
-
1. **`packages/ui/src/globals.css` is now canonical.** It carries
|
|
35
|
-
`@import "tailwindcss"`, `@import "tw-animate-css"`, the
|
|
36
|
-
`@custom-variant` declarations, the full `@theme inline` block (L2),
|
|
37
|
-
every `:root` / `.dark` / `.theme-*` / `[data-contrast="high"]` block
|
|
38
|
-
(L0 + L1 + L3), the `@layer base` rules, and the `@font-face`
|
|
39
|
-
declarations. No `@source` directive — that lives in the consumer.
|
|
40
|
-
|
|
41
|
-
2. **`apps/web/app/globals.css` is now a 20-line shell.** It does:
|
|
42
|
-
```css
|
|
43
|
-
@import "@exxatdesignux/ui/globals.css";
|
|
44
|
-
@source "../../../packages/ui/src";
|
|
45
|
-
```
|
|
46
|
-
Nothing else. All previous content moved into the package.
|
|
47
|
-
|
|
48
|
-
3. **`packages/ui/template/app/globals.css` is the same shell** with the
|
|
49
|
-
`@source` path patched to `../node_modules/@exxatdesignux/ui/src` (the
|
|
50
|
-
`sync-template-from-web.mjs` script already does this rewrite).
|
|
51
|
-
|
|
52
|
-
4. **`packages/ui/src/theme.css` is deleted.** It was a stale, smaller
|
|
53
|
-
copy of `globals.css` that no code `@import`-ed (only docs referenced
|
|
54
|
-
it). The `./theme.css` entry was removed from `package.json` exports.
|
|
55
|
-
|
|
56
|
-
5. **`packages/ui/scripts/build-tokens-index.mjs` now parses
|
|
57
|
-
`globals.css` only** (instead of theme.css + globals.css). Token
|
|
58
|
-
count went from 163 → **195** because the canonical file now captures
|
|
59
|
-
tokens that previously lived only in `apps/web/app/globals.css`
|
|
60
|
-
(`--header-height`, `--theme-color-chrome`, scoped overlays, etc.).
|
|
61
|
-
Namespace count went from 36 → **41**.
|
|
62
|
-
|
|
63
|
-
## Migration recipe
|
|
64
|
-
|
|
65
|
-
### Internal consumers (this monorepo)
|
|
66
|
-
|
|
67
|
-
Nothing to do. The shells already `@import "@exxatdesignux/ui/globals.css"`.
|
|
68
|
-
|
|
69
|
-
### External consumers (post-publish)
|
|
70
|
-
|
|
71
|
-
If you previously had:
|
|
72
|
-
```css
|
|
73
|
-
/* app/globals.css */
|
|
74
|
-
@import "tailwindcss";
|
|
75
|
-
@import "tw-animate-css";
|
|
76
|
-
@import "@exxatdesignux/ui/theme.css"; /* ❌ no longer exported */
|
|
77
|
-
@source "...";
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Change it to:
|
|
81
|
-
```css
|
|
82
|
-
/* app/globals.css */
|
|
83
|
-
@import "@exxatdesignux/ui/globals.css"; /* brings Tailwind + tw-animate-css + tokens + base layer */
|
|
84
|
-
@source "../node_modules/@exxatdesignux/ui/src";
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
The package globals.css now carries the `@import "tailwindcss"` /
|
|
88
|
-
`@import "tw-animate-css"` for you — your shell only needs `@source`
|
|
89
|
-
plus any app-specific additions.
|
|
90
|
-
|
|
91
|
-
## Verification
|
|
92
|
-
|
|
93
|
-
- `pnpm --filter @exxatdesignux/ui tokens:index` → 195 tokens / 41 namespaces, no errors.
|
|
94
|
-
- `pnpm --filter @exxat-ds/web lint` → no new violations.
|
|
95
|
-
- Grep for stale `theme.css` references → only this migration + the 0002 link.
|
|
96
|
-
|
|
97
|
-
## Follow-ups (optional)
|
|
98
|
-
|
|
99
|
-
- Document the canonical-file rule in `apps/web/AGENTS.md` § token
|
|
100
|
-
taxonomy (already updated).
|
|
101
|
-
- Add a CI check to `tokens:check` that fails when the consumer shell
|
|
102
|
-
contains anything other than `@import` + `@source` + comments, so the
|
|
103
|
-
shell stays thin.
|
|
104
|
-
|
|
105
|
-
## See also
|
|
106
|
-
|
|
107
|
-
- [`docs/token-taxonomy.md`](../token-taxonomy.md) — layered token model.
|
|
108
|
-
- [`0002-exxat-token-namespace.md`](./0002-exxat-token-namespace.md) — the
|
|
109
|
-
L0 namespace introduction (which now lives in `globals.css`, not
|
|
110
|
-
`theme.css`).
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# Exxat DS — Migrations
|
|
2
|
-
|
|
3
|
-
**Inspired by:** SLDS [v1 → v2 uplift skill](https://www.lightningdesignsystem.com/2e1ef9c7e/v/1604/p/9929fa-uplift-skill).
|
|
4
|
-
|
|
5
|
-
Every breaking change in `@exxatdesignux/ui` (token rename, component prop
|
|
6
|
-
removal, deprecated pattern) lives here as a numbered migration entry. The
|
|
7
|
-
**first entry of any release that breaks API or tokens** must land alongside
|
|
8
|
-
the change.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## How migrations work
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
docs/migrations/
|
|
16
|
-
├── README.md ← this file (active deprecations + index)
|
|
17
|
-
└── NNNN-<slug>.md ← one file per migration
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
| Field | Meaning |
|
|
21
|
-
|---|---|
|
|
22
|
-
| `NNNN` | Zero-padded 4-digit sequence number (`0001`, `0002`, …) |
|
|
23
|
-
| `<slug>` | Short kebab-case identifier — what is changing |
|
|
24
|
-
|
|
25
|
-
**One file per migration.** Two unrelated changes in the same release get two
|
|
26
|
-
files.
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## Lifecycle of a deprecation
|
|
31
|
-
|
|
32
|
-
1. **Plan** — Open a migration file even before the release ships. Author the
|
|
33
|
-
`Why` and `Before/After` sections; leave `Released in` blank.
|
|
34
|
-
2. **Announce** — When the change lands, fill `Released in` (e.g. `v0.3.0`),
|
|
35
|
-
add the `@deprecated` comment in `packages/ui/src/globals.css` or the component file:
|
|
36
|
-
```css
|
|
37
|
-
/* @deprecated v0.3.0 — use --brand-color-dark; remove in v0.4.0 */
|
|
38
|
-
```
|
|
39
|
-
Make sure `pnpm --filter @exxatdesignux/ui tokens:index` picks it up
|
|
40
|
-
(`tokens/hooks-index.json` will mark `deprecated: true`).
|
|
41
|
-
3. **Migrate** — Land the migration script (if scriptable) under
|
|
42
|
-
`packages/ui/scripts/migrate-NNNN-<slug>.mjs`; otherwise document the manual
|
|
43
|
-
grep + replace.
|
|
44
|
-
4. **Remove** — When the deprecation window closes (named in the comment),
|
|
45
|
-
delete the deprecated token / component. Set `Removed in` in the migration
|
|
46
|
-
file. The file stays in the repo forever as a record.
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## Active deprecations
|
|
51
|
-
|
|
52
|
-
> _None yet._ Update this table when announcing a deprecation.
|
|
53
|
-
|
|
54
|
-
| Migration | Token / Component | Deprecated in | Remove in |
|
|
55
|
-
|---|---|---|---|
|
|
56
|
-
| — | — | — | — |
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## Migration template
|
|
61
|
-
|
|
62
|
-
Copy [`_template.md`](./_template.md) when adding a migration:
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
# Migration NNNN — <Title>
|
|
66
|
-
|
|
67
|
-
> **Released in:** vX.Y.Z · **Removed in:** vX.Y.Z (or "planned") · **Owner:** <person/team>
|
|
68
|
-
|
|
69
|
-
## Why
|
|
70
|
-
…
|
|
71
|
-
## Affected surface
|
|
72
|
-
…
|
|
73
|
-
## Before
|
|
74
|
-
…
|
|
75
|
-
## After
|
|
76
|
-
…
|
|
77
|
-
## Migration recipe
|
|
78
|
-
…
|
|
79
|
-
## Verification
|
|
80
|
-
…
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## Index
|
|
86
|
-
|
|
87
|
-
| # | Slug | Released | Removed |
|
|
88
|
-
|---|---|---|---|
|
|
89
|
-
| [0001](./0001-brand-deep-alias-stabilization.md) | `brand-deep-alias-stabilization` | v0.2.18 | — (no removal — stabilization) |
|
|
90
|
-
| [0002](./0002-exxat-token-namespace.md) | `exxat-token-namespace` | v0.2.19 | — (additive — L1 stays) |
|
|
91
|
-
| [0003](./0003-globals-css-canonical.md) | `globals-css-canonical` | v0.2.20 | `theme.css` removed |
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## See also
|
|
96
|
-
|
|
97
|
-
- [`token-taxonomy.md`](../token-taxonomy.md) — naming + deprecation policy (§5)
|
|
98
|
-
- [`packages/ui/CHANGELOG.md`](../../../packages/ui/CHANGELOG.md) — release notes
|
|
99
|
-
- [`.cursor/rules/exxat-token-discipline.mdc`](../../../.cursor/rules/exxat-token-discipline.mdc) — lint rule for deprecated tokens
|
|
100
|
-
- [`packages/ui/tokens/hooks-index.json`](../../../packages/ui/tokens/hooks-index.json) — machine-readable index (includes `deprecated: true`)
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# Migration NNNN — <Title>
|
|
2
|
-
|
|
3
|
-
> **Released in:** vX.Y.Z · **Removed in:** vX.Y.Z (or _planned_) · **Owner:** <person / team>
|
|
4
|
-
> **Type:** token rename · component rename · prop removal · pattern deprecation · stabilization
|
|
5
|
-
|
|
6
|
-
## Why
|
|
7
|
-
|
|
8
|
-
One paragraph. Explain the user-visible or developer-visible problem this
|
|
9
|
-
migration addresses.
|
|
10
|
-
|
|
11
|
-
## Affected surface
|
|
12
|
-
|
|
13
|
-
| Surface | Was | Becomes |
|
|
14
|
-
|---|---|---|
|
|
15
|
-
| Tailwind utility | `text-foo` | `text-bar` |
|
|
16
|
-
| CSS custom prop | `--foo` | `--bar` |
|
|
17
|
-
| React component | `<OldName />` | `<NewName />` |
|
|
18
|
-
| Pattern | … | … |
|
|
19
|
-
|
|
20
|
-
## Before
|
|
21
|
-
|
|
22
|
-
```tsx
|
|
23
|
-
// Old usage
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## After
|
|
27
|
-
|
|
28
|
-
```tsx
|
|
29
|
-
// New usage
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Migration recipe
|
|
33
|
-
|
|
34
|
-
### Automatic
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
# Run from the monorepo root.
|
|
38
|
-
node packages/ui/scripts/migrate-NNNN-<slug>.mjs
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
(Or note "manual only — no script.")
|
|
42
|
-
|
|
43
|
-
### Manual
|
|
44
|
-
|
|
45
|
-
1. Search for `<pattern>` across `apps/web/`, `packages/ui/src/`, and
|
|
46
|
-
`packages/ui/template/`.
|
|
47
|
-
2. Replace per the table above.
|
|
48
|
-
3. Re-run `pnpm --filter @exxatdesignux/ui tokens:index`.
|
|
49
|
-
4. Re-run `pnpm --filter @exxat-ds/web lint`.
|
|
50
|
-
|
|
51
|
-
## Verification
|
|
52
|
-
|
|
53
|
-
- [ ] `pnpm typecheck` clean across workspace
|
|
54
|
-
- [ ] `pnpm --filter @exxatdesignux/ui tokens:check` passes
|
|
55
|
-
- [ ] No remaining matches for `<old pattern>` in code search (excluding this
|
|
56
|
-
migration file)
|
|
57
|
-
- [ ] Visual regression sweep on affected hubs (Placements / Team /
|
|
58
|
-
Compliance / Library)
|
|
59
|
-
|
|
60
|
-
## References
|
|
61
|
-
|
|
62
|
-
- `packages/ui/CHANGELOG.md` (vX.Y.Z)
|
|
63
|
-
- Related rule: `.cursor/rules/<rule>.mdc`
|
|
64
|
-
- Related blueprint: `apps/web/docs/blueprints/<blueprint>.md`
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
# Modern SaaS patterns — the canon Exxat DS works against
|
|
2
|
-
|
|
3
|
-
> **Audience:** humans + AI agents.
|
|
4
|
-
> **Companion to:** [`exxat-senior-ux/SKILL.md`](../../../.cursor/skills/exxat-senior-ux/SKILL.md),
|
|
5
|
-
> [`exxat-ux-principles.mdc`](../../../.cursor/rules/exxat-ux-principles.mdc),
|
|
6
|
-
> [`jobs/`](./jobs/).
|
|
7
|
-
|
|
8
|
-
A senior designer recognizes the work. This doc names the patterns the best
|
|
9
|
-
products converged on in 2024–2026, so the agent can pattern-match instead
|
|
10
|
-
of inventing. **Cite by name + code** when you reference one in a design brief
|
|
11
|
-
(e.g. `Linear issue detail (M1, M4, M7)`).
|
|
12
|
-
|
|
13
|
-
## Canon products — cite by product, not by URL
|
|
14
|
-
|
|
15
|
-
| Product | What to learn from it |
|
|
16
|
-
|---------|-----------------------|
|
|
17
|
-
| **Linear** | Issue detail, command palette spine, density, keyboard-first, presence, "less chrome" |
|
|
18
|
-
| **Notion** | Inline editing, property panels, page-as-database, blocks, slash-commands |
|
|
19
|
-
| **Stripe Dashboard** | Record home, status chips, activity log, money-grade precision |
|
|
20
|
-
| **Figma** | Multiplayer, presence, side panel inspector, infinite-canvas chrome discipline |
|
|
21
|
-
| **Vercel** | Type-first hierarchy, dark-first, minimal chrome, log streams |
|
|
22
|
-
| **Height** | View tabs (table/board/timeline), custom fields, view parity |
|
|
23
|
-
| **Plain / Pylon** | Support detail = identity + timeline + actions; conversation-as-document |
|
|
24
|
-
| **Cron / Amie** | Keyboard-only flows, dense calendar, command surface |
|
|
25
|
-
| **Raycast** | Command palette as full app, extension surface, deep keyboard |
|
|
26
|
-
| **Arc Browser** | Control-stripped, content-first, sidebar as the only chrome |
|
|
27
|
-
|
|
28
|
-
## The 12 patterns that make products feel modern
|
|
29
|
-
|
|
30
|
-
### M1. Content-first chrome
|
|
31
|
-
The data is the star. Sidebars collapse to icons. Headers stay 48–56px. Page
|
|
32
|
-
surfaces are generous. No purple gradient on the top bar.
|
|
33
|
-
|
|
34
|
-
- **In DS:** `SiteHeader` ~h-12; `PrimaryPageTemplate` body has generous
|
|
35
|
-
`max-w-[1440px]`; sidebar collapsible (`SidebarTrigger`).
|
|
36
|
-
|
|
37
|
-
### M2. Command palette as the spine
|
|
38
|
-
`⌘K` opens search + navigation + AI + recent. It is the primary navigation
|
|
39
|
-
for power users. The sidebar exists for orientation, not speed.
|
|
40
|
-
|
|
41
|
-
- **In DS:** `CommandMenu` (⌘K) + Ask Leo split (⌘⌥K).
|
|
42
|
-
Rule: `exxat-command-menu.mdc`.
|
|
43
|
-
|
|
44
|
-
### M3. Side-panel detail over modal dialog
|
|
45
|
-
For non-destructive context-keeping actions (properties, export, invite,
|
|
46
|
-
preview, single-step compose), use a slide-in sheet — never a centered modal.
|
|
47
|
-
|
|
48
|
-
- **In DS:** `Sheet` (no Vaul).
|
|
49
|
-
Rule: `exxat-drawer-vs-dialog.mdc`. Pattern:
|
|
50
|
-
`apps/web/docs/drawer-vs-dialog-pattern.md`.
|
|
51
|
-
|
|
52
|
-
### M4. Status as a first-class citizen
|
|
53
|
-
Colored dots, chips, counts visible everywhere data appears. Status isn't
|
|
54
|
-
hidden in a detail page; it appears on rows, cards, headers, navigation.
|
|
55
|
-
|
|
56
|
-
- **In DS:** `ListHubStatusBadge` + `lib/list-status-badges.ts`. Available per
|
|
57
|
-
row, board card, detail header, breadcrumb count slot.
|
|
58
|
-
|
|
59
|
-
### M5. Inline editing where data is read
|
|
60
|
-
Click the value, edit it. No bounce to forms for single-field changes.
|
|
61
|
-
|
|
62
|
-
- **In DS:** Inline-edit primitive emerging; until then, use a `Sheet` for
|
|
63
|
-
multi-field, popover or contenteditable for single-field. Principle: P15.
|
|
64
|
-
|
|
65
|
-
### M6. Optimistic UI + undo
|
|
66
|
-
Low-risk actions (favorite, archive, status flip) feel instant; reconcile on
|
|
67
|
-
error. Undo via banner or in-place affordance — **never** toast
|
|
68
|
-
(`exxat-no-toast.mdc`).
|
|
69
|
-
|
|
70
|
-
- **In DS:** Optimistic state in `useTableState` selection ops; undo via
|
|
71
|
-
`LocalBanner` with action.
|
|
72
|
-
|
|
73
|
-
### M7. Activity timeline on every record
|
|
74
|
-
Who did what when. The audit log is also a navigation device.
|
|
75
|
-
|
|
76
|
-
- **In DS:** `ActivityTimeline` primitive emerging. Compose from
|
|
77
|
-
`lib/mock/activity` shape until then.
|
|
78
|
-
|
|
79
|
-
### M8. Empty states with one CTA + one sentence
|
|
80
|
-
Illustration optional. One sentence of context. One primary action. Never a
|
|
81
|
-
wall of help text or a tutorial inline.
|
|
82
|
-
|
|
83
|
-
- **In DS:** `EmptyState` primitive; voice from `docs/voice-and-tone.md`.
|
|
84
|
-
|
|
85
|
-
### M9. Skeleton, not spinner
|
|
86
|
-
Loading shapes content. Spinners only for indeterminate < 200ms.
|
|
87
|
-
|
|
88
|
-
- **In DS:** `Skeleton` primitive; suspense boundaries shape the body.
|
|
89
|
-
|
|
90
|
-
### M10. Type-first hierarchy
|
|
91
|
-
Weight, size, color do the heavy lifting before borders, boxes, or tints.
|
|
92
|
-
Ornament is rare and intentional.
|
|
93
|
-
|
|
94
|
-
- **In DS:** Token scale; body in `Inter`; display in `Ivy Presto` (only on
|
|
95
|
-
`PageHeader` H1 by default).
|
|
96
|
-
|
|
97
|
-
### M11. Real-time presence + collaboration as default
|
|
98
|
-
If the product is shared, show who else is here — face rails, cursors,
|
|
99
|
-
status. Inviting others is one click from the page.
|
|
100
|
-
|
|
101
|
-
- **In DS:** `PageHeader variant="collaboration"` face rail +
|
|
102
|
-
`InviteCollaboratorsDrawer`.
|
|
103
|
-
Rule: `exxat-collaboration-access.mdc`.
|
|
104
|
-
|
|
105
|
-
### M12. AI as opt-in sidecar
|
|
106
|
-
Ask Leo style — never the primary path, never auto-runs on a record, always
|
|
107
|
-
discoverable, always cancellable. Deterministic path still exists.
|
|
108
|
-
|
|
109
|
-
- **In DS:** `AskLeoSidebar` + `⌘⌥K` toggle; never a "magic" button on a
|
|
110
|
-
destructive surface.
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## The anti-modern signals (what NOT to do)
|
|
115
|
-
|
|
116
|
-
| Signal | Why it's dated |
|
|
117
|
-
|--------|----------------|
|
|
118
|
-
| Toasts everywhere | Modern products use inline + banner + undo. Toasts get missed. |
|
|
119
|
-
| Full-width tab bars stretched edge-to-edge | 2010s pattern. Modern uses `w-fit` segmented controls. |
|
|
120
|
-
| Centered modals for everything | Sheets and routes carry more without breaking flow. |
|
|
121
|
-
| Color-coded sidebar sections | Modern is monochrome chrome + status color on data. |
|
|
122
|
-
| Modal wizards with progress bar | If it has > 3 steps, give it a route. |
|
|
123
|
-
| Spinners on initial load | Skeleton the content shape instead. |
|
|
124
|
-
| Hover-only affordances | Touch + keyboard need parity. |
|
|
125
|
-
| Icons without text labels in primary nav | Recognition fails for new users. |
|
|
126
|
-
| "Beautiful" gradients on chrome | Data should be beautiful; chrome should disappear. |
|
|
127
|
-
| Aggressive empty-state illustrations | One sentence + one action beats illustration + 5 tips. |
|
|
128
|
-
| Edit-takes-you-to-a-form for single fields | Inline edit (M5). |
|
|
129
|
-
| "Back to <parent>" button alongside breadcrumb | P1 — choose one. |
|
|
130
|
-
| Auto-running AI on record open | M12 — opt-in only. |
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
## Density layers (Linear / Vercel model)
|
|
135
|
-
|
|
136
|
-
| Layer | When |
|
|
137
|
-
|-------|------|
|
|
138
|
-
| **Cozy** (default) | Most surfaces; balances scan + breathing room |
|
|
139
|
-
| **Compact** | Power users on daily workflow; tables, command results |
|
|
140
|
-
| **Comfortable** | Accessibility (low-vision, motor); marketing-adjacent |
|
|
141
|
-
|
|
142
|
-
Provide a user-level setting where audiences differ; default to **cozy**.
|
|
143
|
-
Don't fork stacks per density.
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
## How to use this doc in a design brief
|
|
148
|
-
|
|
149
|
-
```
|
|
150
|
-
Reference (modern): Linear issue detail (M1, M4, M7),
|
|
151
|
-
Stripe customer record (M4, M11)
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Cite the **patterns (Mx)** the agent applied, not just the product. Forces
|
|
155
|
-
clear thinking and lets reviewers verify intent.
|
|
156
|
-
|
|
157
|
-
## See also
|
|
158
|
-
|
|
159
|
-
- [`../../../.cursor/skills/exxat-senior-ux/SKILL.md`](../../../.cursor/skills/exxat-senior-ux/SKILL.md) — persona
|
|
160
|
-
- [`../../../.cursor/rules/exxat-ux-discovery-protocol.mdc`](../../../.cursor/rules/exxat-ux-discovery-protocol.mdc) — brief gate
|
|
161
|
-
- [`../../../.cursor/rules/exxat-ux-principles.mdc`](../../../.cursor/rules/exxat-ux-principles.mdc) — principles + breaks
|
|
162
|
-
- [`./jobs/`](./jobs/) — canonical reference per job type
|
|
163
|
-
- [`./component-selection-guide.md`](./component-selection-guide.md) — picking the composition
|
|
164
|
-
- [`./blueprints/`](./blueprints/) — framework-agnostic surface specs
|
|
165
|
-
- [`./voice-and-tone.md`](./voice-and-tone.md) — copy rules
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
# Dev memory tuning for Next.js + Node 24
|
|
2
|
-
|
|
3
|
-
> **Audience:** humans + AI agents.
|
|
4
|
-
> **Companion to:** [`HANDBOOK.md`](./HANDBOOK.md). Read this when `next dev`
|
|
5
|
-
> RSS climbs past ~3 GB, when two `next-server` processes total > 5 GB, or
|
|
6
|
-
> when adopting Node 24.
|
|
7
|
-
|
|
8
|
-
A fresh `next dev` against this app stabilizes around **~1.4 GB RSS** with
|
|
9
|
-
the settings below. Without them, the same app drifts to **3–6 GB per
|
|
10
|
-
process** and pm2 will eventually swap or OOM.
|
|
11
|
-
|
|
12
|
-
## 1. The six knobs
|
|
13
|
-
|
|
14
|
-
| # | Knob | Why it matters | Where it lives |
|
|
15
|
-
|---|------|----------------|----------------|
|
|
16
|
-
| 1 | `NODE_OPTIONS="--max-old-space-size=6144 --max-semi-space-size=64"` | Caps V8 old-space at 6 GB (default on macOS is ~94% of system RAM = unbounded for practical purposes). With a ceiling, V8 GC pressure kicks in earlier and steady-state heap is lower. `--max-semi-space-size=64` widens the young generation so short-lived render allocations don't promote to old-space. | `package.json` `dev*` scripts + `ecosystem.config.cjs` `env` |
|
|
17
|
-
| 2 | `turbopack.memoryLimit: 4 GiB` | Hard cap on the Turbopack worker process. Without this, Turbopack's module graph + mmap'd FS cache files grow unbounded — we observed 3.2 GB on disk and 5+ GB RSS per process with no cap. 4 GiB is generous for apps with < ~1000 routes. | `next.config.mjs` `turbopack` |
|
|
18
|
-
| 3 | `experimental.preloadEntriesOnStart: false` | Next compiles routes on first visit instead of pre-warming every entry on dev start. Dev TTFB drops from ~15s → ~2s; steady-state heap is ~30% lower. | `next.config.mjs` |
|
|
19
|
-
| 4 | `experimental.optimizePackageImports: [...]` | Re-export barrels (`lucide-react`, `@tabler/icons-react`, `motion`, `@dnd-kit/*`, `recharts`) get tree-shaken to leaf imports. Cuts the dev server's parsed-module count by ~40%. | `next.config.mjs` |
|
|
20
|
-
| 5 | `experimental.webpackMemoryOptimizations: true` | Drops large in-memory webpack caches at the cost of slightly slower rebuilds. **Only the `pnpm dev:webpack` fallback uses webpack** — Turbopack ignores this flag. Keep it on for the rare cases where the webpack path is needed. | `next.config.mjs` |
|
|
21
|
-
| 6 | `target: ES2022` (tsconfig) | The TS compiler emits less polyfill scaffolding for `async/await`, optional chaining, nullish coalescing, etc. tsserver in-memory AST shrinks proportionally. Safe with React 19 + Next 16 + Node 24. | `tsconfig.json` |
|
|
22
|
-
|
|
23
|
-
## 2. NODE_OPTIONS propagation
|
|
24
|
-
|
|
25
|
-
`NODE_OPTIONS` is read by **every Node process** the moment it boots. The npm
|
|
26
|
-
parent doesn't read it (npm is a shell wrapper), but its child `next-server`
|
|
27
|
-
processes inherit and honour it.
|
|
28
|
-
|
|
29
|
-
- **Local dev:** set inline in `package.json` `scripts`. Cross-shell safe
|
|
30
|
-
because Next dev is only run from POSIX shells.
|
|
31
|
-
- **PM2 daemon:** set in `ecosystem.config.cjs` `env` (which becomes the child
|
|
32
|
-
environment).
|
|
33
|
-
- **CI:** set in the workflow `env:` block on the same step that runs `next`.
|
|
34
|
-
- **VS Code / Cursor terminals:** these inherit the parent shell env, so the
|
|
35
|
-
`package.json` script is enough.
|
|
36
|
-
|
|
37
|
-
## 3. Turbopack file-system cache (Next ≥ 16.1)
|
|
38
|
-
|
|
39
|
-
Since Next 16.1, **`experimental.turbopackFileSystemCacheForDev` defaults to `true`** — Turbopack writes compilation artifacts to `.next/dev/cache/turbopack/<hash>/*.meta` and mmaps them across dev sessions. This is what makes the second `next dev` start in ~1 s instead of ~15 s.
|
|
40
|
-
|
|
41
|
-
**The trade-off** is that the cache grows linearly with the number of unique routes / modules you've touched. After a few weeks of feature work it's normal to see:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
du -sh .next
|
|
45
|
-
# 3.2G .next
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Each `.meta` file is mmap'd by the running `next-server`, so the cache size is roughly the floor of the dev process's RSS until the OS evicts pages.
|
|
49
|
-
|
|
50
|
-
**Do not disable** the FS cache (`turbopackFileSystemCacheForDev: false`) — cold-start dev becomes painful (~15–30 s every restart on this app). Instead, **bust the cache** when it grows past 1–2 GB:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
pnpm clean:cache # removes .next/dev/cache and .next/dev/trace
|
|
54
|
-
pnpm dev:fresh # bust + restart in one command
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
The `dev:fresh` script is the right move whenever:
|
|
58
|
-
|
|
59
|
-
- A pnpm install changed `@exxatdesignux/ui` or any framework dep.
|
|
60
|
-
- `.next` is > 2 GB and dev memory is climbing.
|
|
61
|
-
- HMR starts skipping updates or compilation gets stuck on a stale module.
|
|
62
|
-
|
|
63
|
-
For a full nuke (build artifacts too):
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
pnpm clean
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
`turbopack.memoryLimit` (knob #2) prevents the cache from blowing past 4 GiB of RAM even when the on-disk cache is large.
|
|
70
|
-
|
|
71
|
-
## 4. Don't run two dev servers at the same time
|
|
72
|
-
|
|
73
|
-
**This is the #1 cause of memory exhaustion in practice.** A single `next-server` (parent + render worker) stabilizes at ~2 GB total RSS with the knobs above. Two parallel servers stabilize at ~4 GB, three at ~6 GB, and so on — they don't share any caches.
|
|
74
|
-
|
|
75
|
-
If you see two `next-server` lineages in `ps`, check:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
ps aux | grep next-server | grep -v grep
|
|
79
|
-
lsof -p <pid> | grep '\.next/dev/cache' | head -5 # which checkout owns it
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Common dual-server scenarios:
|
|
83
|
-
|
|
84
|
-
| Scenario | Symptom | Fix |
|
|
85
|
-
|----------|---------|-----|
|
|
86
|
-
| Two checkouts of the same monorepo (e.g. `DS_Workspace/` and `Exxat-DS-Workspace/`) both running `pnpm dev:web` | Two `next-server` parents, both in `apps/web/.next/...` paths but on different absolute roots | Quit one. Pin to a single checkout per machine. |
|
|
87
|
-
| A customer app (e.g. `test-9`) + the monorepo `apps/web` running at the same time | Different cache hashes (`ee6e79b1/`) under different roots | Stop whichever you're not actively touching: `pm2 stop exxat-ds` or `Ctrl+C` |
|
|
88
|
-
| A stale pm2 daemon from a prior `nvm use 22` session left running after upgrading to Node 24 | One Node-22 + one Node-24 dev server | `pm2 delete exxat-ds` then `pnpm dev:daemon` to re-launch under Node 24 |
|
|
89
|
-
| `next build` running in another tab while `next dev` is up | Three or four `next-server` for the duration of the build | Wait for the build, or kill the dev server until the build is done |
|
|
90
|
-
|
|
91
|
-
## 5. Two `next-server` processes per app is normal
|
|
92
|
-
|
|
93
|
-
Next 16 splits dev into:
|
|
94
|
-
|
|
95
|
-
- **`next-server` (main)** — HTTP entry, watcher, router.
|
|
96
|
-
- **`next-server` (render worker)** — RSC + SSR pipeline.
|
|
97
|
-
|
|
98
|
-
Both inherit `NODE_OPTIONS`, so the 6 GB cap applies to **each**. With the
|
|
99
|
-
config in this app the totals stabilize around **~1.4 GB + ~0.6 GB ≈ 2 GB**.
|
|
100
|
-
If you ever see a third or fourth `next-server`, that's the build worker
|
|
101
|
-
spawning during a route compile — they exit when the build completes.
|
|
102
|
-
|
|
103
|
-
## 6. Diagnose a memory regression
|
|
104
|
-
|
|
105
|
-
When dev RSS climbs past 4 GB and stays there:
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
# First, check the boring stuff
|
|
109
|
-
du -sh .next # > 2 GB → run pnpm clean:cache
|
|
110
|
-
ps aux | grep next-server | grep -v grep | wc -l # > 2 lines → you have two dev servers
|
|
111
|
-
lsof -p <pid> | wc -l # > 5000 FDs → cache is mmap-flooding
|
|
112
|
-
|
|
113
|
-
# Then profile a 60s window — heap snapshots to .next/diagnostics/
|
|
114
|
-
pnpm dev:profile
|
|
115
|
-
|
|
116
|
-
# Open the latest .heapprofile in Chrome DevTools → Memory → "Load"
|
|
117
|
-
ls -lt .next/diagnostics | head -3
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Common culprits and their signatures:
|
|
121
|
-
|
|
122
|
-
| Symptom | Likely cause | Fix |
|
|
123
|
-
|---------|--------------|-----|
|
|
124
|
-
| `.next` is > 2 GB on disk; many `.meta` files | Turbopack FS cache bloat over weeks | `pnpm clean:cache` then `pnpm dev` (see §3) |
|
|
125
|
-
| Two or more `next-server` parents in `ps` | Dual dev server across checkouts / apps | Stop the one you aren't using (see §4) |
|
|
126
|
-
| Many copies of `lucide-react.js` / `recharts.js` retained in heap | Missing entry in `optimizePackageImports` | Add the package to the list (knob 4) |
|
|
127
|
-
| Compiled chunks for routes you never visited | `preloadEntriesOnStart` is `true` | Set to `false` (knob 3) |
|
|
128
|
-
| Heap grows on every HMR cycle, never shrinks | RSC HMR cache + import.meta.hot leak | `experimental.serverComponentsHmrCache: false` (try only if knob 3 is already on) |
|
|
129
|
-
| Single retainer chain holds 100 MB+ | A module-level `Map` / `Set` in app code never gets cleared | Move to request-scoped storage |
|
|
130
|
-
| tsserver alone is > 1.5 GB | TS strict + large lib check | `skipLibCheck: true` (already on), drop `allowJs` if not needed |
|
|
131
|
-
| Turbopack worker RSS keeps growing past 4 GiB | `turbopack.memoryLimit` not set | Apply knob 2 |
|
|
132
|
-
|
|
133
|
-
## 7. Node 24 features we leverage
|
|
134
|
-
|
|
135
|
-
Node 24 (LTS-track) is required by `engines.node` in `package.json` and
|
|
136
|
-
pinned in `.nvmrc`. Specifically:
|
|
137
|
-
|
|
138
|
-
- **V8 13.6 with Maglev JIT default-on** — faster startup, lower base heap.
|
|
139
|
-
Steady-state RSS for the `next dev` parent is ~12% lower vs Node 22.
|
|
140
|
-
- **Improved incremental marking GC** — fewer long pauses during HMR; the
|
|
141
|
-
perceived "stutter" when saving a large file is gone.
|
|
142
|
-
- **Permission model (`--permission`)** — not enabled in dev (Next reads
|
|
143
|
-
too many paths to make `--permission` ergonomic), but available for
|
|
144
|
-
hardening production scripts.
|
|
145
|
-
- **`node --run <script>`** — replaces `npm run` for one-off scripts with
|
|
146
|
-
~30 ms less per-invocation overhead. Use it in any CI step that runs a
|
|
147
|
-
workspace script directly: `node --run typecheck`. Not yet wired into
|
|
148
|
-
this repo's pm2 / package scripts because pm2 itself spawns `npm`.
|
|
149
|
-
- **`--heap-prof` / `--cpu-prof` always-on** — the `dev:profile` script in
|
|
150
|
-
`package.json` uses these to drop snapshots into `.next/diagnostics/`
|
|
151
|
-
without any third-party profiler dependency.
|
|
152
|
-
- **`--experimental-strip-types`** — Node 24 can run `.ts` files directly,
|
|
153
|
-
but Next still uses tsc + swc, so this only helps for stand-alone
|
|
154
|
-
scripts under `apps/web/scripts/` (e.g. `fa:subset-audit`) if they're
|
|
155
|
-
converted from `.mjs` to `.ts`.
|
|
156
|
-
- **Smaller initial heap allocations** — V8 13.6 starts with ~50 MB less
|
|
157
|
-
reserved arena vs V8 12.x. Most visible in fast CI test runs.
|
|
158
|
-
|
|
159
|
-
## 8. Anti-patterns
|
|
160
|
-
|
|
161
|
-
| Anti-pattern | Why it's wrong |
|
|
162
|
-
|--------------|----------------|
|
|
163
|
-
| Setting `NODE_OPTIONS` only in `.env.local` | `.env.local` is read by Next, not by Node. The dev server's own runtime never sees it. |
|
|
164
|
-
| Setting `--max-old-space-size` to the system RAM amount | Defeats the cap. The point is to force GC pressure, not raise the ceiling. |
|
|
165
|
-
| Running multiple `next dev` instances on the same machine without unique ports | Each instance ignores the others' caches and the total RSS is N × steady-state. Use the dedicated `dev:3001` / `dev:3005` scripts. |
|
|
166
|
-
| `NODE_OPTIONS=--inspect` in normal dev | Allocates an extra inspector arena per process (~200 MB). Use only when actively debugging. |
|
|
167
|
-
| Adding `nodemon` on top of `next dev` | Next has its own watcher; nodemon doubles the file-system event handlers. |
|
|
168
|
-
| Importing `@exxatdesignux/ui` from the package root for every icon | Defeats `optimizePackageImports`. Always import from the leaf path the DS exposes. |
|
|
169
|
-
| Running pm2 without `max_memory_restart` | A wedged worker stays wedged. The 7 GB ceiling lets pm2 recycle before the OS swaps. |
|
|
170
|
-
| Disabling `turbopackFileSystemCacheForDev` because "cache is the problem" | Cold starts go from ~1s to ~15–30s every restart. Bust with `pnpm clean:cache` instead. |
|
|
171
|
-
| Two checkouts of the same monorepo both running dev | Caches don't share — 2× total RSS. Pin to one checkout per machine. |
|
|
172
|
-
|
|
173
|
-
## 9. Upgrading an existing customer app
|
|
174
|
-
|
|
175
|
-
If your app was scaffolded before `@exxatdesignux/ui@0.5.10`, copy the diffs
|
|
176
|
-
below from `node_modules/@exxatdesignux/ui/template/`:
|
|
177
|
-
|
|
178
|
-
1. `.nvmrc` — set to `24`.
|
|
179
|
-
2. `package.json` — `engines.node: ">=24.0.0"` + the `NODE_OPTIONS` /
|
|
180
|
-
`NEXT_TELEMETRY_DISABLED` prefix on every `dev*` script + the new
|
|
181
|
-
`dev:profile`, `dev:fresh`, `clean`, `clean:cache` scripts.
|
|
182
|
-
3. `next.config.mjs` — add the `turbopack: { memoryLimit }` block, the
|
|
183
|
-
expanded `experimental.optimizePackageImports` array,
|
|
184
|
-
`experimental.preloadEntriesOnStart: false`,
|
|
185
|
-
`experimental.webpackMemoryOptimizations: true`, and the
|
|
186
|
-
`onDemandEntries` block.
|
|
187
|
-
4. `tsconfig.json` — `target: ES2022` + `assumeChangesOnlyAffectDirectDependencies: true`.
|
|
188
|
-
5. `ecosystem.config.cjs` (if used) — add the `env` block with
|
|
189
|
-
`NODE_OPTIONS` + `NEXT_TELEMETRY_DISABLED` and `max_memory_restart: "7G"`.
|
|
190
|
-
6. Run `nvm install 24 && nvm use` (or your Node manager equivalent).
|
|
191
|
-
7. **First run after upgrading:** `pnpm clean && pnpm dev` to drop the
|
|
192
|
-
pre-0.5.10 Turbopack cache (it was written without the memory cap and
|
|
193
|
-
may carry stale mmap layouts).
|
|
194
|
-
|
|
195
|
-
Restart the dev server. You should see steady-state RSS settle in the
|
|
196
|
-
1.5–2 GB range within ~30 s of the first navigation. If you still see
|
|
197
|
-
> 3 GB per process: re-read §4 — you almost certainly have a second
|
|
198
|
-
dev server running somewhere.
|
|
199
|
-
|
|
200
|
-
## See also
|
|
201
|
-
|
|
202
|
-
- [`HANDBOOK.md`](./HANDBOOK.md) — workspace orientation
|
|
203
|
-
- [`consumer-upgrade-checklist.md`](https://github.com/ExxatDesign/Exxat-DS-Workspace/blob/main/packages/ui/consumer-extras/patterns/consumer-upgrade-checklist.md) — what to do after `pnpm add @exxatdesignux/ui@latest`
|
|
204
|
-
- [Next.js — Reducing dev memory usage](https://nextjs.org/docs/app/building-your-application/optimizing/memory-usage)
|
|
205
|
-
- [Node.js — Diagnostics](https://nodejs.org/api/cli.html#--heap-profheap_dir)
|
|
206
|
-
- [V8 — Maglev](https://v8.dev/blog/maglev)
|