@exxatdesignux/ui 0.5.11 → 0.5.13
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 +45 -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 -3
- 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/exxat-product-logo.tsx +3 -3
- 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-vite/src/styles/globals.css +25 -0
- 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/globals.css +0 -20
- 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/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,142 @@
|
|
|
1
|
+
# WCAG 2.1 AA — Full Accessibility Checklist
|
|
2
|
+
|
|
3
|
+
This is the single source of truth for all accessibility requirements in Exxat DS. Every component, edit, or feature must pass ALL applicable sections before it is considered done.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Interactive Elements
|
|
8
|
+
|
|
9
|
+
- Every `<button>` has visible text OR `aria-label`
|
|
10
|
+
- Icon-only buttons: `aria-label` + wrap with `<Tip>` (from `@/components/ui/tip`) — no exceptions
|
|
11
|
+
- Stateful buttons describe current state: `aria-label="Sort ascending — click to sort descending"`
|
|
12
|
+
- Links describe destination (no "click here"); new-window links: "(opens in new tab)"
|
|
13
|
+
- No nested interactive elements (button inside button, anchor inside button)
|
|
14
|
+
- Custom clickable `<div>`/`<span>`: `role="button"` + `tabIndex={0}` + `onKeyDown` (Enter/Space)
|
|
15
|
+
- Disabled: use native `disabled` or `aria-disabled="true"`; keep discoverable by screen readers
|
|
16
|
+
|
|
17
|
+
## 2. Keyboard
|
|
18
|
+
|
|
19
|
+
- ALL interactions reachable by keyboard alone — zero mouse-only features
|
|
20
|
+
- Tab order follows visual reading order
|
|
21
|
+
- Skip link → `#main-content` (verify after layout changes)
|
|
22
|
+
- `Enter`/`Space` activate buttons; `Escape` closes modals/popovers; `Arrow keys` in composite widgets
|
|
23
|
+
- `focus-visible:` ring on ALL interactive elements (≥ 2px, 3:1 contrast against adjacent colors)
|
|
24
|
+
- Hidden elements (opacity-0): `group-focus-within:opacity-100` for keyboard visibility
|
|
25
|
+
- Modal: focus trapped inside, returns to trigger on close
|
|
26
|
+
- Multi-step forms: move focus to step heading on advance (`tabIndex={-1}` + `.focus()`)
|
|
27
|
+
- Popover/Dropdown: `initialFocus` on first interactive child
|
|
28
|
+
|
|
29
|
+
## 3. Forms & Inputs
|
|
30
|
+
|
|
31
|
+
- Every `<input>`/`<textarea>`/`<select>` has `<label>` (visible or `sr-only`) OR `aria-label`
|
|
32
|
+
- Labels use `htmlFor` matching input `id`; placeholder is NOT a label
|
|
33
|
+
- Errors: `aria-describedby` → error element + `aria-invalid="true"` + visible descriptive text
|
|
34
|
+
- Required fields: `aria-required="true"` or HTML `required`
|
|
35
|
+
- Grouped controls: `<fieldset>` + `<legend>` or `role="group"` + `aria-labelledby`
|
|
36
|
+
- Dates: ALWAYS Calendar + Popover picker; format MM/DD/YYYY; `initialFocus` on Calendar open
|
|
37
|
+
- Toggle/Switch: `role="switch"` + `aria-checked` + `<label htmlFor>`
|
|
38
|
+
|
|
39
|
+
## 4. Semantic Structure
|
|
40
|
+
|
|
41
|
+
- One `<main>` per page with `id="main-content"` + `tabIndex={-1}` (required for skip link)
|
|
42
|
+
- Heading hierarchy: one `<h1>` (via `PageHeader`), logical `<h2>` → `<h3>` (no skipping)
|
|
43
|
+
- `SiteHeader` title in the breadcrumb bar is NOT an `<h1>`
|
|
44
|
+
- Multiple `<nav>` elements: each must have `aria-label`
|
|
45
|
+
- Modal/Sheet/Dialog: `DialogTitle`/`SheetTitle` ALWAYS present — use `sr-only` if visually hidden
|
|
46
|
+
- `<aside>` panels: `aria-label` (e.g. "Ask Leo assistant", "Rotation navigation")
|
|
47
|
+
- Data tables: `<table>` + `<thead>` + `<th scope="col">`; sortable columns: `aria-sort` on `<th>`
|
|
48
|
+
- Listbox: `role="listbox"` + `role="option"` + `aria-selected`
|
|
49
|
+
|
|
50
|
+
## 5. Tooltips
|
|
51
|
+
|
|
52
|
+
- Use `<Tip>` from `@/components/ui/tip` — NEVER the HTML `title` attribute
|
|
53
|
+
- Open on BOTH hover AND keyboard focus
|
|
54
|
+
- Every icon-only button gets `<Tip>` — no exceptions
|
|
55
|
+
- Tooltip content is supplementary; never the sole source of critical information
|
|
56
|
+
|
|
57
|
+
## 6. Color & Contrast
|
|
58
|
+
|
|
59
|
+
- **Normal text**: 4.5:1 ratio
|
|
60
|
+
- **Large text** (≥18px regular / ≥14px bold): 3:1
|
|
61
|
+
- **UI components** (borders, focus rings): 3:1
|
|
62
|
+
- Status conveyed NEVER by color alone — always include text label or icon alongside
|
|
63
|
+
- Error states: red + icon + descriptive text (not just red color)
|
|
64
|
+
- Decorative icons: `aria-hidden="true"`
|
|
65
|
+
- All ratios apply in BOTH light AND dark themes
|
|
66
|
+
- Hover states visibly distinct in dark mode (brand-tinted `--accent`, not grey)
|
|
67
|
+
- Test against all themes: Lavender × Prism × light × dark × high-contrast
|
|
68
|
+
|
|
69
|
+
## 7. Dynamic Content
|
|
70
|
+
|
|
71
|
+
- Count changes (filter results, badge updates): `aria-live="polite"` on the element
|
|
72
|
+
- Toast/snackbar notifications: `role="status"` or `aria-live="polite"`
|
|
73
|
+
- Loading states: `aria-busy="true"` on the loading container
|
|
74
|
+
- Progress indicators: `role="progressbar"` + `aria-valuenow/min/max`
|
|
75
|
+
|
|
76
|
+
## 8. Images & Media
|
|
77
|
+
|
|
78
|
+
- Informative images: descriptive `alt` text
|
|
79
|
+
- Decorative images: `alt=""` or `aria-hidden="true"`
|
|
80
|
+
- SVG icons accompanying text: `aria-hidden="true"`
|
|
81
|
+
- Standalone SVG (no adjacent text): `role="img"` + `aria-label`
|
|
82
|
+
|
|
83
|
+
## 9. ARIA Roles — Critical Rules
|
|
84
|
+
|
|
85
|
+
### Tabs
|
|
86
|
+
- `role="tablist"` → only `role="tab"` (or equivalent tab semantics) as direct children
|
|
87
|
+
- **Never** put `role="button"`, menus (`aria-haspopup`), remove buttons, or other controls **inside** the `tablist` container
|
|
88
|
+
- Tab panels: `role="tabpanel"` + `aria-labelledby`
|
|
89
|
+
|
|
90
|
+
### View Switchers (tabs + per-tab settings + remove)
|
|
91
|
+
- These are composite toolbar widgets — use `role="toolbar"` + `aria-label`
|
|
92
|
+
- Use `aria-pressed` on toggle-style controls within the toolbar
|
|
93
|
+
- Do NOT misuse `tablist`/`tab` for mixed-control toolbars
|
|
94
|
+
|
|
95
|
+
### Touch Targets (WCAG 2.2 — 2.5.8)
|
|
96
|
+
- Interactive controls: minimum **24×24 CSS pixels**, OR 24px spacing so hit areas don't overlap
|
|
97
|
+
- Icon-only buttons: `size-6` or `min-h-6 min-w-6` — **never** `size-4` as the sole target
|
|
98
|
+
|
|
99
|
+
## 10. Component-Specific Requirements
|
|
100
|
+
|
|
101
|
+
| Component | Requirements |
|
|
102
|
+
|-----------|-------------|
|
|
103
|
+
| Sidebar nav | `aria-current="page"` on active link; badges include value in `aria-label`; collapsed state → tooltip shows label |
|
|
104
|
+
| DataTable header column menu | `group-focus-within/th:opacity-100`; wrap trigger with `<Tip label="Column options">` |
|
|
105
|
+
| Sort button | `<Tip label="Sort by {col}">` + `aria-sort` attribute on `<th>` |
|
|
106
|
+
| Selection checkbox | `aria-label="Select {row name}"` |
|
|
107
|
+
| Tabs | `role="tablist"` / `role="tab"` / `role="tabpanel"` + `aria-selected` + arrow key navigation |
|
|
108
|
+
| Dropdown | `aria-haspopup` + `aria-expanded` (Radix handles automatically) |
|
|
109
|
+
| Filter pill | Keyboard-navigable; Escape closes the popover |
|
|
110
|
+
| Modal/Dialog | Focus trap + Escape closes + `aria-hidden` on background (Radix handles automatically) |
|
|
111
|
+
| Sheet/Drawer | `SheetTitle` required; floating style defined in component reuse rules |
|
|
112
|
+
| Ask Leo sidebar | `<aside aria-label="Ask Leo assistant">` |
|
|
113
|
+
|
|
114
|
+
## 11. Pre-Completion Testing Protocol
|
|
115
|
+
|
|
116
|
+
Run for EVERY task before marking done:
|
|
117
|
+
|
|
118
|
+
1. **Keyboard:** Tab through all changed elements — can you reach and activate every control without a mouse?
|
|
119
|
+
2. **Focus ring:** Visible on every interactive element after tabbing to it?
|
|
120
|
+
3. **Tooltips:** Every icon-only button has `<Tip>`?
|
|
121
|
+
4. **Labels:** Every input has a label? Every button has text or `aria-label`?
|
|
122
|
+
5. **Color contrast:** Text ≥ 4.5:1, UI ≥ 3:1? (Check with browser devtools or axe)
|
|
123
|
+
6. **Dark mode:** Repeat contrast check in dark theme
|
|
124
|
+
7. **200% zoom:** Layout usable at 200% browser zoom?
|
|
125
|
+
8. **axe audit:** Run axe (DevTools extension) on any page where you touched views toolbar, tabs, or primary list surfaces
|
|
126
|
+
|
|
127
|
+
## Quick Reference Card
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
Icon button → aria-label + <Tip>
|
|
131
|
+
Toggle/Switch → role="switch" + aria-checked
|
|
132
|
+
Dropdown → aria-haspopup + aria-expanded (Radix auto)
|
|
133
|
+
Tab → role="tab" + aria-selected
|
|
134
|
+
Sort column → aria-sort on <th>
|
|
135
|
+
Live count → aria-live="polite"
|
|
136
|
+
Error message → id + aria-describedby on input
|
|
137
|
+
Date input → Calendar + Popover + initialFocus
|
|
138
|
+
Modal → DialogTitle (required) + focus trap
|
|
139
|
+
Progress → role="progressbar" + aria-valuenow/min/max
|
|
140
|
+
Tablist → only tab-role children (no buttons/menus inside)
|
|
141
|
+
View switcher → role="toolbar" + aria-label + aria-pressed
|
|
142
|
+
```
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Coach Marks — Implementation Guide
|
|
2
|
+
|
|
3
|
+
> **Use coach marks for onboarding flows and feature discovery.** Every tour is defined once, targets elements by CSS selector, and is managed centrally from the Settings page.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
| Component | Location | Purpose |
|
|
10
|
+
|-----------|----------|---------|
|
|
11
|
+
| `CoachMark` | `@/components/ui/coach-mark` | Selector-targeted popover with spotlight overlay, brand-colored background |
|
|
12
|
+
| `useCoachMark` | `@/hooks/use-coach-mark` | Flow state manager — step navigation, localStorage persistence, element targeting |
|
|
13
|
+
| `CoachMarkStep` | `@/hooks/use-coach-mark` (type) | Step definition — target selector, side, align, title, description, optional image |
|
|
14
|
+
| Coach mark registry | `@/lib/coach-mark-registry` | Central definition of all flows for the Settings page |
|
|
15
|
+
| Settings page | `@/components/settings-client` + `app/(app)/settings/page.tsx` | UI to view, reset, and preview all coach mark flows |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## How It Works
|
|
20
|
+
|
|
21
|
+
1. **Selector-based targeting** — each step has a `target` CSS selector (e.g. `[aria-label='Properties']`). The coach mark finds the element, scrolls it into view, and positions a popover next to it.
|
|
22
|
+
2. **Spotlight overlay** — a semi-transparent backdrop with an SVG mask cutout highlights the target element with a ring.
|
|
23
|
+
3. **Brand background** — the popover uses `bg-brand-deep text-white` for high visibility. Buttons are white/inverted.
|
|
24
|
+
4. **localStorage persistence** — once completed or skipped, a flow is marked as dismissed and won't show again unless reset from Settings.
|
|
25
|
+
5. **Per-step positioning** — each step can specify its own `side` and `align` for optimal placement relative to the target.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Adding a New Coach Mark Flow
|
|
30
|
+
|
|
31
|
+
### Step 1 — Define the steps
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import type { CoachMarkStep } from "@/hooks/use-coach-mark"
|
|
35
|
+
|
|
36
|
+
const MY_TOUR_STEPS: CoachMarkStep[] = [
|
|
37
|
+
{
|
|
38
|
+
id: "step-1",
|
|
39
|
+
target: "[aria-label='My Widget']", // CSS selector for the target element
|
|
40
|
+
side: "bottom", // popover side: top | bottom | left | right
|
|
41
|
+
align: "start", // popover alignment: start | center | end
|
|
42
|
+
title: "Meet My Widget",
|
|
43
|
+
description: "This widget helps you do X. Click to explore.",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "step-2",
|
|
47
|
+
target: "button[aria-label='Settings']",
|
|
48
|
+
side: "left",
|
|
49
|
+
align: "center",
|
|
50
|
+
title: "Customise Settings",
|
|
51
|
+
description: "Open settings to configure Y and Z.",
|
|
52
|
+
image: "https://example.com/screenshot.jpg", // optional hero image
|
|
53
|
+
imageAlt: "Settings panel screenshot", // required when image is provided
|
|
54
|
+
},
|
|
55
|
+
]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Step 2 — Wire the hook and component
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { CoachMark } from "@/components/ui/coach-mark"
|
|
62
|
+
import { useCoachMark } from "@/hooks/use-coach-mark"
|
|
63
|
+
|
|
64
|
+
function MyPageClient() {
|
|
65
|
+
const tour = useCoachMark({
|
|
66
|
+
flowId: "my-feature-tour", // unique ID — used as localStorage key
|
|
67
|
+
steps: MY_TOUR_STEPS,
|
|
68
|
+
delay: 1200, // ms before first appearance
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<CoachMark state={tour} />
|
|
74
|
+
{/* rest of your page */}
|
|
75
|
+
</>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Key points:**
|
|
81
|
+
- `CoachMark` renders via portal — place it anywhere, it does NOT wrap children
|
|
82
|
+
- The component handles element lookup, scrolling, spotlight overlay, and positioning
|
|
83
|
+
- On flow completion, `localStorage` marks the flow as dismissed
|
|
84
|
+
|
|
85
|
+
### Step 3 — Register in the coach mark registry
|
|
86
|
+
|
|
87
|
+
Add your flow to `lib/coach-mark-registry.ts`:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
{
|
|
91
|
+
id: "my-feature-tour",
|
|
92
|
+
name: "My Feature Tour",
|
|
93
|
+
description: "Introduces the main controls and settings for My Feature.",
|
|
94
|
+
page: "My Feature",
|
|
95
|
+
pageUrl: "/my-feature",
|
|
96
|
+
stepCount: 2,
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This makes it appear in the Settings page where users can reset or preview it.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Variants
|
|
105
|
+
|
|
106
|
+
| Variant | How to use |
|
|
107
|
+
|---------|-----------|
|
|
108
|
+
| **Single step** | Pass a 1-item array to `steps` — no step indicator shown, button says "Got it" |
|
|
109
|
+
| **Multi-step flow** | Pass 2+ items — shows step dots, Skip, Back, Next buttons |
|
|
110
|
+
| **With image** | Set `image` + `imageAlt` on the step — hero image appears above the content |
|
|
111
|
+
| **Without image** | Omit `image` — text-only popover |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Target Selector Best Practices
|
|
116
|
+
|
|
117
|
+
Use stable, semantic selectors that survive refactors:
|
|
118
|
+
|
|
119
|
+
| Prefer | Avoid |
|
|
120
|
+
|--------|-------|
|
|
121
|
+
| `[aria-label='Properties']` | `.css-class-name` |
|
|
122
|
+
| `[role='toolbar'][aria-label='Views']` | `div:nth-child(3)` |
|
|
123
|
+
| `button[aria-label='Search']` | `#auto-generated-id` |
|
|
124
|
+
| `h1` | `.page-header > div > h1` |
|
|
125
|
+
|
|
126
|
+
If no stable selector exists, add a `data-coach-mark="step-name"` attribute to the target element.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Existing Flows
|
|
131
|
+
|
|
132
|
+
| Flow ID | Page | Steps | What it covers |
|
|
133
|
+
|---------|------|-------|---------------|
|
|
134
|
+
| `dashboard-tour` | Dashboard | 4 | Welcome, Key Metrics, AI Insights, Ask Leo |
|
|
135
|
+
| `placements-views-tour` | Placements | 6 | View tabs, view settings, add view, search, filter, Properties |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Settings Page
|
|
140
|
+
|
|
141
|
+
The Settings page at `/settings` (`components/settings-client.tsx`) provides:
|
|
142
|
+
|
|
143
|
+
- **List of all registered flows** from `lib/coach-mark-registry.ts`
|
|
144
|
+
- **Status** — Completed vs Active per flow
|
|
145
|
+
- **Reset** — clears localStorage so the tour replays on next visit
|
|
146
|
+
- **Preview** — resets the flow and navigates to its page
|
|
147
|
+
- **Reset all** — clears all coach mark dismissals
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Utilities (exported from `use-coach-mark`)
|
|
152
|
+
|
|
153
|
+
| Function | Purpose |
|
|
154
|
+
|----------|---------|
|
|
155
|
+
| `getAllCoachMarkKeys()` | List all dismissed coach mark flow IDs |
|
|
156
|
+
| `resetCoachMarkFlow(flowId)` | Clear dismissal for one flow |
|
|
157
|
+
| `resetAllCoachMarks()` | Clear all dismissals |
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Rules
|
|
162
|
+
|
|
163
|
+
1. **Always register new flows** in `lib/coach-mark-registry.ts` so they appear in Settings
|
|
164
|
+
2. **Use CSS selectors based on ARIA attributes** — they're stable and semantic
|
|
165
|
+
3. **Brand background is mandatory** — coach marks use `bg-brand-deep text-white`, not `bg-popover`
|
|
166
|
+
4. **No wrapping children** — `CoachMark` targets elements by selector, never wraps them
|
|
167
|
+
5. **Keep flows short** — 3–6 steps per flow is ideal; split longer tours into separate flows
|
|
168
|
+
6. **Add `data-coach-mark` attributes** if no stable selector exists on the target element
|
|
169
|
+
7. **Set appropriate `delay`** — 800–1200ms gives the page time to render before the first step appears
|