@exxatdesignux/ui 0.5.11 → 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 +26 -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 +1 -0
- 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 +28 -0
- 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/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/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
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: ListPageTemplate — shared table state across views, mock + KPI helpers, dashboard tab. Auto-attaches when editing list-page React clients.
|
|
3
|
+
globs: apps/web/{components,lib,app}/**/*.{tsx,ts}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — list page connected views
|
|
9
|
+
|
|
10
|
+
**Authoritative detail:** **`apps/web/AGENTS.md` §4.1** and **`apps/web/docs/data-views-pattern.md`** (mock data, connected views, dashboard view).
|
|
11
|
+
|
|
12
|
+
## When building `ListPageTemplate` + data client
|
|
13
|
+
|
|
14
|
+
1. **One `useTableState` per tab’s table** — Pass full mock/API rows into a single table component; **`list` / `board` / `dashboard`** surfaces consume **`tableState.rows`** (same filters/search/sort as the grid).
|
|
15
|
+
2. **`renderContent`** — Always pass **`view={tab.viewType}`**; use **`key={tab.id}`** (not `viewType`) so switching views does not reset table state. Pass **`onViewChange`** into the table component so **`TablePropertiesDrawer`** stays aligned (**`currentView`** + **`updateTab({ viewType, icon: dataListViewIcon(viewType) })`** — see **`apps/web/AGENTS.md` §4.2** and **`.cursor/rules/exxat-table-properties-drawer.mdc`**).
|
|
16
|
+
3. **Mock data** — Typed arrays in **`lib/mock/<entity>.ts`**; KPI builders in **`lib/mock/<entity>-kpi.ts`** returning **`MetricItem[]`** / **`MetricInsight`** from **`@/components/key-metrics`**.
|
|
17
|
+
4. **Dashboard view tab** — Use **`KeyMetrics`** (`variant="flat"` or `"card"`) and the **same** KPI functions with **`tableState.rows`**. Do **not** add standalone `Card` metric grids that duplicate those numbers. For chart-heavy dashboards reuse **`ChartsOverview`** / **`DashboardTabs`** patterns from the main dashboard route when appropriate.
|
|
18
|
+
5. **List hub metrics strip** — Prefer **`KeyMetrics variant="flat"`** on **`ListPageTemplate`** **`metrics`** slot (transparent band, brand glow only) — **`docs/kpi-flat-band-pattern.md`**, **`.cursor/rules/exxat-kpi-flat-band.mdc`**.
|
|
19
|
+
6. **MUST NOT** ship “not wired” / “switch to table” placeholders for list/board/dashboard when the stack supports those views.
|
|
20
|
+
7. **MUST NOT** add a **primary nav** destination that is only placeholder copy with no **`ListPageTemplate`** hub, mock rows, and wired views — see **`apps/web/AGENTS.md` §4.1** (no empty hubs).
|
|
21
|
+
8. **Add view parity** — Use **`FULL_HUB_SUPPORTED_VIEWS`** (default) and implement **all seven** views with real bodies — **`.cursor/rules/exxat-hub-supported-views.mdc`**. List rows **MUST** use **`ListPageBoardCard`** (`library-table.tsx`), not bare title + id lines.
|
|
22
|
+
|
|
23
|
+
## See also
|
|
24
|
+
|
|
25
|
+
- **`.cursor/rules/exxat-centralized-list-dataset.mdc`** — single **`tableState.rows`** source for every hub view, inspectors, and **`TablePropertiesDrawer`** on the same **`DataTable`**.
|
|
26
|
+
- **Centered view bodies + reusable shells:** **`apps/web/AGENTS.md` §4.5**, **`.cursor/rules/exxat-list-page-view-shells.mdc`**, **`ListPageViewFrame`** in **`components/data-views/list-page-view-frame.tsx`**.
|
|
27
|
+
- **Flat KPI band:** **`docs/kpi-flat-band-pattern.md`**, **`.cursor/rules/exxat-kpi-flat-band.mdc`**.
|
|
28
|
+
- **Hub supported views:** **`docs/hub-supported-views-pattern.md`**, **`.cursor/rules/exxat-hub-supported-views.mdc`**.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — centered reusable shells for list-page views (not page-specific markup)
|
|
3
|
+
globs: apps/web/components/**/*.tsx
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — list-page view shells (centered, reusable)
|
|
9
|
+
|
|
10
|
+
## Intent
|
|
11
|
+
|
|
12
|
+
**View bodies** under `ListPageTemplate` (table, list, board, dashboard, **folder**, **panel**, and future view types) **MUST** share one **layout primitive** for horizontal gutter and optional **centered max-width** on wide viewports. **MUST NOT** copy `mx-4 lg:mx-6` + `mx-auto max-w-*` per feature file or bake view-only layout into a single route.
|
|
13
|
+
|
|
14
|
+
Domain logic (columns, tiles, folder trees) stays in **`*-table.tsx` / `*-client.tsx`**; **chrome and rhythm** use **`ListPageViewFrame`** from **`@/components/data-views/list-page-view-frame`** (re-exported from **`@/components/data-views`**). **Data** for those branches still comes from the **same** **`tableState.rows`** / hub state as the grid — see **`.cursor/rules/exxat-centralized-list-dataset.mdc`**.
|
|
15
|
+
|
|
16
|
+
## MUST
|
|
17
|
+
|
|
18
|
+
1. Wrap **non-`DataTable`** view branches (icon folder grid, OS-style explorer, empty folder states, comparable dashboard **sections** that are not the main grid) in **`ListPageViewFrame`**, passing **`maxWidthClassName`** when the design calls for a centered cap (defaults: **`LIST_PAGE_VIEW_FRAME_MAX_ICON_GRID`** (`max-w-6xl`), **`LIST_PAGE_VIEW_FRAME_MAX_WIDE`** (`max-w-7xl`)).
|
|
19
|
+
2. Add **new reusable view surfaces** under **`components/data-views/`** with **generic props** (`rows`, `renderTile`, `getRowId`, etc.) — same pattern as **`FolderGridView`**, **`FinderPanelView`**, **`ListPageBoardTemplate`**. Page files **compose** them; they **MUST NOT** own grid/folder markup.
|
|
20
|
+
3. When extracting an entity-specific view (e.g. question OS folders), plan a **generic** `*View` in **`data-views/`** plus a thin adapter if the product still needs typed mock wiring.
|
|
21
|
+
|
|
22
|
+
## MUST NOT
|
|
23
|
+
|
|
24
|
+
- Wrap **`DataTable`** (or its outer toolbar shell) in **`ListPageViewFrame`** if that **duplicates** horizontal inset already applied by **`DataTable`** / **`DataTableToolbar`** — see **`AGENTS.md` §5** (double indent).
|
|
25
|
+
- Ship **view-only** layout classes only inside **`app/(app)/.../page.tsx`** for a hub that other entities will mirror — belong in **`data-views/`** or **`templates/`**.
|
|
26
|
+
|
|
27
|
+
## See also
|
|
28
|
+
|
|
29
|
+
- **`apps/web/AGENTS.md` §4.5** — handbook table + constants.
|
|
30
|
+
- **`apps/web/docs/data-views-pattern.md`** — “View layout frame”.
|
|
31
|
+
- **`.cursor/rules/exxat-centralized-list-dataset.mdc`** — **`tableState.rows`** into every view branch.
|
|
32
|
+
- **`.cursor/skills/exxat-list-page-view-shells/SKILL.md`** — step-by-step for agents.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Sidebar and secondary nav — exactly one active item per route (longest path match)
|
|
3
|
+
globs: "**/app-sidebar.tsx,**/secondary-nav.tsx,**/navigation.tsx,**/lib/nav-active.ts"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — single active nav item (MUST)
|
|
9
|
+
|
|
10
|
+
Only **one** primary sidebar row, collapsible child, or secondary-panel link may show the active/selected state for the current route.
|
|
11
|
+
|
|
12
|
+
## MUST
|
|
13
|
+
|
|
14
|
+
1. **Use shared helpers** from `@exxatdesignux/ui/lib/nav-active` (or the app copy synced from the DS template):
|
|
15
|
+
- `collectNavUrls` + `buildNavHashClaims` for the full nav tree
|
|
16
|
+
- `isNavHrefActive(pathname, url, allNavUrls, { locationHash, hashClaimsByPath })` for sidebar `SidebarMenuButton` / `isActive`
|
|
17
|
+
- `resolveActiveNavHref(pathname, allLinkHrefs)` for secondary-panel links — compare the returned href to each link’s `href` (longest prefix wins)
|
|
18
|
+
2. **Never** mark a nav item active with bare `pathname === href` or `pathname.startsWith(href + "/")` when other nav targets share that prefix (e.g. `/dashboard` vs `/dashboard/students`).
|
|
19
|
+
3. **Hash disambiguation** — when multiple items share the same path (e.g. `/settings` vs `/settings#appearance`), use `buildNavHashClaims` so the no-fragment row defers to the hash-specific row.
|
|
20
|
+
4. **Collapsible parents** — when any child is active, the **parent** row stays visually neutral in the expanded sidebar; only the child gets `data-active` (icon rail may still highlight the parent — see `isCollapsibleParentMenuButtonActive` in `app-sidebar.tsx`).
|
|
21
|
+
5. **Keep `ListPageTemplate.supportedViewTypes` and `HubTable.supportedViewTypes` in sync** on the same hub — default **`FULL_HUB_SUPPORTED_VIEWS`** (seven views). See **`exxat-hub-supported-views.mdc`** for renderer requirements.
|
|
22
|
+
|
|
23
|
+
## MUST NOT
|
|
24
|
+
|
|
25
|
+
- Duplicate bespoke `isNavActive` / `isActive` logic that ignores longest-prefix matching.
|
|
26
|
+
- Ship a new nav surface (sidebar, secondary rail, hub scope list) without wiring the helpers above.
|
|
27
|
+
|
|
28
|
+
## Reference
|
|
29
|
+
|
|
30
|
+
- `packages/ui/src/lib/nav-active.ts`
|
|
31
|
+
- `packages/ui/template/components/sidebar/app-sidebar.tsx`
|
|
32
|
+
- `packages/ui/template/components/sidebar/secondary-nav.tsx`
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Uploaded screenshots/mockups are IA reference only — map to Exxat DS patterns; never pixel-copy
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — uploaded images are not the implementation spec
|
|
8
|
+
|
|
9
|
+
When the user attaches a **screenshot**, **mockup**, **Figma export**, **legacy app capture**, or any **reference image**, treat it as **product intent** — **not** as code to transcribe.
|
|
10
|
+
|
|
11
|
+
**Exxat DS rules, blueprints, and reference hubs always win** over what the image shows.
|
|
12
|
+
|
|
13
|
+
## Allowed to take from an image (content / IA)
|
|
14
|
+
|
|
15
|
+
- **Screen purpose** — list hub, detail record, settings, search, etc.
|
|
16
|
+
- **Nav labels**, **section grouping**, **route slugs**, **field names**, **column headers**
|
|
17
|
+
- **Data the user cares about** — which KPIs, filters, actions, statuses exist
|
|
18
|
+
- **Icon choice** (Font Awesome suffix) when obvious — still use **`fa-light` / `fa-solid`** DS pairing
|
|
19
|
+
- **Workflow** — e.g. “export from ⋯”, “row opens profile”, “invite from header”
|
|
20
|
+
|
|
21
|
+
## MUST NOT copy from an image (visual / stack)
|
|
22
|
+
|
|
23
|
+
- **Hex / RGB colors**, per-section rainbow text, custom sidebar washes, one-off pill shapes
|
|
24
|
+
- **Layout chrome** that duplicates DS shell — sidebar, **`SiteHeader`**, **`PageHeader`**, **`ListPageTemplate`** toolbar, tab bars
|
|
25
|
+
- **Bespoke components** when a DS primitive exists — raw `<table>`, custom popovers, hand-built buttons, full-width tabs, Vaul drawers
|
|
26
|
+
- **Forking shared files** (`app-sidebar.tsx`, `sidebar.tsx`, `globals.css`) to match a legacy product
|
|
27
|
+
- **Implementing “because it’s in the picture”** without naming the **DS pattern + reference file** you are using
|
|
28
|
+
|
|
29
|
+
## REQUIRED workflow (before writing UI code)
|
|
30
|
+
|
|
31
|
+
1. **Classify the screen** — use **`docs/component-selection-guide.md`** + **`docs/blueprints/`** (list page, page header, data table, dedicated search, …).
|
|
32
|
+
2. **Pick a reference hub** — **`docs/reference-implementations.md`** (Placements, Team, Library, Compliance, …).
|
|
33
|
+
3. **State the mapping** (briefly in your reply): e.g. “Students roster → **`ListPageTemplate`** + **`HubTable`** like Team; nav rows → **`lib/mock/navigation.tsx`** only; sidebar chrome unchanged.”
|
|
34
|
+
4. **Implement with DS components + tokens** — **`AGENTS.md`**, **`exxat-ds-agents.mdc`**, topic rules (`exxat-tabs-chrome`, `exxat-page-header-actions`, `exxat-sidebar-shell`, …).
|
|
35
|
+
5. **If the image conflicts with DS** — follow DS; ask **one** clarifying question only when the **business requirement** (not pixels) cannot be met with existing patterns.
|
|
36
|
+
|
|
37
|
+
## Consumer apps
|
|
38
|
+
|
|
39
|
+
- Product UI lives in the **customer repo** with **`@exxatdesignux/ui`** — not by restyling the DS monorepo to match uploads.
|
|
40
|
+
- After upgrade, run **`exxat-ui sync-extras`** so this rule and siblings stay on disk.
|
|
41
|
+
|
|
42
|
+
## See also
|
|
43
|
+
|
|
44
|
+
- **`exxat-reuse-before-custom.mdc`** — compose before inventing
|
|
45
|
+
- **`exxat-sidebar-shell.mdc`** — sidebar-specific anti-patterns
|
|
46
|
+
- **`exxat-token-economy`** skill §2 — pre-flight includes image discipline
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: No Salesforce SLDS leakage — never copy slds-* classes, lightning-* elements, SLDS token names, or synthetic-shadow assumptions into Exxat DS code.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — no SLDS leakage
|
|
8
|
+
|
|
9
|
+
**Context:** Some agents have access to other repos, including the **Salesforce
|
|
10
|
+
`design-system-2-starter-kit`** (LWC + SLDS 2 / Cosmos). Patterns there look
|
|
11
|
+
similar at a glance (`page-header`, `data-table`, `card`) but the
|
|
12
|
+
**implementations are not portable** — they assume LWC, synthetic shadow,
|
|
13
|
+
SLDS class systems, and the Salesforce platform.
|
|
14
|
+
|
|
15
|
+
This rule prevents accidental cross-pollination from any **Salesforce / SLDS**
|
|
16
|
+
codebase into **`apps/web`** or **`packages/ui`**.
|
|
17
|
+
|
|
18
|
+
## MUST NOT
|
|
19
|
+
|
|
20
|
+
1. **CSS class strings** — Do **not** introduce **`slds-*`** classes
|
|
21
|
+
(`slds-page-header`, `slds-m-around_*`, `slds-grid`, `slds-button`,
|
|
22
|
+
`slds-text-*`, etc.) anywhere in `apps/web/**` or `packages/ui/**`. The
|
|
23
|
+
Exxat stack is **Tailwind v4** + the token system documented in
|
|
24
|
+
[`apps/web/docs/token-taxonomy.md`](mdc:apps/web/docs/token-taxonomy.md).
|
|
25
|
+
2. **LWC markup** — Do **not** render `<lightning-*>` tags
|
|
26
|
+
(`<lightning-button>`, `<lightning-datatable>`, `<lightning-card>`,
|
|
27
|
+
`<lightning-icon>`, etc.) or import from `lightning/*` modules. Use the
|
|
28
|
+
shadcn-style primitives in `packages/ui/src/components/ui/` and the
|
|
29
|
+
Exxat compositions in `apps/web/components/`.
|
|
30
|
+
3. **Synthetic-shadow assumptions** — Do **not** call `template.querySelector`,
|
|
31
|
+
`slds-color-scheme_dark`, `shadowRoot === null` checks, or anything that
|
|
32
|
+
only makes sense under Salesforce synthetic shadow. The Next.js app uses
|
|
33
|
+
the real DOM + Radix portals.
|
|
34
|
+
4. **SLDS styling-hook vars** — Do **not** use SLDS custom properties
|
|
35
|
+
(`--slds-g-color-surface-*`, `--slds-g-color-on-surface-*`,
|
|
36
|
+
`--slds-g-color-border-*`, `--slds-g-color-accent-*`, etc.). The Exxat
|
|
37
|
+
equivalents are documented in
|
|
38
|
+
[`apps/web/docs/token-taxonomy.md`](mdc:apps/web/docs/token-taxonomy.md):
|
|
39
|
+
surface → `--background` / `--card` / `--secondary-panel-bg`; ink →
|
|
40
|
+
`--foreground` / `--muted-foreground`; border → `--border` /
|
|
41
|
+
`--border-control` family; brand → `--brand-color` family.
|
|
42
|
+
5. **SLDS theme switching** — Do **not** add UI for selecting "SLDS 1" vs
|
|
43
|
+
"SLDS 2". Exxat ships **Exxat One** (lavender) and **Exxat Prism** (rose)
|
|
44
|
+
themes via `.theme-one` / `.theme-prism` classes — see
|
|
45
|
+
`docs/shell-surface-elevation-pattern.md`.
|
|
46
|
+
6. **Slot-vs-prop confusion** — Do **not** import LWC slot conventions
|
|
47
|
+
(`slot="actions"`, `<slot name="…">`) into React components. React uses
|
|
48
|
+
`children` + named props (`actions={…}`, `header={…}`).
|
|
49
|
+
7. **`lightning-base-components` patterns inside React** — Do **not** add
|
|
50
|
+
per-page form-input wrappers like `lightning-input` clones. Use shadcn
|
|
51
|
+
`Input` / `Select` / `Combobox` from `packages/ui`.
|
|
52
|
+
|
|
53
|
+
## SHOULD
|
|
54
|
+
|
|
55
|
+
- If you're comparing SLDS patterns to gain inspiration (e.g. the
|
|
56
|
+
`object-home` / `record-home` / `related-list` variant model from
|
|
57
|
+
`ui-page-header`), **document the idea** as a blueprint under
|
|
58
|
+
[`apps/web/docs/blueprints/`](mdc:apps/web/docs/blueprints) — do **not**
|
|
59
|
+
copy SLDS markup verbatim.
|
|
60
|
+
- For any "but SLDS does it this way" thought, check
|
|
61
|
+
[`apps/web/docs/component-selection-guide.md`](mdc:apps/web/docs/component-selection-guide.md)
|
|
62
|
+
first to see what Exxat ships for that pattern.
|
|
63
|
+
|
|
64
|
+
## Why this rule exists
|
|
65
|
+
|
|
66
|
+
Salesforce's design system is excellent — but it is **built for a different
|
|
67
|
+
runtime** (LWC + synthetic shadow + classic SLDS layered with SLDS 2 /
|
|
68
|
+
Cosmos). Mixing its CSS class system into a Tailwind v4 + OKLCH token app
|
|
69
|
+
silently breaks:
|
|
70
|
+
|
|
71
|
+
- **Theming** — `slds-*` classes don't react to `.theme-prism` / `.dark` /
|
|
72
|
+
`[data-contrast="high"]`.
|
|
73
|
+
- **Accessibility** — SLDS 1 contrast ratios differ from Exxat's WCAG 2.1 AA
|
|
74
|
+
targets; mixing them produces unverifiable composites.
|
|
75
|
+
- **Bundling** — Importing `@salesforce-ux/design-system` would balloon the
|
|
76
|
+
Next.js bundle and pull in classic SLDS that we explicitly chose not to use.
|
|
77
|
+
|
|
78
|
+
## See also
|
|
79
|
+
|
|
80
|
+
- [`apps/web/docs/token-taxonomy.md`](mdc:apps/web/docs/token-taxonomy.md) — Exxat tokens
|
|
81
|
+
- [`apps/web/docs/blueprints/`](mdc:apps/web/docs/blueprints) — framework-agnostic specs
|
|
82
|
+
- [`apps/web/docs/component-selection-guide.md`](mdc:apps/web/docs/component-selection-guide.md) — what to reach for
|
|
83
|
+
- [`.cursor/rules/exxat-token-discipline.mdc`](mdc:.cursor/rules/exxat-token-discipline.mdc) — token-level enforcement
|
|
84
|
+
- [`.cursor/rules/exxat-reuse-before-custom.mdc`](mdc:.cursor/rules/exxat-reuse-before-custom.mdc) — ask before adding new primitives
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Exxat DS — do not use toast / sonner / transient snackbars for product messaging.
|
|
3
3
|
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Exxat DS — no toast
|
|
@@ -22,5 +23,4 @@ Toasts are easy to miss, stack poorly with complex layouts, and behave inconsist
|
|
|
22
23
|
|
|
23
24
|
## Authoritative detail
|
|
24
25
|
|
|
25
|
-
- **`AGENTS.md` §6.5**
|
|
26
|
-
- Repo root **`.cursor/rules/exxat-no-toast.mdc`** when the monorepo parent is open
|
|
26
|
+
- **`apps/web/AGENTS.md` §6.5**
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — no Vaul Drawer primitive; side panels use Sheet only
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — no Vaul
|
|
8
|
+
|
|
9
|
+
## MUST
|
|
10
|
+
|
|
11
|
+
- Side panels (export, properties, invite, folder create) use **`Sheet`** from `@exxatdesignux/ui/components/sheet` with the floating inset pattern (`showOverlay={false}`, `getFloatingSheetInsetProps`).
|
|
12
|
+
- Product code names may end in `*Drawer` (`ExportDrawer`, `TablePropertiesDrawer`) — implementation is **`Sheet`**, not Vaul.
|
|
13
|
+
|
|
14
|
+
## MUST NOT
|
|
15
|
+
|
|
16
|
+
- Add **`vaul`** to `package.json` or import `@/components/ui/drawer` / Vaul `Drawer`.
|
|
17
|
+
- Scaffold or extend the removed shadcn Vaul wrapper — it was deleted from `@exxatdesignux/ui@0.5.3+`.
|
|
18
|
+
|
|
19
|
+
## Consumer apps
|
|
20
|
+
|
|
21
|
+
After upgrading to **`@exxatdesignux/ui@0.5.3`**, remove **`vaul`** from the app `package.json`, delete any local `components/ui/drawer.tsx` re-export, and re-run **`exxat-ui sync-extras`**.
|
|
22
|
+
|
|
23
|
+
## See also
|
|
24
|
+
|
|
25
|
+
- **`.cursor/rules/exxat-drawer-vs-dialog.mdc`**
|
|
26
|
+
- **`apps/web/docs/drawer-vs-dialog-pattern.md`**
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — PageHeader actions slot; View as / overflow / primary CTA patterns. Auto-attaches when editing React PageHeader callsites; the universal "one filled primary" rule lives in P3 of exxat-ux-principles.
|
|
3
|
+
globs: apps/web/{components,app}/**/*.{tsx,ts}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — page header actions
|
|
9
|
+
|
|
10
|
+
**Blueprint:** `apps/web/docs/blueprints/page-header.md`
|
|
11
|
+
|
|
12
|
+
## Anatomy (actions slot only)
|
|
13
|
+
|
|
14
|
+
| Control | DS pattern |
|
|
15
|
+
|---------|------------|
|
|
16
|
+
| **Primary CTA** | One **`Button`** `variant="default"` `size="lg"` (filled) — e.g. “Add student”, “Ask Leo” when it is the main action |
|
|
17
|
+
| **Secondary / scope** (“View as”, “All students”) | **`Button variant="outline" size="lg"`** + chevron, or **`Select`** / **`DropdownMenu`** trigger styled as outline button — **not** plain grey rectangles or unstyled `<button>` |
|
|
18
|
+
| **Overflow (⋯)** | **`Button variant="outline" size="icon-lg"`** (or `size="lg"` icon-only) opening **`DropdownMenu`** — Export, settings, invite |
|
|
19
|
+
| **Collaboration** | **`PageHeader variant="collaboration"`** face rail — not a custom avatar row |
|
|
20
|
+
|
|
21
|
+
## MUST
|
|
22
|
+
|
|
23
|
+
- Compose **`PageHeader`** + pass **`actions`** as DS **`Button`** / **`DropdownMenu`** primitives.
|
|
24
|
+
- Keep **one** filled primary; put Export and long-tail items under **⋯**.
|
|
25
|
+
|
|
26
|
+
## MUST NOT
|
|
27
|
+
|
|
28
|
+
- Hand-built header button rows that skip **`PageHeader`** and **`Button`** variants.
|
|
29
|
+
- Multiple filled primaries beside each other (e.g. “Ask Leo” + “Add student” both solid black unless product explicitly documents hierarchy — prefer outline for secondary).
|
|
30
|
+
|
|
31
|
+
## Reference
|
|
32
|
+
|
|
33
|
+
- `components/library-page-header.tsx`, Placements/Team header patterns in **`apps/web/docs/reference-implementations.md`**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Exxat DS — when to use a drawer vs a new page for actions and auxiliary UI.
|
|
3
3
|
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Exxat DS — page vs drawer
|
|
@@ -8,6 +9,7 @@ alwaysApply: true
|
|
|
8
9
|
## Rule
|
|
9
10
|
|
|
10
11
|
- **Drawer / sheet** — Use when the user needs **the current page’s context** *and* a **quick view**, **quick actions**, or a **short auxiliary flow** (parent list or hub stays meaningful behind the panel).
|
|
12
|
+
- **Dialog (modal)** — Use for **blocking** short confirms/alerts on the **same route** when the hub must not stay interactable — see **`docs/drawer-vs-dialog-pattern.md`**, **`.cursor/rules/exxat-drawer-vs-dialog.mdc`**.
|
|
11
13
|
- **New page (route)** — Use **otherwise**: primary tasks, long-form or multi-step work, or anything that should have its **own URL** / history without the parent page visible.
|
|
12
14
|
|
|
13
15
|
## Product examples (this repo)
|
|
@@ -17,6 +19,6 @@ alwaysApply: true
|
|
|
17
19
|
|
|
18
20
|
## Authoritative detail
|
|
19
21
|
|
|
20
|
-
- **`AGENTS.md` §6.4**
|
|
21
|
-
- **`docs/data-views-pattern.md`** (Page vs drawer)
|
|
22
|
-
-
|
|
22
|
+
- **`apps/web/AGENTS.md` §6.4**
|
|
23
|
+
- **`apps/web/docs/data-views-pattern.md`** (Page vs drawer)
|
|
24
|
+
- **`apps/web/docs/drawer-vs-dialog-pattern.md`** (drawer vs modal dialog)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — avatar + name + email when showing a person; omit email in dense contexts (e.g. board cards).
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
appliesTo: [universal]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — person identity (name + email)
|
|
8
|
+
|
|
9
|
+
Use this when rendering **a known person** (user, collaborator, author roster row, invitee, etc.) — not anonymous counts or system actors.
|
|
10
|
+
|
|
11
|
+
## MUST (when email is available)
|
|
12
|
+
|
|
13
|
+
1. **Primary** — Display name (or given + family name) as the dominant line.
|
|
14
|
+
2. **Avatar** — Use **`AvatarInitials`** / the same primitives as **`PageHeader`** collaborator stack and **team** roster patterns (initials from display name via **`initialsFromDisplayName`** where applicable).
|
|
15
|
+
3. **Secondary** — Work email (or login id) on a **muted** second line **under** the name, smaller type (`text-xs` / `text-muted-foreground`), truncated with **`truncate`** / **`min-w-0`** when the container is narrow.
|
|
16
|
+
|
|
17
|
+
## Context: when **not** to show email
|
|
18
|
+
|
|
19
|
+
Dense or width-constrained surfaces **MUST NOT** squeeze email if it harms scanability or layout:
|
|
20
|
+
|
|
21
|
+
- **Board / kanban cards** — title row, card trailing, or narrow columns: **avatar + name (or initials chip) only**; skip the email line. Prefer a **Tip** on the avatar/name with full email for discovery if the card is interactive.
|
|
22
|
+
- **Icon-only rails** — avatar only + tooltip with name + email.
|
|
23
|
+
- **Single-line table cells** — if the column is not “Person”, only show what fits (e.g. name + Tip); do not wrap email into a second line inside a 1-line cell unless the column is dedicated to identity.
|
|
24
|
+
|
|
25
|
+
## Table / list / drawer / inspector
|
|
26
|
+
|
|
27
|
+
- **Dedicated person column** or **profile / invite / access** surfaces — show **avatar + name + email** (stacked or name with email below), consistent with **`InviteCollaboratorsDrawer`** and **`PageHeader`** collaboration variant.
|
|
28
|
+
- **Example:** `LibraryTable` **Author** — `AvatarInitials`, **primary name** (`text-sm font-medium`), **muted `text-xs` email** with optional `mailto:` (row click does not navigate when the link is used).
|
|
29
|
+
|
|
30
|
+
## Avatar rows — **never overlapping**
|
|
31
|
+
|
|
32
|
+
When multiple people share a row (face rail, reviewer pile, attendee list, collaborator stack):
|
|
33
|
+
|
|
34
|
+
- **MUST** render avatars **side-by-side** with a small gap (`gap-1` / `gap-1.5`) — same pattern as the **`PageHeader`** collaboration variant.
|
|
35
|
+
- **MUST NOT** overlap avatars (Slack / GitHub face piles with negative margins like `-space-x-2` and ring-on-background separators). Ring contrast is fragile in dark mode, click targets stack, and screen readers announce ambiguous groupings.
|
|
36
|
+
- **`AvatarGroup`** in **`packages/ui`** already ships the non-overlap default (`flex items-center gap-1.5`). Pair with **`AvatarGroupCount`** for the **`+N`** overflow chip, wrapped in a **`Tip`** that names the hidden people. Each visible avatar gets its own **`Tip`** with the person’s name (per **§8.6 (Case B)** in **`AGENTS.md`**).
|
|
37
|
+
|
|
38
|
+
## MUST NOT
|
|
39
|
+
|
|
40
|
+
- Show raw email as the only visible identifier when a display name exists (unless the product context is strictly technical, e.g. audit log “principal” column).
|
|
41
|
+
- Use a different avatar size/shape for the same person between list and detail without a deliberate density tier.
|
|
42
|
+
- Restore an overlapping face pile by adding back `-space-x-*` / negative margins / `*:ring-*` to **`AvatarGroup`** or any ad-hoc row of avatars.
|
|
43
|
+
|
|
44
|
+
## See also
|
|
45
|
+
|
|
46
|
+
- **`.cursor/rules/exxat-collaboration-access.mdc`** — face rail, invite sheet, access roster.
|
|
47
|
+
- **`.cursor/skills/exxat-collaboration-access/SKILL.md`**
|
|
48
|
+
- **`apps/web/AGENTS.md`** collaboration + list hub patterns where relevant.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Primary sidebar item opens nested SecondaryPanel (Library pattern)
|
|
3
|
+
globs: apps/web/components/**/*.tsx
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — primary nav → secondary panel
|
|
9
|
+
|
|
10
|
+
Some hubs expose **scoped navigation** (All / My / tree / filters) in a **nested panel** between the **icon rail** and **main content** — not as **primary nav child rows**.
|
|
11
|
+
|
|
12
|
+
**Reference:** **Library** — **`lib/mock/navigation.tsx`** (`secondaryPanel: "library"`), **`components/app-sidebar.tsx`**, **`components/secondary-panel.tsx`**, **`components/library-panel-activator.tsx`**, **`components/library-secondary-nav.tsx`**, **`lib/library-nav.ts`** (URL scope).
|
|
13
|
+
|
|
14
|
+
## MUST (wiring)
|
|
15
|
+
|
|
16
|
+
1. **`NavLinkItem`** — On the **primary** row that should drive the panel, set **`secondaryPanel`** to a **stable string id** (e.g. **`"library"`**). Keep **`url`** pointing at the **hub route**.
|
|
17
|
+
2. **`SecondaryPanel` registry** — In **`components/secondary-panel.tsx`**, add **`PANELS[id]`** → component that renders **panel chrome** (title, optional search) + **your secondary nav** list. **MUST** keep ids in sync with **`NAV_PRIMARY`**.
|
|
18
|
+
3. **Auto-open on route** — The hub **`client`** (or layout slot) **MUST** mount **`*PanelActivator`** that calls **`useAutoPanel(id)`** with the **same id**, so deep links and first visit open the panel while the route is mounted (e.g. **`LibraryPanelActivator`** + **`beforeSiteHeader`** on **`LibraryClient`**).
|
|
19
|
+
4. **Same-route reopen** — **`AppSidebar`** already calls **`openPanel(secondaryPanel)`** when the user clicks the primary item **again** while already on that **`url`** (prevents no-op navigation). Secondary nav rows that stay on the same path **SHOULD** call **`openPanel`** on click where **Next.js `Link`** would not fire (same **`href`**).
|
|
20
|
+
|
|
21
|
+
## Surface elevation (brand chrome)
|
|
22
|
+
|
|
23
|
+
The nested panel sits **between** the icon rail and main content. **MUST** use the three-level stack (darkest → lightest):
|
|
24
|
+
|
|
25
|
+
| Level | Surface | Token |
|
|
26
|
+
|-------|---------|--------|
|
|
27
|
+
| **0** | Primary sidebar | `--sidebar` (= `--brand-tint` on product themes) |
|
|
28
|
+
| **1** | Secondary panel | `--secondary-panel-bg` |
|
|
29
|
+
| **2** | Page canvas | `--background` |
|
|
30
|
+
|
|
31
|
+
- **`NestedSecondaryPanelShell`** — **`bg-[var(--secondary-panel-bg)]`**, not **`bg-sidebar`** (same as level 0).
|
|
32
|
+
- **OKLCH** — light: `color-mix(in oklch, var(--background) 40%, var(--brand-tint-light) 60%)`; dark: `color-mix(in oklch, var(--card) 32%, var(--brand-tint) 68%)`. **MUST** follow **active product** (`theme-one`, `theme-prism`, `theme-custom`) — **MUST NOT** hardcode rose for all products.
|
|
33
|
+
- **`ProductProvider`** — `theme-custom` only when stored accent **differs** from product default (`accentOverrideActive`).
|
|
34
|
+
|
|
35
|
+
**Authoritative detail:** **`apps/web/docs/shell-surface-elevation-pattern.md`**.
|
|
36
|
+
|
|
37
|
+
## SHOULD
|
|
38
|
+
|
|
39
|
+
- Drive **hub scope** from the **URL** (**`useSearchParams`** + helpers like **`parseLibraryNav`**) so **refresh**, **share link**, and **breadcrumbs** match the secondary list.
|
|
40
|
+
- Keep **panel content** **Font Awesome**-aligned with the rest of the app (**`.cursor/rules/exxat-fontawesome-icons.mdc`**).
|
|
41
|
+
|
|
42
|
+
## MUST NOT
|
|
43
|
+
|
|
44
|
+
- Register a **`secondaryPanel`** id in **`NAV_PRIMARY`** **without** a **`PANELS`** entry and a matching **`useAutoPanel`** on the hub — users get a **collapsed sidebar** with **empty** panel space.
|
|
45
|
+
- Use **`secondaryPanel`** for **full** app sections that deserve their **own primary nav rows** — reserve this for **one hub** + **inner scope** UI.
|
|
46
|
+
|
|
47
|
+
## See also
|
|
48
|
+
|
|
49
|
+
- **`apps/web/AGENTS.md` §4.6** — handbook summary.
|
|
50
|
+
- **`apps/web/docs/shell-surface-elevation-pattern.md`** — OKLCH tokens + product theme.
|
|
51
|
+
- **`.cursor/rules/exxat-library-hub-header.mdc`** — folder-scoped library header **⋯** → **Customize folder** + client-mounted sheet.
|
|
52
|
+
- **`.cursor/rules/exxat-page-vs-drawer.mdc`** — when **drawer** vs **route**; secondary panel is **nav chrome**, not a workflow drawer.
|
|
53
|
+
- **`.cursor/rules/exxat-kpi-flat-band.mdc`** — flat KPI strip (separate from panel fill).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — prefer centralized reusable components; ask the user before new bespoke primitives.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
appliesTo: [universal]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — reuse before custom
|
|
8
|
+
|
|
9
|
+
## MUST
|
|
10
|
+
|
|
11
|
+
1. **Compose first** — Use existing **`components/ui/`**, **`components/data-views/`**, **`components/templates/`**, **`PageHeader`**, **`ListPageTemplate`**, **`DataTable`**, **`KeyMetrics`**, and patterns in **`AGENTS.md` §9** before writing new layout or interaction chrome.
|
|
12
|
+
2. **Search the codebase** — Grep or open the nearest hub (Placements, Team, Library) for the same UX (toolbar, drawer, metrics, board card, `ListPageViewFrame`, etc.).
|
|
13
|
+
3. **Extend in place** — Prefer adding a variant, slot, or prop to a shared component over a one-off duplicate under a single route.
|
|
14
|
+
|
|
15
|
+
## When the tool must ask the user
|
|
16
|
+
|
|
17
|
+
**MUST pause and ask the user** (with a short option list) when, after scanning, **no reasonable reuse** exists and the implementation would add **any** of:
|
|
18
|
+
|
|
19
|
+
- A **new reusable primitive** (new file under `components/` meant for multiple routes), or
|
|
20
|
+
- A **non-trivial bespoke widget** (custom data grid, chart system, modal stack, or parallel design-system fork) that is not already implied by the task.
|
|
21
|
+
|
|
22
|
+
Do **not** silently ship a second stack for the same product pattern (e.g. another “table”, another metrics strip, another sidebar).
|
|
23
|
+
|
|
24
|
+
When the user **uploads a screenshot or mockup**, read **`exxat-no-image-pixel-copy.mdc`** first — extract IA only; map to DS patterns; never pixel-copy colors or chrome.
|
|
25
|
+
|
|
26
|
+
If the **user or task already explicitly** approved a greenfield component (“build a new X from scratch”), you may proceed without re-asking.
|
|
27
|
+
|
|
28
|
+
## MUST NOT
|
|
29
|
+
|
|
30
|
+
- Add **route-only copies** of patterns that already live in **`components/`** or **`packages/ui`** without product reason.
|
|
31
|
+
- Introduce **parallel primitives** (second button row, second card shell, second command surface) when an existing one can be parameterized.
|
|
32
|
+
|
|
33
|
+
## See also
|
|
34
|
+
|
|
35
|
+
- **`apps/web/AGENTS.md` §1** (compose / scan before new UI), **§9** architecture table
|
|
36
|
+
- **`.cursor/rules/exxat-ds-agents.mdc`**
|
|
37
|
+
- **`.cursor/rules/exxat-centralized-list-dataset.mdc`** — one dataset / one presentation path for hubs
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Application sidebar — DS shell only; legacy screenshots are IA reference, not visual spec
|
|
3
|
+
globs: "**/app-sidebar.tsx,**/navigation*.tsx,**/lib/mock/navigation.tsx,**/lib/*-navigation.tsx"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — application sidebar shell (MUST)
|
|
9
|
+
|
|
10
|
+
Customer / legacy app **screenshots** may show **what links exist** — not **how the sidebar is styled**. Full policy: **`exxat-no-image-pixel-copy.mdc`**.
|
|
11
|
+
|
|
12
|
+
## Screenshots are allowed to inform (content only)
|
|
13
|
+
|
|
14
|
+
- Nav **labels**, **grouping**, **route paths**, **Font Awesome icon suffixes**
|
|
15
|
+
- Whether a row needs **children**, a **secondary panel**, or a **badge**
|
|
16
|
+
|
|
17
|
+
## Screenshots MUST NOT drive styling (MUST NOT)
|
|
18
|
+
|
|
19
|
+
- **Per-section label colors** (magenta / teal / gold text on every row in a group)
|
|
20
|
+
- **Custom active pills** (hand-built white `rounded-*` + shadow instead of `SidebarMenuButton` `data-active`)
|
|
21
|
+
- **Non-token sidebar fills** (pink wash, `#hex` tints, inline `style={{ color: … }}`)
|
|
22
|
+
- **Forking `app-sidebar.tsx` layout** for one product while rest of DS uses shared chrome
|
|
23
|
+
- **Re-implementing `isNavActive`** in the app — use **`@exxatdesignux/ui/lib/nav-active`** (see **`exxat-nav-single-active.mdc`**)
|
|
24
|
+
|
|
25
|
+
## MUST — DS sidebar chrome
|
|
26
|
+
|
|
27
|
+
1. **Shell:** **`AppSidebar`** + **`SidebarMenuButton`** / **`SidebarGroup`** / **`SidebarGroupLabel`** from **`@exxatdesignux/ui`** (or app shims). Reference: **`apps/web/components/sidebar/app-sidebar.tsx`**, **`AGENTS.md` §9.1**, **`exxat-ds-skill` §3.1–§3.2**.
|
|
28
|
+
2. **Typography:** Row labels **`text-sidebar-foreground`** (default from `SidebarMenuButton`). Section headings **`SidebarGroupLabel`** + **`text-sidebar-section-label`** — not rainbow section body text.
|
|
29
|
+
3. **Active state:** Pass **`isActive`** into **`SidebarMenuButton`** only. Expanded rail: **one** active row (`data-active` → background pill + ring from DS). Collapsible parent stays **neutral** when a child is active. Icon rail: parent icon may light up when any child is active.
|
|
30
|
+
4. **Icons:** **`fa-light`** idle / **`fa-solid`** active on the **icon** only — do **not** recolor the label per module.
|
|
31
|
+
5. **Nav data:** Add or extend **`lib/mock/navigation.tsx`** (or a file it imports). **MUST NOT** paste a full legacy sidebar component from a customer repo into **`apps/web`**.
|
|
32
|
+
6. **Consumer products:** Prism / customer hubs belong in a **separate consumer repo** with **`@exxatdesignux/ui`** — not product-specific rewrites of the DS monorepo sidebar.
|
|
33
|
+
|
|
34
|
+
## Reference (canonical visual)
|
|
35
|
+
|
|
36
|
+
- **`apps/web/components/sidebar/app-sidebar.tsx`** + **`lib/mock/navigation.tsx`**
|
|
37
|
+
- **`packages/ui/src/components/ui/sidebar.tsx`** — `data-active:bg-background` pill
|
|
38
|
+
|
|
39
|
+
## Anti-pattern
|
|
40
|
+
|
|
41
|
+
Legacy Exxat Prism sidebar: color-coded **text** per module block, pink sidebar wash, bespoke selection chrome — **do not reproduce** when building on Exxat DS.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: TablePropertiesDrawer must receive currentView and onViewChange when used with ListPageTemplate view tabs. Auto-attaches when editing apps/web React files; ask explicitly when wiring view-type-aware drawers.
|
|
3
|
+
globs: apps/web/{components,packages/ui}/**/*.{tsx,ts}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
appliesTo: [react]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Exxat DS — Table properties + active view
|
|
9
|
+
|
|
10
|
+
**Authoritative detail:** **`apps/web/AGENTS.md` §4.2**.
|
|
11
|
+
|
|
12
|
+
## Why this exists
|
|
13
|
+
|
|
14
|
+
`TablePropertiesDrawer` uses **`currentView`** (`DataListViewType`) for the first summary row (“Board display” vs “Table display”, matching icons/descriptions) and to show **table-only** vs **board-only** sub-panels. If **`currentView`** is omitted, the drawer assumes **table** — wrong when the tab is **Board**, **List**, or **Dashboard**.
|
|
15
|
+
|
|
16
|
+
## MUST
|
|
17
|
+
|
|
18
|
+
When **`ListPageTemplate`** drives **`tab.viewType`** and the page renders **`TablePropertiesDrawer`** (directly or via a toolbar slot):
|
|
19
|
+
|
|
20
|
+
1. Pass **`currentView={view}`** (same value as **`tab.viewType`** passed into your table component).
|
|
21
|
+
2. Pass **`onViewChange`** from **`renderContent={(tab, updateTab) => ...}`** so the drawer’s view-type tiles stay in sync with the tab:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { dataListViewIcon, type DataListViewType } from "@/lib/data-list-view"
|
|
25
|
+
|
|
26
|
+
onViewChange={(v: DataListViewType) =>
|
|
27
|
+
updateTab({ viewType: v, icon: dataListViewIcon(v) })
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
3. Thread **`view`** and **`onViewChange`** through: **client → table component → drawer toolbar → `TablePropertiesDrawer`**.
|
|
32
|
+
|
|
33
|
+
**Reference implementations:** `components/library-hub-client.tsx` + `library-table.tsx` (canonical seven-view hub), `components/columns-showcase.tsx` (`LibraryTable` + custom `columnDefs`), `components/tokens-themes-client.tsx` (`FULL_HUB_SUPPORTED_VIEWS` + `tokens-hub-auxiliary-views.tsx`). **Add view allowlist:** **`.cursor/rules/exxat-hub-supported-views.mdc`**.
|
|
34
|
+
|
|
35
|
+
## Deep-linking into a specific panel
|
|
36
|
+
|
|
37
|
+
`TablePropertiesDrawer` accepts an optional **`initialPanel`** prop so callsites can open the drawer focused on a named panel — `"main"` (default), `"table-display"`, `"filter"`, `"sort"`, `"group"`, `"columns"`, or `"conditional-rules"`. The current built-in use is the **Add Conditional Rule** item in every column header menu, which deep-links to the **Conditional rules** panel.
|
|
38
|
+
|
|
39
|
+
### MUST
|
|
40
|
+
|
|
41
|
+
1. **One state pair owns it.** `useTableState` exposes **`sheetInitialPanel`** + **`setSheetInitialPanel`** alongside `sheetOpen` / `setSheetOpen`. Read both in the drawer button; pass **`initialPanel={sheetInitialPanel}`** to `TablePropertiesDrawer`. Reference: `packages/ui/src/components/table-properties/drawer-button.tsx`.
|
|
42
|
+
2. **The toolbar Properties button MUST clear the deep-link.** Otherwise the next plain "Properties" click re-opens onto whatever panel the previous deep-link set:
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
onClick={() => {
|
|
46
|
+
setSheetInitialPanel?.(null)
|
|
47
|
+
setSheetOpen(true)
|
|
48
|
+
}}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
3. **Deep-link callsites MUST set panel + open in the same batched setState call.** Two setters in one callback land in one render, so the drawer mounts with `initialPanel` already populated — no panel-flash:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
setSheetInitialPanel("conditional-rules")
|
|
55
|
+
setSheetOpen(true)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
4. **From inside a Radix DropdownMenu, queue the action into `onCloseAutoFocus`.** Opening the non-modal Sheet synchronously from `DropdownMenuItem.onSelect` races with the menu close cycle. Use the `columnMenuPendingActionRef` pattern in `packages/ui/src/components/data-table/index.tsx` (or copy it) so the drawer opens after focus has returned to the trigger.
|
|
59
|
+
|
|
60
|
+
### MUST NOT
|
|
61
|
+
|
|
62
|
+
- Don't introduce a second source of truth for "which panel" — `sheetInitialPanel` is the only deep-link channel. The drawer's internal `sheetPanel` state stays internal.
|
|
63
|
+
- Don't call `setSheetInitialPanel` from a non-deep-link callsite (e.g. a generic "open Properties" toolbar button). Leave it `null` for index opens.
|
|
64
|
+
|
|
65
|
+
## View-type tile grid is uniformly square
|
|
66
|
+
|
|
67
|
+
The drawer's "View type" tile grid (and the Export drawer's "File format" grid) renders through `SelectionTileGrid` with `interaction="button"` + `labelPlacement="inside"`. The shared `selectionTileClassNames` utility now applies **`aspect-square`** so every tile is the same shape regardless of how many tiles populate the last row of a `grid-cols-N` track. Two-word labels (e.g. "List & details") wrap inside the square because `leading-tight` keeps line height compact.
|
|
68
|
+
|
|
69
|
+
When you compose your own tile-style picker, **prefer the shared `SelectionTileGrid`** (or `selectionTileClassNames` directly) instead of inventing flex/grid wrappers — that's the only way to keep the squares uniform across the system.
|
|
70
|
+
|
|
71
|
+
## MUST NOT (overall)
|
|
72
|
+
|
|
73
|
+
- Mount **`TablePropertiesDrawer`** on a multi-view list page **without** **`currentView`** when the active view is known from the tab.
|
|
74
|
+
- Omit **`onViewChange`** if the product shows the **view type** control inside Properties (otherwise tiles cannot update the tab).
|
|
75
|
+
|
|
76
|
+
## See also
|
|
77
|
+
|
|
78
|
+
- **`apps/web/AGENTS.md` §4.2**, **§13** checklist
|
|
79
|
+
- **`.cursor/rules/exxat-list-page-connected-views.mdc`**
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — table row identity preview uses HoverCard + DS tokens, not bespoke Popover cards
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
appliesTo: [react]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — table row preview (hover / click)
|
|
8
|
+
|
|
9
|
+
When a **data table** cell opens a **rich preview** of a person or record (student, placement, team member):
|
|
10
|
+
|
|
11
|
+
## MUST
|
|
12
|
+
|
|
13
|
+
- Use **`HoverCard`** (`@/components/ui/hover-card`) for hover-driven previews, or **`Popover`** only when the preview is **click-pinned** and small.
|
|
14
|
+
- Structure identity with **`AvatarInitials`**, name, **`font-mono tabular-nums`** on IDs only (**`exxat-mono-ids.mdc`**), **`ListHubStatusBadge`** + **`lib/list-status-badges.ts`** for status — **color + icon** (**`exxat-accessibility.mdc`** 1.4.1).
|
|
15
|
+
- Reuse table cell patterns from **`@/components/data-views`** (`PeopleAvatarRailCell`, `PillCell`, progress cells) — see **`columns-showcase.tsx`**.
|
|
16
|
+
|
|
17
|
+
## MUST NOT
|
|
18
|
+
|
|
19
|
+
- Bespoke **`PopoverContent`** layouts with ad-hoc spacing, one-off progress bars, or status chips without the shared badge map.
|
|
20
|
+
- Raw email as the only identifier when a display name exists (**`exxat-person-identity-display.mdc`**).
|
|
21
|
+
|
|
22
|
+
## Reference
|
|
23
|
+
|
|
24
|
+
- **`HoverCard`** docs in `packages/ui/src/components/ui/hover-card.tsx`
|
|
25
|
+
- **`apps/web/docs/blueprints/data-table.md`**
|