@eifi1/ui-kit 0.31.1 → 0.32.1
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/README.md +51 -28
- package/dist/account/delete-account-setting.d.ts +8 -3
- package/dist/admin/admin-action-confirm.d.ts +8 -3
- package/dist/admin/admin-action-log.d.ts +1 -1
- package/dist/admin/admin-action-log.js +4 -2
- package/dist/admin/admin-action-log.js.map +1 -1
- package/dist/admin/invitations-panel.d.ts +8 -3
- package/dist/admin/invitations-panel.js +29 -26
- package/dist/admin/invitations-panel.js.map +1 -1
- package/dist/admin/reviewer-scope-editor.d.ts +8 -3
- package/dist/admin/role-select.d.ts +10 -5
- package/dist/admin/transfer-ownership-dialog.d.ts +8 -3
- package/dist/admin/user-roster.d.ts +10 -5
- package/dist/admin/user-roster.js +8 -47
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/auth/auth-errors.d.ts +43 -8
- package/dist/auth/auth-errors.js +24 -1
- package/dist/auth/auth-errors.js.map +1 -1
- package/dist/auth/register-form.d.ts +8 -3
- package/dist/billing/billing-banner.d.ts +184 -0
- package/dist/billing/billing-banner.js +98 -0
- package/dist/billing/billing-banner.js.map +1 -0
- package/dist/billing/billing-labels.d.ts +100 -0
- package/dist/billing/billing-labels.js +76 -0
- package/dist/billing/billing-labels.js.map +1 -0
- package/dist/billing/billing-lock.d.ts +158 -0
- package/dist/billing/billing-lock.js +17 -0
- package/dist/billing/billing-lock.js.map +1 -0
- package/dist/billing/billing-standing.d.ts +149 -0
- package/dist/billing/billing-standing.js +28 -0
- package/dist/billing/billing-standing.js.map +1 -0
- package/dist/billing/plan-card.d.ts +248 -0
- package/dist/billing/plan-card.js +248 -0
- package/dist/billing/plan-card.js.map +1 -0
- package/dist/billing/plan-limit-notice.d.ts +156 -0
- package/dist/billing/plan-limit-notice.js +76 -0
- package/dist/billing/plan-limit-notice.js.map +1 -0
- package/dist/billing/plan-limit.d.ts +34 -0
- package/dist/billing/plan-limit.js +20 -0
- package/dist/billing/plan-limit.js.map +1 -0
- package/dist/billing/plan-price.d.ts +61 -0
- package/dist/billing/plan-price.js +45 -0
- package/dist/billing/plan-price.js.map +1 -0
- package/dist/billing/subscription-actions.d.ts +133 -0
- package/dist/billing/subscription-actions.js +28 -0
- package/dist/billing/subscription-actions.js.map +1 -0
- package/dist/billing/subscription-status-chip.d.ts +149 -0
- package/dist/billing/subscription-status-chip.js +48 -0
- package/dist/billing/subscription-status-chip.js.map +1 -0
- package/dist/chart.d.ts +9 -3
- package/dist/chart.js.map +1 -1
- package/dist/components/account-chips.d.ts +16 -4
- package/dist/components/account-chips.js +21 -3
- package/dist/components/account-chips.js.map +1 -1
- package/dist/components/alert-banner.d.ts +6 -1
- package/dist/components/alert-banner.js +5 -4
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.d.ts +8 -3
- package/dist/components/amount-input.js +5 -5
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/appearance-labels.d.ts +38 -0
- package/dist/components/appearance-labels.js +19 -0
- package/dist/components/appearance-labels.js.map +1 -0
- package/dist/components/autocomplete.js +2 -2
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/breadcrumbs.js +3 -2
- package/dist/components/breadcrumbs.js.map +1 -1
- package/dist/components/bulk-action-bar.d.ts +2 -2
- package/dist/components/bulk-action-bar.js +7 -12
- package/dist/components/bulk-action-bar.js.map +1 -1
- package/dist/components/button-group.d.ts +8 -3
- package/dist/components/button-group.js +7 -1
- package/dist/components/button-group.js.map +1 -1
- package/dist/components/calculator.d.ts +8 -3
- package/dist/components/calculator.js +4 -3
- package/dist/components/calculator.js.map +1 -1
- package/dist/components/calendar-heatmap.js +5 -5
- package/dist/components/calendar-heatmap.js.map +1 -1
- package/dist/components/chart-height.d.ts +23 -0
- package/dist/components/chart-height.js +15 -0
- package/dist/components/chart-height.js.map +1 -0
- package/dist/components/chart-zoom.js +6 -1
- package/dist/components/chart-zoom.js.map +1 -1
- package/dist/components/checkbox-group.js +2 -2
- package/dist/components/checkbox-group.js.map +1 -1
- package/dist/components/checkbox.d.ts +10 -0
- package/dist/components/checkbox.js +25 -10
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/chip.d.ts +19 -3
- package/dist/components/chip.js +54 -23
- package/dist/components/chip.js.map +1 -1
- package/dist/components/choice-card.d.ts +1 -1
- package/dist/components/choice-card.js +9 -8
- package/dist/components/choice-card.js.map +1 -1
- package/dist/components/column-mapper.js +1 -1
- package/dist/components/column-mapper.js.map +1 -1
- package/dist/components/combobox-core.js +8 -7
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.js +8 -8
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +8 -3
- package/dist/components/copy-button.d.ts +10 -5
- package/dist/components/country-select.js +1 -1
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/currency-select.js +3 -3
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +8 -3
- package/dist/components/data-table-cells.d.ts +1 -1
- package/dist/components/data-table-filter-popover.d.ts +1 -1
- package/dist/components/data-table-filter-popover.js +4 -1
- package/dist/components/data-table-filter-popover.js.map +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table-labels.d.ts +1 -1
- package/dist/components/data-table-pagination.d.ts +3 -2
- package/dist/components/data-table-pagination.js +15 -6
- package/dist/components/data-table-pagination.js.map +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +123 -52
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +1 -1
- package/dist/components/date-picker.js +18 -6
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/description-list.js +2 -2
- package/dist/components/description-list.js.map +1 -1
- package/dist/components/dialog-frame.js +2 -1
- package/dist/components/dialog-frame.js.map +1 -1
- package/dist/components/disclosure.js +3 -2
- package/dist/components/disclosure.js.map +1 -1
- package/dist/components/dropdown.d.ts +1 -1
- package/dist/components/dropdown.js +7 -1
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/facing-pair.d.ts +19 -6
- package/dist/components/facing-pair.js +3 -2
- package/dist/components/facing-pair.js.map +1 -1
- package/dist/components/field-parts.d.ts +102 -6
- package/dist/components/field-parts.js +81 -6
- package/dist/components/field-parts.js.map +1 -1
- package/dist/components/field-sync.d.ts +4 -1
- package/dist/components/field-sync.js +12 -2
- package/dist/components/field-sync.js.map +1 -1
- package/dist/components/field.js +2 -2
- package/dist/components/field.js.map +1 -1
- package/dist/components/file-button.d.ts +8 -3
- package/dist/components/file-dropzone.d.ts +8 -3
- package/dist/components/floating-panel.js +7 -4
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/focus-ring.d.ts +12 -0
- package/dist/components/focus-ring.js +8 -0
- package/dist/components/focus-ring.js.map +1 -0
- package/dist/components/form-actions.d.ts +8 -3
- package/dist/components/form-actions.js +33 -19
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/full-bleed-dialog.d.ts +11 -1
- package/dist/components/full-bleed-dialog.js +3 -1
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/iban-input.d.ts +8 -3
- package/dist/components/image-grid.js +1 -1
- package/dist/components/image-grid.js.map +1 -1
- package/dist/components/inline-edit-field.js +10 -4
- package/dist/components/inline-edit-field.js.map +1 -1
- package/dist/components/language-select.d.ts +8 -3
- package/dist/components/legal-page.d.ts +26 -5
- package/dist/components/legal-page.js +3 -1
- package/dist/components/legal-page.js.map +1 -1
- package/dist/components/legal.d.ts +8 -3
- package/dist/components/legal.js +2 -1
- package/dist/components/legal.js.map +1 -1
- package/dist/components/lightbox.js +24 -2
- package/dist/components/lightbox.js.map +1 -1
- package/dist/components/line-items.js +2 -1
- package/dist/components/line-items.js.map +1 -1
- package/dist/components/list.d.ts +5 -0
- package/dist/components/list.js +13 -9
- package/dist/components/list.js.map +1 -1
- package/dist/components/measured-grid.d.ts +3 -1
- package/dist/components/measured-grid.js +38 -20
- package/dist/components/measured-grid.js.map +1 -1
- package/dist/components/menu-item.js +2 -1
- package/dist/components/menu-item.js.map +1 -1
- package/dist/components/mini-calendar.js +8 -4
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/money-field.d.ts +8 -3
- package/dist/components/month-picker.js +50 -39
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/nav-pills.js +1 -1
- package/dist/components/nav-pills.js.map +1 -1
- package/dist/components/number-field.d.ts +8 -3
- package/dist/components/number-input.d.ts +11 -4
- package/dist/components/number-input.js +8 -6
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +8 -3
- package/dist/components/numpad-sheet.js +2 -1
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/one-time-code-input.js +2 -2
- package/dist/components/one-time-code-input.js.map +1 -1
- package/dist/components/page-contents.js +2 -2
- package/dist/components/page-contents.js.map +1 -1
- package/dist/components/phone-input.d.ts +8 -3
- package/dist/components/phone-input.js +4 -4
- package/dist/components/phone-input.js.map +1 -1
- package/dist/components/pie-chart.d.ts +8 -2
- package/dist/components/pie-chart.js +11 -7
- package/dist/components/pie-chart.js.map +1 -1
- package/dist/components/popover.d.ts +9 -1
- package/dist/components/popover.js +8 -4
- package/dist/components/popover.js.map +1 -1
- package/dist/components/row-actions.d.ts +100 -0
- package/dist/components/row-actions.js +231 -0
- package/dist/components/row-actions.js.map +1 -0
- package/dist/components/scroll-area.js +1 -1
- package/dist/components/scroll-area.js.map +1 -1
- package/dist/components/search-field.js +3 -2
- package/dist/components/search-field.js.map +1 -1
- package/dist/components/series-chart-budget.d.ts +30 -7
- package/dist/components/series-chart-budget.js +12 -4
- package/dist/components/series-chart-budget.js.map +1 -1
- package/dist/components/series-chart-ticks.d.ts +7 -1
- package/dist/components/series-chart-ticks.js +6 -0
- package/dist/components/series-chart-ticks.js.map +1 -1
- package/dist/components/series-chart.d.ts +58 -28
- package/dist/components/series-chart.js +42 -32
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/settings-fields.d.ts +57 -8
- package/dist/components/settings-fields.js +39 -1
- package/dist/components/settings-fields.js.map +1 -1
- package/dist/components/share-card.d.ts +11 -6
- package/dist/components/share-card.js +8 -2
- package/dist/components/share-card.js.map +1 -1
- package/dist/components/sign-chip.d.ts +4 -0
- package/dist/components/sign-chip.js +17 -4
- package/dist/components/sign-chip.js.map +1 -1
- package/dist/components/signature-pad.d.ts +3 -1
- package/dist/components/signature-pad.js +8 -5
- package/dist/components/signature-pad.js.map +1 -1
- package/dist/components/slider.js +7 -6
- package/dist/components/slider.js.map +1 -1
- package/dist/components/sparkline.d.ts +5 -1
- package/dist/components/sparkline.js +4 -2
- package/dist/components/sparkline.js.map +1 -1
- package/dist/components/stat-tile.d.ts +17 -1
- package/dist/components/stat-tile.js +48 -20
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/status-dot.d.ts +4 -0
- package/dist/components/status-dot.js +9 -2
- package/dist/components/status-dot.js.map +1 -1
- package/dist/components/swipeable-row.js +10 -2
- package/dist/components/swipeable-row.js.map +1 -1
- package/dist/components/switch.d.ts +21 -7
- package/dist/components/switch.js +24 -11
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table.js +2 -2
- package/dist/components/table.js.map +1 -1
- package/dist/components/text-link.d.ts +8 -3
- package/dist/components/text-link.js +1 -1
- package/dist/components/text-link.js.map +1 -1
- package/dist/components/text.d.ts +7 -2
- package/dist/components/text.js +5 -5
- package/dist/components/text.js.map +1 -1
- package/dist/components/tile-radio.js +2 -1
- package/dist/components/tile-radio.js.map +1 -1
- package/dist/components/time-input.d.ts +8 -3
- package/dist/components/toast.d.ts +18 -2
- package/dist/components/toast.js +8 -5
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toggle-group.d.ts +25 -3
- package/dist/components/toggle-group.js +40 -11
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle-legend.d.ts +10 -1
- package/dist/components/toggle-legend.js +21 -7
- package/dist/components/toggle-legend.js.map +1 -1
- package/dist/components/translation-review-editor.js +1 -1
- package/dist/components/translation-review-editor.js.map +1 -1
- package/dist/components/translation-review-parts.d.ts +4 -0
- package/dist/components/translation-review.d.ts +4 -0
- package/dist/components/translation-review.js +3 -3
- package/dist/components/translation-review.js.map +1 -1
- package/dist/components/tree-view.js +3 -2
- package/dist/components/tree-view.js.map +1 -1
- package/dist/components/treemap.d.ts +5 -2
- package/dist/components/treemap.js +33 -16
- package/dist/components/treemap.js.map +1 -1
- package/dist/components/ui.d.ts +8 -3
- package/dist/components/ui.js +207 -119
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -1
- package/dist/components/user-avatar.d.ts +5 -1
- package/dist/components/user-avatar.js +1 -1
- package/dist/components/user-avatar.js.map +1 -1
- package/dist/components/wizard-stepper.js +20 -14
- package/dist/components/wizard-stepper.js.map +1 -1
- package/dist/{data-table-labels-DUELeXpa.d.ts → data-table-labels-kpAAVi6g.d.ts} +26 -2
- package/dist/data-table.d.ts +1 -1
- package/dist/{delete-account-setting-CpIhzxxZ.d.ts → delete-account-setting-FLyJf-kZ.d.ts} +642 -60
- package/dist/demo/demo-banner.js +1 -0
- package/dist/demo/demo-banner.js.map +1 -1
- package/dist/feedback/feedback-attachment.d.ts +8 -3
- package/dist/feedback/feedback-context.d.ts +8 -3
- package/dist/feedback/feedback-crash.d.ts +8 -3
- package/dist/feedback/feedback-dialog.d.ts +8 -3
- package/dist/feedback/feedback-dialog.js +3 -3
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.d.ts +8 -3
- package/dist/feedback/feedback-inbox.js +2 -2
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback/feedback-labels.d.ts +8 -3
- package/dist/feedback/feedback-menu.d.ts +9 -4
- package/dist/feedback/feedback-record.d.ts +8 -3
- package/dist/feedback/feedback-row-detail.d.ts +8 -3
- package/dist/feedback/feedback-status-undo.d.ts +8 -3
- package/dist/feedback/feedback-submit.d.ts +8 -3
- package/dist/feedback/feedback-swipe.d.ts +8 -3
- package/dist/feedback/feedback-table.d.ts +8 -3
- package/dist/feedback/feedback-thread.d.ts +8 -3
- package/dist/feedback/feedback-thread.js +3 -3
- package/dist/feedback/feedback-thread.js.map +1 -1
- package/dist/feedback.d.ts +8 -3
- package/dist/hooks/use-anchored-panel.d.ts +32 -3
- package/dist/hooks/use-anchored-panel.js +21 -3
- package/dist/hooks/use-anchored-panel.js.map +1 -1
- package/dist/hooks/use-breakpoint.d.ts +88 -0
- package/dist/hooks/use-breakpoint.js +50 -0
- package/dist/hooks/use-breakpoint.js.map +1 -0
- package/dist/hooks/use-file-drop.d.ts +8 -3
- package/dist/hooks/use-large-text.d.ts +29 -0
- package/dist/hooks/use-large-text.js +20 -0
- package/dist/hooks/use-large-text.js.map +1 -0
- package/dist/hooks/use-windowed-rows.d.ts +52 -8
- package/dist/hooks/use-windowed-rows.js +94 -5
- package/dist/hooks/use-windowed-rows.js.map +1 -1
- package/dist/i18n/defaults.d.ts +8 -3
- package/dist/i18n/defaults.js +8 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +8 -3
- package/dist/i18n/german.js +94 -3
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +8 -3
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +10 -5
- package/dist/i18n/locales/de-CH.d.ts +8 -3
- package/dist/i18n/locales/en.d.ts +8 -3
- package/dist/i18n/locales/es.d.ts +8 -3
- package/dist/i18n/locales/es.js +89 -3
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +8 -3
- package/dist/i18n/locales/fr.js +91 -3
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +8 -3
- package/dist/i18n/locales/hu.js +91 -3
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +8 -3
- package/dist/i18n/locales/it.js +92 -3
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +8 -3
- package/dist/i18n/locales/zh.js +89 -3
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +8 -3
- package/dist/i18n/review.js +15 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +27 -11
- package/dist/index.js +77 -4
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-actions.d.ts +8 -3
- package/dist/landing/landing-sections.d.ts +8 -3
- package/dist/landing/landing-sections.js +7 -3
- package/dist/landing/landing-sections.js.map +1 -1
- package/dist/landing/public-header.d.ts +9 -4
- package/dist/landing/public-header.js +3 -3
- package/dist/landing/public-header.js.map +1 -1
- package/dist/landing/routing.d.ts +8 -3
- package/dist/lib/cn.js +3 -2
- package/dist/lib/cn.js.map +1 -1
- package/dist/rhf/fields.d.ts +8 -3
- package/dist/rhf/fields.js +1 -1
- package/dist/rhf/fields.js.map +1 -1
- package/dist/rhf/form.d.ts +8 -3
- package/dist/rhf/form.js +2 -2
- package/dist/rhf/form.js.map +1 -1
- package/dist/rhf.d.ts +8 -3
- package/dist/search/command-palette.d.ts +1 -1
- package/dist/search/command-palette.js +9 -8
- package/dist/search/command-palette.js.map +1 -1
- package/dist/settings/settings-layout.js +2 -1
- package/dist/settings/settings-layout.js.map +1 -1
- package/dist/settings/settings-section.d.ts +8 -3
- package/dist/settings/use-account-appearance.d.ts +92 -0
- package/dist/settings/use-account-appearance.js +63 -0
- package/dist/settings/use-account-appearance.js.map +1 -0
- package/dist/settings/use-settings-route.d.ts +1 -1
- package/dist/settings/use-settings-route.js +2 -3
- package/dist/settings/use-settings-route.js.map +1 -1
- package/dist/shell/app-shell.d.ts +12 -171
- package/dist/shell/app-shell.js +144 -28
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/auth-layout.js +1 -1
- package/dist/shell/auth-layout.js.map +1 -1
- package/dist/shell/option-switcher-menu.js +1 -1
- package/dist/shell/option-switcher-menu.js.map +1 -1
- package/dist/shell/top-bar-brand.d.ts +8 -3
- package/dist/shell/top-bar-brand.js +3 -1
- package/dist/shell/top-bar-brand.js.map +1 -1
- package/dist/shell/topbar-action-menu.d.ts +4 -0
- package/dist/shell/topbar-action-menu.js +3 -3
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell/topbar-controls.js +4 -4
- package/dist/shell/topbar-controls.js.map +1 -1
- package/dist/shell.d.ts +9 -5
- package/dist/theme/contrast-tokens.d.ts +41 -0
- package/dist/theme/contrast-tokens.js +59 -0
- package/dist/theme/contrast-tokens.js.map +1 -0
- package/dist/theme/contrast.d.ts +110 -0
- package/dist/theme/contrast.js +128 -0
- package/dist/theme/contrast.js.map +1 -0
- package/dist/theme/palette-presets.d.ts +12 -1
- package/dist/theme/palette-presets.js +9 -4
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/theme/palette-store.js.map +1 -1
- package/dist/theme/text-size.d.ts +173 -0
- package/dist/theme/text-size.js +121 -0
- package/dist/theme/text-size.js.map +1 -0
- package/dist/wizard/stepper-nav.d.ts +8 -3
- package/dist/wizard/wizard-summary.js +1 -1
- package/dist/wizard/wizard-summary.js.map +1 -1
- package/dist/wizard.d.ts +8 -3
- package/package.json +1 -1
- package/src/admin/admin-action-log.tsx +6 -1
- package/src/admin/invitations-panel.tsx +36 -29
- package/src/admin/user-roster.tsx +17 -72
- package/src/auth/auth-errors.ts +68 -7
- package/src/billing/billing-banner.tsx +205 -0
- package/src/billing/billing-labels.ts +240 -0
- package/src/billing/billing-lock.ts +75 -0
- package/src/billing/billing-standing.ts +72 -0
- package/src/billing/plan-card.tsx +458 -0
- package/src/billing/plan-limit-notice.tsx +145 -0
- package/src/billing/plan-limit.ts +53 -0
- package/src/billing/plan-price.ts +101 -0
- package/src/billing/subscription-actions.tsx +76 -0
- package/src/billing/subscription-status-chip.tsx +95 -0
- package/src/chart.ts +2 -0
- package/src/components/account-chips.tsx +36 -10
- package/src/components/alert-banner.tsx +16 -7
- package/src/components/amount-input.tsx +5 -5
- package/src/components/appearance-labels.ts +47 -0
- package/src/components/autocomplete.tsx +2 -2
- package/src/components/breadcrumbs.tsx +3 -3
- package/src/components/bulk-action-bar.tsx +11 -14
- package/src/components/button-group.tsx +21 -0
- package/src/components/calculator.tsx +4 -3
- package/src/components/calendar-heatmap.tsx +5 -5
- package/src/components/chart-height.ts +30 -0
- package/src/components/chart-zoom.tsx +6 -1
- package/src/components/checkbox-group.tsx +2 -2
- package/src/components/checkbox.tsx +62 -20
- package/src/components/chip.tsx +139 -30
- package/src/components/choice-card.tsx +16 -11
- package/src/components/column-mapper.tsx +1 -1
- package/src/components/combobox-core.tsx +8 -7
- package/src/components/combobox.tsx +8 -8
- package/src/components/country-select.tsx +1 -1
- package/src/components/currency-select.tsx +3 -3
- package/src/components/data-table-filter-popover.tsx +7 -1
- package/src/components/data-table-pagination.tsx +60 -31
- package/src/components/data-table.tsx +164 -43
- package/src/components/date-picker.tsx +44 -10
- package/src/components/description-list.tsx +2 -2
- package/src/components/dialog-frame.tsx +2 -1
- package/src/components/disclosure.tsx +3 -2
- package/src/components/dropdown.tsx +10 -2
- package/src/components/facing-pair.ts +12 -5
- package/src/components/field-parts.tsx +190 -7
- package/src/components/field-sync.tsx +17 -3
- package/src/components/field.tsx +2 -2
- package/src/components/floating-panel.tsx +7 -4
- package/src/components/focus-ring.ts +12 -0
- package/src/components/form-actions.tsx +55 -18
- package/src/components/full-bleed-dialog.tsx +18 -4
- package/src/components/image-grid.tsx +1 -1
- package/src/components/inline-edit-field.tsx +26 -4
- package/src/components/legal-page.tsx +28 -2
- package/src/components/legal.tsx +19 -1
- package/src/components/lightbox.tsx +24 -2
- package/src/components/line-items.tsx +7 -1
- package/src/components/list.tsx +36 -10
- package/src/components/measured-grid.tsx +49 -24
- package/src/components/menu-item.tsx +2 -2
- package/src/components/mini-calendar.tsx +8 -4
- package/src/components/month-picker.tsx +15 -6
- package/src/components/nav-pills.tsx +1 -1
- package/src/components/number-input.tsx +14 -7
- package/src/components/numpad-sheet.tsx +2 -2
- package/src/components/one-time-code-input.tsx +2 -2
- package/src/components/page-contents.tsx +2 -2
- package/src/components/phone-input.tsx +9 -4
- package/src/components/pie-chart.tsx +19 -9
- package/src/components/popover.tsx +22 -5
- package/src/components/row-actions.tsx +450 -0
- package/src/components/scroll-area.tsx +1 -1
- package/src/components/search-field.tsx +4 -2
- package/src/components/series-chart-budget.ts +40 -8
- package/src/components/series-chart-ticks.ts +10 -0
- package/src/components/series-chart.tsx +123 -72
- package/src/components/settings-fields.tsx +89 -5
- package/src/components/share-card.tsx +31 -2
- package/src/components/sign-chip.tsx +28 -4
- package/src/components/signature-pad.tsx +13 -7
- package/src/components/slider.tsx +22 -9
- package/src/components/sparkline.tsx +11 -3
- package/src/components/stat-tile.tsx +70 -14
- package/src/components/status-dot.tsx +25 -2
- package/src/components/swipeable-row.tsx +10 -2
- package/src/components/switch.tsx +57 -23
- package/src/components/table.tsx +2 -2
- package/src/components/text-link.tsx +1 -1
- package/src/components/text.tsx +11 -6
- package/src/components/tile-radio.tsx +4 -1
- package/src/components/toast.tsx +27 -7
- package/src/components/toggle-group.tsx +115 -14
- package/src/components/toggle-legend.tsx +53 -8
- package/src/components/translation-review-editor.tsx +1 -1
- package/src/components/translation-review.tsx +11 -5
- package/src/components/tree-view.tsx +3 -2
- package/src/components/treemap.tsx +27 -10
- package/src/components/ui.tsx +353 -153
- package/src/components/user-avatar.tsx +1 -1
- package/src/components/wizard-stepper.tsx +5 -1
- package/src/demo/demo-banner.tsx +5 -0
- package/src/feedback/feedback-dialog.tsx +3 -3
- package/src/feedback/feedback-inbox.tsx +2 -2
- package/src/feedback/feedback-thread.tsx +3 -3
- package/src/hooks/use-anchored-panel.ts +67 -5
- package/src/hooks/use-breakpoint.ts +132 -0
- package/src/hooks/use-large-text.ts +41 -0
- package/src/hooks/use-windowed-rows.ts +168 -12
- package/src/i18n/defaults.ts +8 -0
- package/src/i18n/german.ts +107 -0
- package/src/i18n/kit-labels.tsx +12 -0
- package/src/i18n/locales/es.ts +98 -0
- package/src/i18n/locales/fr.ts +102 -0
- package/src/i18n/locales/hu.ts +100 -0
- package/src/i18n/locales/it.ts +103 -0
- package/src/i18n/locales/zh.ts +94 -0
- package/src/i18n/review.ts +14 -0
- package/src/index.ts +76 -6
- package/src/landing/landing-sections.tsx +7 -3
- package/src/landing/public-header.tsx +4 -4
- package/src/lib/cn.ts +13 -2
- package/src/rhf/fields.tsx +1 -1
- package/src/rhf/form.tsx +2 -2
- package/src/search/command-palette.tsx +10 -9
- package/src/settings/settings-layout.tsx +2 -1
- package/src/settings/use-account-appearance.ts +148 -0
- package/src/settings/use-settings-route.ts +3 -4
- package/src/shell/app-shell.tsx +278 -54
- package/src/shell/auth-layout.tsx +1 -1
- package/src/shell/option-switcher-menu.tsx +1 -1
- package/src/shell/top-bar-brand.tsx +3 -1
- package/src/shell/topbar-action-menu.tsx +3 -3
- package/src/shell/topbar-controls.tsx +4 -4
- package/src/theme/contrast-tokens.ts +112 -0
- package/src/theme/contrast.ts +225 -0
- package/src/theme/palette-presets.ts +24 -4
- package/src/theme/palette-store.ts +6 -1
- package/src/theme/text-size.ts +283 -0
- package/src/wizard/wizard-summary.tsx +4 -2
- package/tokens.css +158 -8
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { Link as RouterLink } from "react-router";
|
|
3
3
|
import { cn } from "../lib/cn";
|
|
4
|
+
import { FOCUS_RING } from "../components/focus-ring";
|
|
4
5
|
import { useKitLink } from "../i18n/kit-labels";
|
|
5
6
|
import { pickLinkRenderer } from "../components/text-link";
|
|
6
7
|
import type { KitLinkComponent, KitLinkProps } from "../i18n/kit-labels";
|
|
@@ -58,7 +59,8 @@ export function TopBarBrand({
|
|
|
58
59
|
href={to}
|
|
59
60
|
className={cn(
|
|
60
61
|
// `relative` contains the sr-only name.
|
|
61
|
-
"relative flex min-w-0 items-center gap-2 rounded-md font-semibold text-[var(--text-primary)] hover:opacity-80
|
|
62
|
+
"relative flex min-w-0 items-center gap-2 rounded-md font-semibold text-[var(--text-primary)] hover:opacity-80",
|
|
63
|
+
FOCUS_RING,
|
|
62
64
|
className,
|
|
63
65
|
)}
|
|
64
66
|
>
|
|
@@ -326,7 +326,7 @@ export function TopBarActionMenu({
|
|
|
326
326
|
const footerBody = typeof footer === "function" ? footer(close) : footer;
|
|
327
327
|
const headerExtra = typeof header?.extra === "function" ? header.extra(close) : header?.extra;
|
|
328
328
|
const footerClass =
|
|
329
|
-
"flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--text-muted)] [&_a:hover]:text-[var(--text-primary)] [&_a]:rounded-sm [&_a:focus-visible]:outline-none [&_a:focus-visible]:ring-
|
|
329
|
+
"flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--text-muted)] [&_a:hover]:text-[var(--text-primary)] [&_a]:rounded-sm [&_a:focus-visible]:outline-none [&_a:focus-visible]:ring-[length:var(--focus-ring-width)] [&_a:focus-visible]:ring-[var(--brand)]";
|
|
330
330
|
/** One action or link row — loose, or inside a heading's group. */
|
|
331
331
|
const row = (entry: RowEntry) => (
|
|
332
332
|
<li key={entry.key}>
|
|
@@ -374,7 +374,7 @@ export function TopBarActionMenu({
|
|
|
374
374
|
</MenuSection>
|
|
375
375
|
)}
|
|
376
376
|
{heading && (
|
|
377
|
-
<li className="px-3 py-1 text-
|
|
377
|
+
<li className="px-3 py-1 text-micro font-semibold uppercase tracking-wide text-[var(--text-placeholder)]">
|
|
378
378
|
{heading}
|
|
379
379
|
</li>
|
|
380
380
|
)}
|
|
@@ -397,7 +397,7 @@ export function TopBarActionMenu({
|
|
|
397
397
|
id={headingId}
|
|
398
398
|
role="presentation"
|
|
399
399
|
className={cn(
|
|
400
|
-
"px-3 pb-0.5 text-
|
|
400
|
+
"px-3 pb-0.5 text-caption font-semibold uppercase tracking-wide text-[var(--text-muted)] [overflow-wrap:anywhere]",
|
|
401
401
|
tight ? "pt-1" : "pt-2.5",
|
|
402
402
|
)}
|
|
403
403
|
>
|
|
@@ -19,7 +19,7 @@ export const TOPBAR_MENU_ITEM_CLASS =
|
|
|
19
19
|
/** The small uppercase heading row the top-bar menus share (`heading`). */
|
|
20
20
|
function MenuHeading({ children }: { children: string }) {
|
|
21
21
|
return (
|
|
22
|
-
<li className="px-3 py-1 text-
|
|
22
|
+
<li className="px-3 py-1 text-micro font-semibold uppercase tracking-wide text-[var(--text-placeholder)]">
|
|
23
23
|
{children}
|
|
24
24
|
</li>
|
|
25
25
|
);
|
|
@@ -131,7 +131,7 @@ export function PaletteMenu({
|
|
|
131
131
|
</span>
|
|
132
132
|
<span className="flex min-w-0 flex-col text-start">
|
|
133
133
|
<span className="truncate">{p.name}</span>
|
|
134
|
-
<span className="truncate text-
|
|
134
|
+
<span className="truncate text-caption text-[var(--text-placeholder)]">
|
|
135
135
|
{p.blurb}
|
|
136
136
|
</span>
|
|
137
137
|
</span>
|
|
@@ -197,7 +197,7 @@ export function LanguageMenu({
|
|
|
197
197
|
{active ? (
|
|
198
198
|
<span
|
|
199
199
|
aria-hidden
|
|
200
|
-
className={`fi fi-${active.country} inline-block h-[
|
|
200
|
+
className={`fi fi-${active.country} inline-block h-[0.9375rem] w-5 shrink-0 rounded-sm shadow-[0_0_0_1px_rgba(0,0,0,0.08)]`}
|
|
201
201
|
/>
|
|
202
202
|
) : (
|
|
203
203
|
<Globe className="size-5" />
|
|
@@ -224,7 +224,7 @@ export function LanguageMenu({
|
|
|
224
224
|
<span className="flex items-center gap-2">
|
|
225
225
|
<span
|
|
226
226
|
aria-hidden
|
|
227
|
-
className={`fi fi-${lang.country} inline-block h-[
|
|
227
|
+
className={`fi fi-${lang.country} inline-block h-[0.9375rem] w-5 shrink-0 rounded-sm shadow-[0_0_0_1px_rgba(0,0,0,0.08)]`}
|
|
228
228
|
/>
|
|
229
229
|
{lang.label}
|
|
230
230
|
</span>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { contrast, oklchToRgb, parseHex, rgbToOklch, toHex } from "./color";
|
|
2
|
+
import type { TokenSet } from "./palette-presets";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The "More contrast" step on a token set (docs/text-size-harmonization.md §5, §10.5).
|
|
6
|
+
* INTERNAL — `applyTokenSet` and the contrast layer share it; it is not re-exported.
|
|
7
|
+
*
|
|
8
|
+
* The step happens HERE, in JS, because the tokens it changes are written inline on
|
|
9
|
+
* `<html>` by `applyTokenSet`: an inline value beats any `[data-contrast=more]` rule and
|
|
10
|
+
* any `@media (prefers-contrast: more)` block, so a stylesheet cannot do it (§10.5, the
|
|
11
|
+
* keksdose review). `--text-placeholder` is a CSS-derived token, and it is written inline
|
|
12
|
+
* here too rather than redefined per attribute in tokens.css.
|
|
13
|
+
*
|
|
14
|
+
* What moves (§5):
|
|
15
|
+
* - `--text-muted` becomes the set's `--text-secondary`;
|
|
16
|
+
* - `--text-secondary` moves halfway to `--text-primary`;
|
|
17
|
+
* - `--text-placeholder` becomes the set's `--text-muted` (4.6–5.5:1 on every shipped
|
|
18
|
+
* preset, where the CSS formula gives 2.4–2.8:1) — the order primary > secondary >
|
|
19
|
+
* muted > placeholder survives the step;
|
|
20
|
+
* - `--border` moves toward `--text-primary` until it clears 3:1 against the worst of the
|
|
21
|
+
* three surfaces (WCAG 1.4.11, a field's boundary), and never less far than
|
|
22
|
+
* `--border-strong`'s 45 % — so every border is at least as strong as a "strong" one was.
|
|
23
|
+
* `--border-strong` stays derived in tokens.css and grows with it.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** The custom properties a contrast step writes. */
|
|
27
|
+
export const CONTRAST_STEPPED_VARS = ["--text-secondary", "--text-muted", "--text-placeholder", "--border"] as const;
|
|
28
|
+
|
|
29
|
+
export interface ContrastStep {
|
|
30
|
+
textSecondary: string;
|
|
31
|
+
textMuted: string;
|
|
32
|
+
textPlaceholder: string;
|
|
33
|
+
border: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface Lab {
|
|
37
|
+
l: number;
|
|
38
|
+
a: number;
|
|
39
|
+
b: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function toLab(hex: string): Lab | null {
|
|
43
|
+
const rgb = parseHex(hex);
|
|
44
|
+
if (!rgb) return null;
|
|
45
|
+
const { l, c, h } = rgbToOklch(rgb);
|
|
46
|
+
const rad = (h * Math.PI) / 180;
|
|
47
|
+
return { l, a: c * Math.cos(rad), b: c * Math.sin(rad) };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** `color-mix(in oklab, a p, b)` as a hex — the same formula tokens.css's derived roles
|
|
51
|
+
* use, so a stepped value sits where the stylesheet would have put it. A colour that is
|
|
52
|
+
* not hex (a consumer's own token set) comes back unmixed. */
|
|
53
|
+
function mix(a: string, b: string, p: number): string {
|
|
54
|
+
const A = toLab(a);
|
|
55
|
+
const B = toLab(b);
|
|
56
|
+
if (!A || !B) return a;
|
|
57
|
+
const l = A.l * p + B.l * (1 - p);
|
|
58
|
+
const x = A.a * p + B.a * (1 - p);
|
|
59
|
+
const y = A.b * p + B.b * (1 - p);
|
|
60
|
+
const c = Math.hypot(x, y);
|
|
61
|
+
const h = c < 1e-6 ? 0 : ((Math.atan2(y, x) * 180) / Math.PI + 360) % 360;
|
|
62
|
+
return toHex(oklchToRgb({ l, c, h }));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** WCAG 1.4.11 for a component's boundary. */
|
|
66
|
+
const BORDER_TARGET = 3;
|
|
67
|
+
/** `--border-strong` in tokens.css: 55 % border, 45 % text. */
|
|
68
|
+
const BORDER_STRONG_SHARE = 0.45;
|
|
69
|
+
|
|
70
|
+
function worstContrast(color: string, t: TokenSet): number {
|
|
71
|
+
return Math.min(contrast(color, t.bgPage), contrast(color, t.bgSurface), contrast(color, t.bgSurface2));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function steppedBorder(t: TokenSet): string {
|
|
75
|
+
// The share of `--text-primary` in the border: at least --border-strong's, more until
|
|
76
|
+
// 3:1. Monotonic in the share (the mix walks from the border to the text, away from
|
|
77
|
+
// every surface), so a bisection finds the least that clears it.
|
|
78
|
+
let lo = BORDER_STRONG_SHARE;
|
|
79
|
+
if (worstContrast(mix(t.border, t.textPrimary, 1 - lo), t) >= BORDER_TARGET) return mix(t.border, t.textPrimary, 1 - lo);
|
|
80
|
+
let hi = 1;
|
|
81
|
+
for (let i = 0; i < 16; i++) {
|
|
82
|
+
const mid = (lo + hi) / 2;
|
|
83
|
+
if (worstContrast(mix(t.border, t.textPrimary, 1 - mid), t) >= BORDER_TARGET) hi = mid;
|
|
84
|
+
else lo = mid;
|
|
85
|
+
}
|
|
86
|
+
return mix(t.border, t.textPrimary, 1 - hi);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The four stepped values for `t`. Pure. */
|
|
90
|
+
export function contrastStep(t: TokenSet): ContrastStep {
|
|
91
|
+
return {
|
|
92
|
+
textSecondary: mix(t.textSecondary, t.textPrimary, 0.5),
|
|
93
|
+
textMuted: t.textSecondary,
|
|
94
|
+
textPlaceholder: t.textMuted,
|
|
95
|
+
border: steppedBorder(t),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* ── The token set last written to an element ───────────────────────────── */
|
|
100
|
+
|
|
101
|
+
const lastWritten = new WeakMap<HTMLElement, TokenSet>();
|
|
102
|
+
|
|
103
|
+
/** Remember the set `applyTokenSet` wrote, so a contrast change can re-apply it. */
|
|
104
|
+
export function rememberTokenSet(el: HTMLElement, t: TokenSet): void {
|
|
105
|
+
lastWritten.set(el, t);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The set `applyTokenSet` last wrote to `el`, or undefined when nothing did — an app
|
|
109
|
+
* without the palette layer, whose base tokens come from tokens.css. */
|
|
110
|
+
export function rememberedTokenSet(el: HTMLElement): TokenSet | undefined {
|
|
111
|
+
return lastWritten.get(el);
|
|
112
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { create } from "zustand";
|
|
3
|
+
import { persist } from "zustand/middleware";
|
|
4
|
+
import { logger } from "../lib/logger";
|
|
5
|
+
import { readStored } from "../lib/safe-storage";
|
|
6
|
+
import { useMediaQuery } from "../hooks/use-media-query";
|
|
7
|
+
import { CONTRAST_STEPPED_VARS, contrastStep, rememberedTokenSet } from "./contrast-tokens";
|
|
8
|
+
import { applyTokenSet, DEFAULT_PRESET } from "./palette-presets";
|
|
9
|
+
import type { PersistedAppearanceOptions } from "./text-size";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* "More contrast" (docs/text-size-harmonization.md §2.4, §5, §10.5, §10.6): a switch
|
|
13
|
+
* beside the text size, on by choice or — on "System" — when the device asks for more
|
|
14
|
+
* contrast (`prefers-contrast: more`). Any text size.
|
|
15
|
+
*
|
|
16
|
+
* In force it is `<html data-contrast="more">` (else `"standard"`), which does two things:
|
|
17
|
+
* - the inline colour tokens are re-written one step stronger (`applyTokenSet`'s
|
|
18
|
+
* contrast flag — see contrast-tokens.ts for what moves and why it is JS);
|
|
19
|
+
* - tokens.css raises `--focus-ring-width` from 2 to 3 px and turns on the `contrast:`
|
|
20
|
+
* variant for anything else a component wants to change.
|
|
21
|
+
*
|
|
22
|
+
* The names below are frozen like the text size's: server-kit's `CONTRAST_MODES`, the
|
|
23
|
+
* stored format, the attribute.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** The stored choices (§10.6): server-kit's `CONTRAST_MODES`. "system" IS a stored value
|
|
27
|
+
* — the PATCH rule refuses an explicit null, so null means only "never chosen". */
|
|
28
|
+
export const CONTRAST_MODES = ["system", "standard", "more"] as const;
|
|
29
|
+
|
|
30
|
+
export type ContrastMode = (typeof CONTRAST_MODES)[number];
|
|
31
|
+
|
|
32
|
+
/** What is in force on `<html data-contrast>`: a mode with "system" resolved. */
|
|
33
|
+
export type ContrastLevel = "standard" | "more";
|
|
34
|
+
|
|
35
|
+
const CONTRAST_ATTRIBUTE = "data-contrast";
|
|
36
|
+
const PREFERS_MORE_QUERY = "(prefers-contrast: more)";
|
|
37
|
+
|
|
38
|
+
export function isContrastMode(value: unknown): value is ContrastMode {
|
|
39
|
+
return value === "system" || value === "standard" || value === "more";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The mode in force: **this device's own choice → the account's → "system"** (§2.6).
|
|
43
|
+
* The one order the pre-paint call, the apply hook and `useAccountAppearance` share. */
|
|
44
|
+
export function resolveContrastMode(device: unknown, account?: unknown): ContrastMode {
|
|
45
|
+
if (isContrastMode(device)) return device;
|
|
46
|
+
if (isContrastMode(account)) return account;
|
|
47
|
+
return "system";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Whether the device asks for more contrast now. False without `matchMedia`. */
|
|
51
|
+
export function prefersMoreContrast(): boolean {
|
|
52
|
+
try {
|
|
53
|
+
return typeof window !== "undefined" && typeof window.matchMedia === "function"
|
|
54
|
+
? window.matchMedia(PREFERS_MORE_QUERY).matches
|
|
55
|
+
: false;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A mode to the level it puts on `<html>`; "system" asks the device (or takes
|
|
62
|
+
* `prefersMore`, when the caller already knows). */
|
|
63
|
+
export function contrastLevel(mode: ContrastMode, prefersMore: boolean = prefersMoreContrast()): ContrastLevel {
|
|
64
|
+
return mode === "more" || (mode === "system" && prefersMore) ? "more" : "standard";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The level `<html>` carries now; "standard" without one. */
|
|
68
|
+
export function readContrastLevel(): ContrastLevel {
|
|
69
|
+
if (typeof document === "undefined") return "standard";
|
|
70
|
+
return document.documentElement.getAttribute(CONTRAST_ATTRIBUTE) === "more" ? "more" : "standard";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Re-write the contrast-dependent tokens on `el` for its current `data-contrast`.
|
|
75
|
+
*
|
|
76
|
+
* With a palette layer (`applyTokenSet` has written a set here), that set is applied
|
|
77
|
+
* again and steps itself. Without one — an app whose base tokens come from tokens.css,
|
|
78
|
+
* like Kurvenschmiede's — the four stepped tokens are written over the stylesheet for
|
|
79
|
+
* the default preset in the current light/dark, or removed again.
|
|
80
|
+
*/
|
|
81
|
+
function reapplyContrastTokens(el: HTMLElement): void {
|
|
82
|
+
const written = rememberedTokenSet(el);
|
|
83
|
+
if (written) {
|
|
84
|
+
applyTokenSet(el, written);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
for (const name of CONTRAST_STEPPED_VARS) el.style.removeProperty(name);
|
|
88
|
+
if (el.getAttribute(CONTRAST_ATTRIBUTE) !== "more") return;
|
|
89
|
+
const step = contrastStep(DEFAULT_PRESET[el.classList.contains("dark") ? "dark" : "light"]);
|
|
90
|
+
el.style.setProperty("--text-secondary", step.textSecondary);
|
|
91
|
+
el.style.setProperty("--text-muted", step.textMuted);
|
|
92
|
+
el.style.setProperty("--text-placeholder", step.textPlaceholder);
|
|
93
|
+
el.style.setProperty("--border", step.border);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Put `level` on `<html>` and re-write the colour tokens for it. */
|
|
97
|
+
export function applyContrast(level: ContrastLevel): void {
|
|
98
|
+
if (typeof document === "undefined") return;
|
|
99
|
+
const root = document.documentElement;
|
|
100
|
+
root.setAttribute(CONTRAST_ATTRIBUTE, level);
|
|
101
|
+
reapplyContrastTokens(root);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* ── Storage: the frozen format, the pre-paint call ─────────────────────── */
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* What a contrast store persists under its key — FROZEN, like the text size's:
|
|
108
|
+
*
|
|
109
|
+
* {"state":{"contrast":"more"},"version":1}
|
|
110
|
+
*
|
|
111
|
+
* `contrast` is one of {@link CONTRAST_MODES}, or `null` / absent when this device never
|
|
112
|
+
* chose.
|
|
113
|
+
*/
|
|
114
|
+
export interface PersistedContrast {
|
|
115
|
+
state?: { contrast?: ContrastMode | null };
|
|
116
|
+
version?: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** This device's own choice under `storageKey`, or `null` when it never chose. */
|
|
120
|
+
export function readPersistedContrast(storageKey: string): ContrastMode | null {
|
|
121
|
+
try {
|
|
122
|
+
const blob = JSON.parse(readStored(storageKey) ?? "null") as PersistedContrast | null;
|
|
123
|
+
const mode = blob?.state?.contrast;
|
|
124
|
+
return isContrastMode(mode) ? mode : null;
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Pre-paint (§10.3, §10.5): this device's choice under `storageKey` — else the account's
|
|
132
|
+
* last known value, else "system" — resolved against `prefers-contrast` and applied
|
|
133
|
+
* BEFORE `createRoot`. Call it next to `applyPersistedPalette`, in EITHER order — the
|
|
134
|
+
* palette steps itself when `data-contrast` is already set, and this re-applies the
|
|
135
|
+
* palette's set when it is not; no app has to put one first (0.32.1, keksdose's 0.32
|
|
136
|
+
* report asked which). Returns the level applied.
|
|
137
|
+
*/
|
|
138
|
+
export function applyPersistedContrast(storageKey: string, options: PersistedAppearanceOptions = {}): ContrastLevel {
|
|
139
|
+
let account: unknown;
|
|
140
|
+
try {
|
|
141
|
+
account = options.account?.();
|
|
142
|
+
} catch {
|
|
143
|
+
account = undefined;
|
|
144
|
+
}
|
|
145
|
+
const level = contrastLevel(resolveContrastMode(readPersistedContrast(storageKey), account));
|
|
146
|
+
applyContrast(level);
|
|
147
|
+
return level;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* ── The store ──────────────────────────────────────────────────────────── */
|
|
151
|
+
|
|
152
|
+
export interface ContrastState {
|
|
153
|
+
/** This device's own choice (persisted), or `null` when it never chose — then the
|
|
154
|
+
* account's value applies, else "system". */
|
|
155
|
+
contrast: ContrastMode | null;
|
|
156
|
+
/** Store this device's choice; `null` forgets it. */
|
|
157
|
+
setContrast: (contrast: ContrastMode | null) => void;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface ApplyContrastOptions {
|
|
161
|
+
/** The account's value (`/auth/me`'s `contrast`): it applies while this device has no
|
|
162
|
+
* choice of its own. */
|
|
163
|
+
account?: string | null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Build a contrast store bound to a consumer-supplied localStorage key, shaped like
|
|
168
|
+
* `createThemeStore` and {@link createTextSizeStore} (§6). It holds the DEVICE's choice;
|
|
169
|
+
* the account's value comes in through `useApplyContrast({ account })`.
|
|
170
|
+
*
|
|
171
|
+
* `useApplyContrast` — mount it once near the root, beside `useApplyPalette`:
|
|
172
|
+
* - puts the level on `<html>` and steps the tokens;
|
|
173
|
+
* - on "System", follows `prefers-contrast: more` live (§10.5);
|
|
174
|
+
* - without a palette layer, re-writes the stepped tokens when the light/dark class
|
|
175
|
+
* changes, since they differ per theme.
|
|
176
|
+
*
|
|
177
|
+
* The persisted format is {@link PersistedContrast}, and frozen.
|
|
178
|
+
*/
|
|
179
|
+
export function createContrastStore(storageKey: string) {
|
|
180
|
+
const useContrastStore = create<ContrastState>()(
|
|
181
|
+
logger(
|
|
182
|
+
persist(
|
|
183
|
+
(set) => ({
|
|
184
|
+
contrast: null,
|
|
185
|
+
setContrast: (contrast) => set({ contrast: isContrastMode(contrast) ? contrast : null }),
|
|
186
|
+
}),
|
|
187
|
+
{
|
|
188
|
+
name: storageKey,
|
|
189
|
+
version: 1,
|
|
190
|
+
partialize: (s) => ({ contrast: s.contrast }),
|
|
191
|
+
merge: (persisted, current) => {
|
|
192
|
+
const value = (persisted as { contrast?: unknown } | null)?.contrast;
|
|
193
|
+
return { ...current, contrast: isContrastMode(value) ? value : null };
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
),
|
|
197
|
+
"contrast",
|
|
198
|
+
),
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
function useApplyContrast({ account }: ApplyContrastOptions = {}): ContrastLevel {
|
|
202
|
+
const device = useContrastStore((s) => s.contrast);
|
|
203
|
+
// Subscribed whatever the mode (hooks cannot be skipped); only "system" reads it.
|
|
204
|
+
const prefersMore = useMediaQuery(PREFERS_MORE_QUERY, false);
|
|
205
|
+
const level = contrastLevel(resolveContrastMode(device, account), prefersMore);
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
applyContrast(level);
|
|
208
|
+
}, [level]);
|
|
209
|
+
// Without a palette layer the stepped tokens are per theme: follow `.dark`.
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
if (typeof MutationObserver === "undefined") return;
|
|
212
|
+
const root = document.documentElement;
|
|
213
|
+
const observer = new MutationObserver(() => {
|
|
214
|
+
if (!rememberedTokenSet(root)) reapplyContrastTokens(root);
|
|
215
|
+
});
|
|
216
|
+
observer.observe(root, { attributes: true, attributeFilter: ["class"] });
|
|
217
|
+
return () => observer.disconnect();
|
|
218
|
+
}, []);
|
|
219
|
+
return level;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { useContrastStore, useApplyContrast };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export type ContrastStore = ReturnType<typeof createContrastStore>;
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// (the default preset here mirrors those exactly).
|
|
16
16
|
|
|
17
17
|
import type { HeatStops } from "./chart-palette";
|
|
18
|
+
import { contrastStep, rememberTokenSet } from "./contrast-tokens";
|
|
18
19
|
|
|
19
20
|
export interface TokenSet {
|
|
20
21
|
// Backgrounds & chrome
|
|
@@ -740,15 +741,34 @@ export function presetById(id: string): PalettePreset {
|
|
|
740
741
|
* have been silent and only a dead function would have noticed. The list is gone;
|
|
741
742
|
* the test beside this asserts instead that every field of a `TokenSet` reaches the
|
|
742
743
|
* element, which is what that list was standing in for.
|
|
744
|
+
*
|
|
745
|
+
* **More contrast (0.32, docs/text-size-harmonization.md §5, §10.5).** With `contrast`
|
|
746
|
+
* the text and border tokens are written one step stronger, and `--text-placeholder` —
|
|
747
|
+
* otherwise derived in tokens.css — is written inline too; without it that inline
|
|
748
|
+
* placeholder is removed again, so the stylesheet's formula applies. The step is made
|
|
749
|
+
* HERE because these tokens are inline: no `[data-contrast=more]` rule could beat them.
|
|
750
|
+
*
|
|
751
|
+
* `contrast` defaults to the element's own `data-contrast="more"`, which the contrast
|
|
752
|
+
* layer (`applyContrast`, `useApplyContrast`) sets first. That is how the palette and the
|
|
753
|
+
* contrast cooperate without knowing each other: a palette change re-applies through
|
|
754
|
+
* here and keeps the contrast, and a contrast change re-applies the set remembered here.
|
|
743
755
|
*/
|
|
744
|
-
export function applyTokenSet(
|
|
756
|
+
export function applyTokenSet(
|
|
757
|
+
el: HTMLElement,
|
|
758
|
+
t: TokenSet,
|
|
759
|
+
contrast: boolean = el.getAttribute("data-contrast") === "more",
|
|
760
|
+
): void {
|
|
761
|
+
rememberTokenSet(el, t);
|
|
762
|
+
const step = contrast ? contrastStep(t) : null;
|
|
745
763
|
el.style.setProperty("--bg-page", t.bgPage);
|
|
746
764
|
el.style.setProperty("--bg-surface", t.bgSurface);
|
|
747
765
|
el.style.setProperty("--bg-surface-2", t.bgSurface2);
|
|
748
|
-
el.style.setProperty("--border", t.border);
|
|
766
|
+
el.style.setProperty("--border", step?.border ?? t.border);
|
|
749
767
|
el.style.setProperty("--text-primary", t.textPrimary);
|
|
750
|
-
el.style.setProperty("--text-secondary", t.textSecondary);
|
|
751
|
-
el.style.setProperty("--text-muted", t.textMuted);
|
|
768
|
+
el.style.setProperty("--text-secondary", step?.textSecondary ?? t.textSecondary);
|
|
769
|
+
el.style.setProperty("--text-muted", step?.textMuted ?? t.textMuted);
|
|
770
|
+
if (step) el.style.setProperty("--text-placeholder", step.textPlaceholder);
|
|
771
|
+
else el.style.removeProperty("--text-placeholder");
|
|
752
772
|
el.style.setProperty("--brand", t.brand);
|
|
753
773
|
el.style.setProperty("--brand-hover", t.brandHover);
|
|
754
774
|
el.style.setProperty("--brand-contrast", t.brandContrast);
|
|
@@ -60,7 +60,12 @@ export function createPaletteStore(
|
|
|
60
60
|
/** Apply the active preset's tokens to <html> as inline CSS custom properties,
|
|
61
61
|
* re-applying when the preset or the light/dark theme changes. Tailwind v4
|
|
62
62
|
* strips a bare root token block from the bundle, so this inline write from
|
|
63
|
-
* the TS source of truth is what actually drives the colours.
|
|
63
|
+
* the TS source of truth is what actually drives the colours.
|
|
64
|
+
*
|
|
65
|
+
* 0.32: keeps More contrast. `applyTokenSet` reads `<html data-contrast>`, which the
|
|
66
|
+
* contrast store sets, and steps the tokens itself; the contrast store re-applies
|
|
67
|
+
* the set written here when the contrast changes (docs/text-size-harmonization.md
|
|
68
|
+
* §10.5). Neither store needs the other. */
|
|
64
69
|
function useApplyPalette(): void {
|
|
65
70
|
const id = usePalette((s) => s.id);
|
|
66
71
|
const mode = useTheme((s) => s.mode);
|