@eifi1/ui-kit 0.32.1 → 0.33.0
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 +16 -8
- package/dist/account/delete-account-setting.d.ts +10 -10
- package/dist/account/delete-account-setting.js +2 -1
- package/dist/account/delete-account-setting.js.map +1 -1
- package/dist/account/email-change-setting.d.ts +5 -0
- package/dist/account/email-change-setting.js +4 -3
- package/dist/account/email-change-setting.js.map +1 -1
- package/dist/account/sessions-setting.d.ts +4 -0
- package/dist/account/sessions-setting.js +3 -2
- package/dist/account/sessions-setting.js.map +1 -1
- package/dist/admin/admin-action-confirm.d.ts +10 -10
- package/dist/admin/admin-action-confirm.js.map +1 -1
- package/dist/admin/invitations-panel.d.ts +10 -10
- package/dist/admin/invitations-panel.js +4 -3
- package/dist/admin/invitations-panel.js.map +1 -1
- package/dist/admin/plan-change.d.ts +100 -0
- package/dist/admin/plan-change.js +208 -0
- package/dist/admin/plan-change.js.map +1 -0
- package/dist/admin/reviewer-scope-editor.d.ts +10 -10
- package/dist/admin/reviewer-scope-editor.js.map +1 -1
- package/dist/admin/role-select.d.ts +7 -7
- package/dist/admin/role-select.js.map +1 -1
- package/dist/admin/transfer-ownership-dialog.d.ts +10 -10
- package/dist/admin/user-roster.d.ts +7 -7
- package/dist/admin/user-roster.js +2 -2
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/auth/auth-errors.d.ts +13 -2
- package/dist/auth/auth-errors.js +5 -1
- package/dist/auth/auth-errors.js.map +1 -1
- package/dist/auth/complete-name-dialog.d.ts +4 -1
- package/dist/auth/complete-name-dialog.js +2 -1
- package/dist/auth/complete-name-dialog.js.map +1 -1
- package/dist/auth/register-form.d.ts +10 -10
- package/dist/billing/billing-banner.d.ts +13 -11
- package/dist/billing/billing-banner.js.map +1 -1
- package/dist/billing/billing-labels.d.ts +10 -10
- package/dist/billing/billing-labels.js +10 -1
- package/dist/billing/billing-labels.js.map +1 -1
- package/dist/billing/billing-lock.d.ts +39 -12
- package/dist/billing/billing-lock.js +9 -3
- package/dist/billing/billing-lock.js.map +1 -1
- package/dist/billing/billing-overview.d.ts +154 -0
- package/dist/billing/billing-overview.js +2 -0
- package/dist/billing/billing-overview.js.map +1 -0
- package/dist/billing/billing-standing.d.ts +10 -10
- package/dist/billing/checkout-processing.d.ts +217 -0
- package/dist/billing/checkout-processing.js +198 -0
- package/dist/billing/checkout-processing.js.map +1 -0
- package/dist/billing/checkout-return.d.ts +51 -0
- package/dist/billing/checkout-return.js +43 -0
- package/dist/billing/checkout-return.js.map +1 -0
- package/dist/billing/pay-page.d.ts +210 -0
- package/dist/billing/pay-page.js +113 -0
- package/dist/billing/pay-page.js.map +1 -0
- package/dist/billing/plan-card.d.ts +10 -10
- package/dist/billing/plan-limit-notice.d.ts +10 -10
- package/dist/billing/plan-limit-toast.d.ts +143 -0
- package/dist/billing/plan-limit-toast.js +40 -0
- package/dist/billing/plan-limit-toast.js.map +1 -0
- package/dist/billing/subscription-actions.d.ts +38 -15
- package/dist/billing/subscription-actions.js +9 -2
- package/dist/billing/subscription-actions.js.map +1 -1
- package/dist/billing/subscription-status-chip.d.ts +9 -9
- package/dist/bin/eifi1-pay-page.mjs +166 -0
- package/dist/chart.d.ts +10 -10
- package/dist/components/account-chips.d.ts +6 -1
- package/dist/components/account-settings.d.ts +4 -1
- package/dist/components/account-settings.js +2 -1
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/amount-input.d.ts +10 -10
- package/dist/components/autocomplete.d.ts +2 -1
- package/dist/components/autocomplete.js +3 -1
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/button-group.d.ts +10 -10
- package/dist/components/button-group.js +1 -1
- package/dist/components/button-group.js.map +1 -1
- package/dist/components/calculator.d.ts +10 -10
- package/dist/components/calculator.js +3 -3
- package/dist/components/calculator.js.map +1 -1
- package/dist/components/checkbox-group.d.ts +2 -1
- package/dist/components/checkbox-group.js.map +1 -1
- package/dist/components/checkbox.d.ts +2 -1
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/chip.d.ts +2 -1
- package/dist/components/chip.js +19 -17
- package/dist/components/chip.js.map +1 -1
- package/dist/components/choice-card.d.ts +3 -1
- package/dist/components/choice-card.js +1 -1
- package/dist/components/choice-card.js.map +1 -1
- package/dist/components/combobox.d.ts +4 -3
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +10 -10
- package/dist/components/confirm-dialog.js +6 -3
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/copy-button.d.ts +11 -11
- package/dist/components/country-select.d.ts +2 -1
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +10 -10
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-cells.d.ts +12 -1
- package/dist/components/data-table-filter-popover.d.ts +12 -1
- package/dist/components/data-table-filters.d.ts +12 -1
- package/dist/components/data-table-labels.d.ts +12 -1
- package/dist/components/data-table-pagination.d.ts +12 -1
- package/dist/components/data-table.d.ts +12 -1
- package/dist/components/data-table.js +3 -3
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.js +6 -4
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +3 -2
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +10 -10
- package/dist/components/field-parts.d.ts +32 -4
- package/dist/components/field-parts.js +54 -19
- package/dist/components/field-parts.js.map +1 -1
- package/dist/components/file-button.d.ts +10 -10
- package/dist/components/file-button.js +3 -2
- package/dist/components/file-button.js.map +1 -1
- package/dist/components/file-dropzone.d.ts +11 -11
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/floating-panel.js +5 -5
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +9 -9
- package/dist/components/form-actions.js +3 -3
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/iban-input.d.ts +10 -10
- package/dist/components/inline-edit-field.d.ts +2 -1
- package/dist/components/inline-edit-field.js +6 -6
- package/dist/components/inline-edit-field.js.map +1 -1
- package/dist/components/language-select.d.ts +10 -10
- package/dist/components/legal-page.d.ts +10 -10
- package/dist/components/legal.d.ts +10 -10
- package/dist/components/list.d.ts +2 -1
- package/dist/components/list.js +4 -5
- package/dist/components/list.js.map +1 -1
- package/dist/components/money-field.d.ts +10 -10
- package/dist/components/month-picker.js +54 -44
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.d.ts +3 -2
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/number-field.d.ts +10 -10
- package/dist/components/number-input.d.ts +10 -10
- package/dist/components/numpad-sheet.d.ts +10 -10
- package/dist/components/numpad-sheet.js +2 -2
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/page-header.js +6 -1
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/phone-input.d.ts +10 -10
- package/dist/components/popover.d.ts +1 -1
- package/dist/components/popover.js +2 -0
- package/dist/components/popover.js.map +1 -1
- package/dist/components/row-actions.d.ts +5 -5
- package/dist/components/row-actions.js +130 -38
- package/dist/components/row-actions.js.map +1 -1
- package/dist/components/series-chart.d.ts +10 -10
- package/dist/components/settings-fields.d.ts +10 -10
- package/dist/components/share-card.d.ts +7 -7
- package/dist/components/share-card.js +26 -10
- package/dist/components/share-card.js.map +1 -1
- package/dist/components/status-dot.d.ts +1 -0
- package/dist/components/status-dot.js +2 -2
- package/dist/components/status-dot.js.map +1 -1
- package/dist/components/swatch-picker.d.ts +2 -1
- package/dist/components/swatch-picker.js.map +1 -1
- package/dist/components/swipeable-row.d.ts +88 -17
- package/dist/components/swipeable-row.js +46 -9
- package/dist/components/swipeable-row.js.map +1 -1
- package/dist/components/switch.d.ts +2 -1
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table.d.ts +2 -1
- package/dist/components/table.js +6 -0
- package/dist/components/table.js.map +1 -1
- package/dist/components/text-link.d.ts +9 -9
- package/dist/components/time-input.d.ts +10 -10
- package/dist/components/toggle-group.d.ts +2 -1
- package/dist/components/toggle-group.js +1 -1
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/translation-review-editor.d.ts +4 -0
- package/dist/components/translation-review-editor.js +4 -3
- package/dist/components/translation-review-editor.js.map +1 -1
- package/dist/components/translation-review-parts.d.ts +1 -0
- package/dist/components/translation-review.d.ts +1 -0
- package/dist/components/translation-review.js +15 -18
- package/dist/components/translation-review.js.map +1 -1
- package/dist/components/ui.d.ts +9 -9
- package/dist/components/ui.js +48 -28
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +12 -1
- package/dist/components/user-avatar.d.ts +1 -0
- package/dist/components/write-lock.d.ts +101 -13
- package/dist/components/write-lock.js +37 -13
- package/dist/components/write-lock.js.map +1 -1
- package/dist/{data-table-labels-kpAAVi6g.d.ts → data-table-labels-BJ5JE_X2.d.ts} +17 -0
- package/dist/data-table.d.ts +12 -1
- package/dist/{delete-account-setting-FLyJf-kZ.d.ts → delete-account-setting-DiPX4Vue.d.ts} +365 -47
- package/dist/feedback/feedback-attachment.d.ts +10 -10
- package/dist/feedback/feedback-attachment.js +7 -7
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-context.d.ts +10 -10
- package/dist/feedback/feedback-crash.d.ts +10 -10
- package/dist/feedback/feedback-dialog.d.ts +10 -10
- package/dist/feedback/feedback-inbox.d.ts +9 -9
- package/dist/feedback/feedback-inbox.js +7 -3
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback/feedback-labels.d.ts +10 -10
- package/dist/feedback/feedback-menu.d.ts +3 -3
- package/dist/feedback/feedback-record.d.ts +10 -10
- package/dist/feedback/feedback-row-detail.d.ts +10 -10
- package/dist/feedback/feedback-row-detail.js.map +1 -1
- package/dist/feedback/feedback-status-undo.d.ts +10 -10
- package/dist/feedback/feedback-submit.d.ts +10 -10
- package/dist/feedback/feedback-swipe.d.ts +10 -10
- package/dist/feedback/feedback-swipe.js +3 -6
- package/dist/feedback/feedback-swipe.js.map +1 -1
- package/dist/feedback/feedback-table.d.ts +10 -10
- package/dist/feedback/feedback-table.js.map +1 -1
- package/dist/feedback/feedback-thread.d.ts +10 -10
- package/dist/feedback.d.ts +10 -10
- package/dist/hooks/use-file-drop.d.ts +10 -10
- package/dist/i18n/defaults.d.ts +10 -10
- package/dist/i18n/defaults.js +2 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +10 -10
- package/dist/i18n/german.js +34 -1
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +4 -4
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +10 -10
- package/dist/i18n/locales/de-CH.d.ts +10 -10
- package/dist/i18n/locales/en.d.ts +10 -10
- package/dist/i18n/locales/es.d.ts +10 -10
- package/dist/i18n/locales/es.js +34 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +10 -10
- package/dist/i18n/locales/fr.js +35 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +10 -10
- package/dist/i18n/locales/hu.js +36 -1
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +10 -10
- package/dist/i18n/locales/it.js +34 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +10 -10
- package/dist/i18n/locales/zh.js +34 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +10 -10
- package/dist/i18n/review.js +8 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +40 -2
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-actions.d.ts +10 -10
- package/dist/landing/landing-sections.d.ts +10 -10
- package/dist/landing/public-header.d.ts +10 -10
- package/dist/landing/routing.d.ts +10 -10
- package/dist/pay/.well-known/apple-developer-merchantid-domain-association +1 -0
- package/dist/pay/index.html +30 -0
- package/dist/pay/pay.css +126 -0
- package/dist/pay/pay.js +198 -0
- package/dist/rhf/fields.d.ts +10 -10
- package/dist/rhf/form.d.ts +10 -10
- package/dist/rhf.d.ts +10 -10
- package/dist/settings/settings-section.d.ts +10 -10
- package/dist/shell/app-shell.d.ts +9 -9
- package/dist/shell/app-shell.js +2 -1
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/top-bar-brand.d.ts +10 -10
- package/dist/shell/topbar-action-menu.d.ts +1 -0
- package/dist/shell.d.ts +10 -10
- package/dist/testing/css-variables.d.ts +74 -0
- package/dist/testing/css-variables.generated.d.ts +7 -0
- package/dist/testing/css-variables.generated.js +8 -0
- package/dist/testing/css-variables.generated.js.map +1 -0
- package/dist/testing/css-variables.js +93 -0
- package/dist/testing/css-variables.js.map +1 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +12 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/theme/contrast-tokens.d.ts +44 -1
- package/dist/theme/contrast-tokens.js +21 -0
- package/dist/theme/contrast-tokens.js.map +1 -1
- package/dist/theme/contrast.d.ts +2 -2
- package/dist/theme/palette-derive.js +60 -11
- package/dist/theme/palette-derive.js.map +1 -1
- package/dist/theme/palette-presets.js +45 -43
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +11 -11
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard.d.ts +10 -10
- package/package.json +10 -2
- package/src/account/delete-account-setting.tsx +6 -1
- package/src/account/email-change-setting.tsx +9 -3
- package/src/account/sessions-setting.tsx +7 -2
- package/src/admin/admin-action-confirm.tsx +2 -1
- package/src/admin/invitations-panel.tsx +9 -3
- package/src/admin/plan-change.tsx +459 -0
- package/src/admin/reviewer-scope-editor.tsx +2 -1
- package/src/admin/role-select.tsx +2 -1
- package/src/admin/user-roster.tsx +5 -3
- package/src/auth/auth-errors.ts +25 -2
- package/src/auth/complete-name-dialog.tsx +6 -2
- package/src/billing/billing-banner.tsx +3 -1
- package/src/billing/billing-labels.ts +45 -0
- package/src/billing/billing-lock.ts +36 -3
- package/src/billing/billing-overview.ts +58 -0
- package/src/billing/checkout-processing.ts +391 -0
- package/src/billing/checkout-return.ts +90 -0
- package/src/billing/pay-page.ts +242 -0
- package/src/billing/plan-limit-toast.tsx +91 -0
- package/src/billing/subscription-actions.tsx +41 -8
- package/src/components/account-settings.tsx +6 -2
- package/src/components/autocomplete.tsx +5 -2
- package/src/components/button-group.tsx +4 -1
- package/src/components/calculator.tsx +10 -6
- package/src/components/checkbox-group.tsx +2 -2
- package/src/components/checkbox.tsx +2 -1
- package/src/components/chip.tsx +37 -32
- package/src/components/choice-card.tsx +4 -2
- package/src/components/combobox.tsx +4 -4
- package/src/components/confirm-dialog.tsx +13 -6
- package/src/components/country-select.tsx +2 -2
- package/src/components/danger-confirm.tsx +2 -2
- package/src/components/data-table.tsx +20 -9
- package/src/components/date-picker.tsx +10 -7
- package/src/components/entity-combobox.tsx +3 -3
- package/src/components/field-parts.tsx +61 -14
- package/src/components/file-button.tsx +5 -4
- package/src/components/file-dropzone.tsx +2 -2
- package/src/components/floating-panel.tsx +7 -5
- package/src/components/form-actions.tsx +12 -5
- package/src/components/inline-edit-field.tsx +13 -13
- package/src/components/list.tsx +10 -9
- package/src/components/month-picker.tsx +8 -2
- package/src/components/multi-entity-combobox.tsx +3 -3
- package/src/components/numpad-sheet.tsx +8 -4
- package/src/components/page-header.tsx +15 -1
- package/src/components/popover.tsx +4 -0
- package/src/components/row-actions.tsx +201 -50
- package/src/components/share-card.tsx +67 -13
- package/src/components/status-dot.tsx +7 -7
- package/src/components/swatch-picker.tsx +2 -2
- package/src/components/swipeable-row.tsx +138 -27
- package/src/components/switch.tsx +2 -1
- package/src/components/table.tsx +8 -1
- package/src/components/toggle-group.tsx +5 -3
- package/src/components/translation-review-editor.tsx +8 -3
- package/src/components/translation-review.tsx +18 -22
- package/src/components/ui.tsx +98 -44
- package/src/components/write-lock.tsx +150 -24
- package/src/feedback/feedback-attachment.tsx +13 -9
- package/src/feedback/feedback-inbox.tsx +10 -6
- package/src/feedback/feedback-row-detail.tsx +3 -2
- package/src/feedback/feedback-swipe.tsx +6 -11
- package/src/feedback/feedback-table.tsx +2 -1
- package/src/i18n/defaults.ts +2 -0
- package/src/i18n/german.ts +38 -0
- package/src/i18n/kit-labels.tsx +4 -0
- package/src/i18n/locales/es.ts +34 -0
- package/src/i18n/locales/fr.ts +39 -0
- package/src/i18n/locales/hu.ts +37 -0
- package/src/i18n/locales/it.ts +35 -0
- package/src/i18n/locales/zh.ts +33 -0
- package/src/i18n/review.ts +7 -0
- package/src/index.ts +48 -4
- package/src/shell/app-shell.tsx +11 -6
- package/src/testing/css-variables.generated.ts +112 -0
- package/src/testing/css-variables.ts +191 -0
- package/src/testing/index.ts +13 -0
- package/src/theme/contrast-tokens.ts +53 -2
- package/src/theme/palette-derive.ts +87 -10
- package/src/theme/palette-presets.ts +45 -43
- package/src/wizard/stepper-nav.tsx +2 -1
- package/tokens.css +306 -46
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// GENERATED by scripts/gen-kit-css-variables.mjs — do not edit by hand. Re-run it after
|
|
2
|
+
// changing tokens.css or upgrading Tailwind; src/__tests__/token-vars-declared.test.ts
|
|
3
|
+
// fails while this is stale.
|
|
4
|
+
|
|
5
|
+
/** Every custom property tokens.css declares, `@theme inline` names and `--app-nav-h`
|
|
6
|
+
* left out (100 names). */
|
|
7
|
+
export const KIT_NAMES =
|
|
8
|
+
"--bg-active --bg-hover --bg-inverse --bg-page --bg-surface --bg-surface-2 --border " +
|
|
9
|
+
"--border-strong --brand --brand-bg --brand-bg-hover --brand-contrast --brand-hover --brand-muted " +
|
|
10
|
+
"--breakpoint-3xl --chart-1 --chart-2 --chart-3 --chart-4 --chart-5 --chart-6 --chart-7 --chart-8 " +
|
|
11
|
+
"--chart-9 --danger --danger-bg --danger-border --danger-border-strong --danger-contrast " +
|
|
12
|
+
"--danger-hover --flyout-from --focus-ring-width --hue-blue --hue-blue-bg --hue-blue-border " +
|
|
13
|
+
"--hue-blue-contrast --hue-indigo --hue-indigo-bg --hue-indigo-border --hue-indigo-contrast " +
|
|
14
|
+
"--hue-orange --hue-orange-bg --hue-orange-border --hue-orange-contrast --hue-purple " +
|
|
15
|
+
"--hue-purple-bg --hue-purple-border --hue-purple-contrast --hue-teal --hue-teal-bg " +
|
|
16
|
+
"--hue-teal-border --hue-teal-contrast --info --info-bg --info-border --info-contrast --media-ink " +
|
|
17
|
+
"--media-scrim --media-scrim-hover --money-expense --money-expense-contrast --money-income " +
|
|
18
|
+
"--money-income-contrast --money-net --money-net-contrast --money-neutral " +
|
|
19
|
+
"--money-neutral-contrast --neutral --neutral-contrast --status-edited --status-error " +
|
|
20
|
+
"--status-pending --status-synced --success --success-bg --success-border --success-contrast " +
|
|
21
|
+
"--text-caption --text-inverse --text-micro --text-muted --text-placeholder --text-primary " +
|
|
22
|
+
"--text-secondary --toast-action-bg --toast-action-fg --toast-danger-bg --toast-danger-border " +
|
|
23
|
+
"--toast-info-bg --toast-info-border --toast-success-bg --toast-success-border --toast-warning-bg " +
|
|
24
|
+
"--toast-warning-border --warning --warning-bg --warning-border --warning-border-strong " +
|
|
25
|
+
"--warning-contrast --z-toast";
|
|
26
|
+
|
|
27
|
+
/** The theme variables of Tailwind 4.3.3's `theme.css` (413 names). */
|
|
28
|
+
export const TAILWIND_THEME_NAMES =
|
|
29
|
+
"--animate-bounce --animate-ping --animate-pulse --animate-spin --aspect-video --blur-2xl " +
|
|
30
|
+
"--blur-3xl --blur-lg --blur-md --blur-sm --blur-xl --blur-xs --breakpoint-2xl --breakpoint-lg " +
|
|
31
|
+
"--breakpoint-md --breakpoint-sm --breakpoint-xl --color-amber-100 --color-amber-200 " +
|
|
32
|
+
"--color-amber-300 --color-amber-400 --color-amber-50 --color-amber-500 --color-amber-600 " +
|
|
33
|
+
"--color-amber-700 --color-amber-800 --color-amber-900 --color-amber-950 --color-black " +
|
|
34
|
+
"--color-blue-100 --color-blue-200 --color-blue-300 --color-blue-400 --color-blue-50 " +
|
|
35
|
+
"--color-blue-500 --color-blue-600 --color-blue-700 --color-blue-800 --color-blue-900 " +
|
|
36
|
+
"--color-blue-950 --color-cyan-100 --color-cyan-200 --color-cyan-300 --color-cyan-400 " +
|
|
37
|
+
"--color-cyan-50 --color-cyan-500 --color-cyan-600 --color-cyan-700 --color-cyan-800 " +
|
|
38
|
+
"--color-cyan-900 --color-cyan-950 --color-emerald-100 --color-emerald-200 --color-emerald-300 " +
|
|
39
|
+
"--color-emerald-400 --color-emerald-50 --color-emerald-500 --color-emerald-600 " +
|
|
40
|
+
"--color-emerald-700 --color-emerald-800 --color-emerald-900 --color-emerald-950 " +
|
|
41
|
+
"--color-fuchsia-100 --color-fuchsia-200 --color-fuchsia-300 --color-fuchsia-400 " +
|
|
42
|
+
"--color-fuchsia-50 --color-fuchsia-500 --color-fuchsia-600 --color-fuchsia-700 " +
|
|
43
|
+
"--color-fuchsia-800 --color-fuchsia-900 --color-fuchsia-950 --color-gray-100 --color-gray-200 " +
|
|
44
|
+
"--color-gray-300 --color-gray-400 --color-gray-50 --color-gray-500 --color-gray-600 " +
|
|
45
|
+
"--color-gray-700 --color-gray-800 --color-gray-900 --color-gray-950 --color-green-100 " +
|
|
46
|
+
"--color-green-200 --color-green-300 --color-green-400 --color-green-50 --color-green-500 " +
|
|
47
|
+
"--color-green-600 --color-green-700 --color-green-800 --color-green-900 --color-green-950 " +
|
|
48
|
+
"--color-indigo-100 --color-indigo-200 --color-indigo-300 --color-indigo-400 --color-indigo-50 " +
|
|
49
|
+
"--color-indigo-500 --color-indigo-600 --color-indigo-700 --color-indigo-800 --color-indigo-900 " +
|
|
50
|
+
"--color-indigo-950 --color-lime-100 --color-lime-200 --color-lime-300 --color-lime-400 " +
|
|
51
|
+
"--color-lime-50 --color-lime-500 --color-lime-600 --color-lime-700 --color-lime-800 " +
|
|
52
|
+
"--color-lime-900 --color-lime-950 --color-mauve-100 --color-mauve-200 --color-mauve-300 " +
|
|
53
|
+
"--color-mauve-400 --color-mauve-50 --color-mauve-500 --color-mauve-600 --color-mauve-700 " +
|
|
54
|
+
"--color-mauve-800 --color-mauve-900 --color-mauve-950 --color-mist-100 --color-mist-200 " +
|
|
55
|
+
"--color-mist-300 --color-mist-400 --color-mist-50 --color-mist-500 --color-mist-600 " +
|
|
56
|
+
"--color-mist-700 --color-mist-800 --color-mist-900 --color-mist-950 --color-neutral-100 " +
|
|
57
|
+
"--color-neutral-200 --color-neutral-300 --color-neutral-400 --color-neutral-50 " +
|
|
58
|
+
"--color-neutral-500 --color-neutral-600 --color-neutral-700 --color-neutral-800 " +
|
|
59
|
+
"--color-neutral-900 --color-neutral-950 --color-olive-100 --color-olive-200 --color-olive-300 " +
|
|
60
|
+
"--color-olive-400 --color-olive-50 --color-olive-500 --color-olive-600 --color-olive-700 " +
|
|
61
|
+
"--color-olive-800 --color-olive-900 --color-olive-950 --color-orange-100 --color-orange-200 " +
|
|
62
|
+
"--color-orange-300 --color-orange-400 --color-orange-50 --color-orange-500 --color-orange-600 " +
|
|
63
|
+
"--color-orange-700 --color-orange-800 --color-orange-900 --color-orange-950 --color-pink-100 " +
|
|
64
|
+
"--color-pink-200 --color-pink-300 --color-pink-400 --color-pink-50 --color-pink-500 " +
|
|
65
|
+
"--color-pink-600 --color-pink-700 --color-pink-800 --color-pink-900 --color-pink-950 " +
|
|
66
|
+
"--color-purple-100 --color-purple-200 --color-purple-300 --color-purple-400 --color-purple-50 " +
|
|
67
|
+
"--color-purple-500 --color-purple-600 --color-purple-700 --color-purple-800 --color-purple-900 " +
|
|
68
|
+
"--color-purple-950 --color-red-100 --color-red-200 --color-red-300 --color-red-400 " +
|
|
69
|
+
"--color-red-50 --color-red-500 --color-red-600 --color-red-700 --color-red-800 --color-red-900 " +
|
|
70
|
+
"--color-red-950 --color-rose-100 --color-rose-200 --color-rose-300 --color-rose-400 " +
|
|
71
|
+
"--color-rose-50 --color-rose-500 --color-rose-600 --color-rose-700 --color-rose-800 " +
|
|
72
|
+
"--color-rose-900 --color-rose-950 --color-sky-100 --color-sky-200 --color-sky-300 " +
|
|
73
|
+
"--color-sky-400 --color-sky-50 --color-sky-500 --color-sky-600 --color-sky-700 --color-sky-800 " +
|
|
74
|
+
"--color-sky-900 --color-sky-950 --color-slate-100 --color-slate-200 --color-slate-300 " +
|
|
75
|
+
"--color-slate-400 --color-slate-50 --color-slate-500 --color-slate-600 --color-slate-700 " +
|
|
76
|
+
"--color-slate-800 --color-slate-900 --color-slate-950 --color-stone-100 --color-stone-200 " +
|
|
77
|
+
"--color-stone-300 --color-stone-400 --color-stone-50 --color-stone-500 --color-stone-600 " +
|
|
78
|
+
"--color-stone-700 --color-stone-800 --color-stone-900 --color-stone-950 --color-taupe-100 " +
|
|
79
|
+
"--color-taupe-200 --color-taupe-300 --color-taupe-400 --color-taupe-50 --color-taupe-500 " +
|
|
80
|
+
"--color-taupe-600 --color-taupe-700 --color-taupe-800 --color-taupe-900 --color-taupe-950 " +
|
|
81
|
+
"--color-teal-100 --color-teal-200 --color-teal-300 --color-teal-400 --color-teal-50 " +
|
|
82
|
+
"--color-teal-500 --color-teal-600 --color-teal-700 --color-teal-800 --color-teal-900 " +
|
|
83
|
+
"--color-teal-950 --color-violet-100 --color-violet-200 --color-violet-300 --color-violet-400 " +
|
|
84
|
+
"--color-violet-50 --color-violet-500 --color-violet-600 --color-violet-700 --color-violet-800 " +
|
|
85
|
+
"--color-violet-900 --color-violet-950 --color-white --color-yellow-100 --color-yellow-200 " +
|
|
86
|
+
"--color-yellow-300 --color-yellow-400 --color-yellow-50 --color-yellow-500 --color-yellow-600 " +
|
|
87
|
+
"--color-yellow-700 --color-yellow-800 --color-yellow-900 --color-yellow-950 --color-zinc-100 " +
|
|
88
|
+
"--color-zinc-200 --color-zinc-300 --color-zinc-400 --color-zinc-50 --color-zinc-500 " +
|
|
89
|
+
"--color-zinc-600 --color-zinc-700 --color-zinc-800 --color-zinc-900 --color-zinc-950 " +
|
|
90
|
+
"--container-2xl --container-2xs --container-3xl --container-3xs --container-4xl --container-5xl " +
|
|
91
|
+
"--container-6xl --container-7xl --container-lg --container-md --container-sm --container-xl " +
|
|
92
|
+
"--container-xs --default-font-family --default-font-feature-settings " +
|
|
93
|
+
"--default-font-variation-settings --default-mono-font-family " +
|
|
94
|
+
"--default-mono-font-feature-settings --default-mono-font-variation-settings " +
|
|
95
|
+
"--default-transition-duration --default-transition-timing-function --drop-shadow-2xl " +
|
|
96
|
+
"--drop-shadow-lg --drop-shadow-md --drop-shadow-sm --drop-shadow-xl --drop-shadow-xs --ease-in " +
|
|
97
|
+
"--ease-in-out --ease-out --font-mono --font-sans --font-serif --font-weight-black " +
|
|
98
|
+
"--font-weight-bold --font-weight-extrabold --font-weight-extralight --font-weight-light " +
|
|
99
|
+
"--font-weight-medium --font-weight-normal --font-weight-semibold --font-weight-thin " +
|
|
100
|
+
"--inset-shadow-2xs --inset-shadow-sm --inset-shadow-xs --leading-loose --leading-normal " +
|
|
101
|
+
"--leading-relaxed --leading-snug --leading-tight --perspective-distant --perspective-dramatic " +
|
|
102
|
+
"--perspective-midrange --perspective-near --perspective-normal --radius-2xl --radius-3xl " +
|
|
103
|
+
"--radius-4xl --radius-lg --radius-md --radius-sm --radius-xl --radius-xs --shadow-2xl " +
|
|
104
|
+
"--shadow-2xs --shadow-lg --shadow-md --shadow-sm --shadow-xl --shadow-xs --spacing --text-2xl " +
|
|
105
|
+
"--text-2xl--line-height --text-3xl --text-3xl--line-height --text-4xl --text-4xl--line-height " +
|
|
106
|
+
"--text-5xl --text-5xl--line-height --text-6xl --text-6xl--line-height --text-7xl " +
|
|
107
|
+
"--text-7xl--line-height --text-8xl --text-8xl--line-height --text-9xl --text-9xl--line-height " +
|
|
108
|
+
"--text-base --text-base--line-height --text-lg --text-lg--line-height --text-shadow-2xs " +
|
|
109
|
+
"--text-shadow-lg --text-shadow-md --text-shadow-sm --text-shadow-xs --text-sm " +
|
|
110
|
+
"--text-sm--line-height --text-xl --text-xl--line-height --text-xs --text-xs--line-height " +
|
|
111
|
+
"--tracking-normal --tracking-tight --tracking-tighter --tracking-wide --tracking-wider " +
|
|
112
|
+
"--tracking-widest";
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { KIT_NAMES, TAILWIND_THEME_NAMES } from "./css-variables.generated";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The guard against an undeclared CSS custom property (0.33,
|
|
5
|
+
* docs/colour-roles-harmonization.md §8, §12.5).
|
|
6
|
+
*
|
|
7
|
+
* A `var(--name)` with no fallback, where nothing declares `--name`, makes the whole
|
|
8
|
+
* declaration invalid at computed-value time: `fill` inherits, `color` inherits, a
|
|
9
|
+
* background goes transparent. Kurvenschmiede's control diagram painted
|
|
10
|
+
* `fill-[var(--surface)]` — a token that never existed (the kit's is `--bg-surface`) —
|
|
11
|
+
* and drew black boxes on a public page. The class compiled, the build passed, the type
|
|
12
|
+
* checker had no opinion, and nothing in any suite noticed. This is the check that
|
|
13
|
+
* notices, run from an app's own test over the source texts the app collects.
|
|
14
|
+
*
|
|
15
|
+
* Browser-safe on purpose, like the rest of the kit: it takes TEXTS, not paths, so an app
|
|
16
|
+
* reads its files however its setup allows (`import.meta.glob` with `?raw`, or `node:fs`)
|
|
17
|
+
* and the kit carries no `node:fs`.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Every custom property the installed kit declares or publishes to apps.
|
|
21
|
+
*
|
|
22
|
+
* Generated from `tokens.css` when the kit is built (scripts/gen-kit-css-variables.mjs),
|
|
23
|
+
* so it is always the installed version's, and an app never searches `node_modules`.
|
|
24
|
+
* It leaves out two kinds of name on purpose:
|
|
25
|
+
* - a name declared only in an `@theme inline` block (`--text-color-muted`,
|
|
26
|
+
* `--background-color-surface`): Tailwind emits no variable for it, so
|
|
27
|
+
* `var(--text-color-muted)` would pass a guard that counted it and paint nothing;
|
|
28
|
+
* - AppShell's measured `--app-nav-h`, which exists only while an AppShell is mounted —
|
|
29
|
+
* on a landing, legal, sign-in or pay page it is undefined. Read it with a fallback,
|
|
30
|
+
* `var(--app-nav-h,0px)`, as every kit read does.
|
|
31
|
+
* Everything `applyTokenSet` and More contrast write on `<html>` is in, through the
|
|
32
|
+
* stylesheet's `:root` / `.dark` mirror. Component internals (`--slider-fill`, `--fp-*`,
|
|
33
|
+
* `--icon-button-tone`, `--label-strip-edge`) are not: an app that reads one depends on
|
|
34
|
+
* the kit's internals, and the guard says so. */
|
|
35
|
+
export const KIT_CSS_VARIABLES: ReadonlySet<string> = new Set(KIT_NAMES.split(" "));
|
|
36
|
+
|
|
37
|
+
/** Tailwind's own theme variables (`--color-rose-500`, `--spacing`, `--text-sm` …), from
|
|
38
|
+
* the `tailwindcss/theme.css` the kit builds with. Tailwind emits one when a scanned file
|
|
39
|
+
* names it, so a `var()` of it resolves. */
|
|
40
|
+
const TAILWIND_THEME: ReadonlySet<string> = new Set(TAILWIND_THEME_NAMES.split(" "));
|
|
41
|
+
|
|
42
|
+
/** A Tailwind PALETTE colour: a hue with a shade, black or white. `refusePalette` reports
|
|
43
|
+
* these although they resolve — an app that has no palette lint asks this test to be
|
|
44
|
+
* its refusal. */
|
|
45
|
+
const PALETTE_VARIABLE = /^--color-(?:[a-z]+-\d{2,3}|black|white)$/;
|
|
46
|
+
|
|
47
|
+
/** Read only where it is set: the guard accepts it in a fallback use, never bare. */
|
|
48
|
+
const APP_NAV_H = "--app-nav-h";
|
|
49
|
+
|
|
50
|
+
export interface UndeclaredCssVariablesOptions {
|
|
51
|
+
/** Variables a library writes at runtime, as names or patterns:
|
|
52
|
+
* `["--radix-collapsible-content-height"]`. An app's own writes are found in its
|
|
53
|
+
* source and need no listing. */
|
|
54
|
+
runtime?: readonly (string | RegExp)[];
|
|
55
|
+
/** Also check `var(--x, …)`. Off by default: a fallback says what happens when the
|
|
56
|
+
* name is missing. `--app-nav-h` counts as declared only here. */
|
|
57
|
+
checkFallbacks?: boolean;
|
|
58
|
+
/** Report Tailwind palette variables (`--color-red-500`, `--color-white`) although
|
|
59
|
+
* they resolve. Default false. */
|
|
60
|
+
refusePalette?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Comments are not CSS. `/* *\/` in both; `//` in TS only (not after a `:`, so a URL in a
|
|
64
|
+
* string survives). Replaced by spaces of the same shape, so line numbers hold. */
|
|
65
|
+
function stripComments(text: string, isCss: boolean): string {
|
|
66
|
+
const blank = (m: string) => m.replace(/[^\n]/g, " ");
|
|
67
|
+
const noBlocks = text.replace(/\/\*[\s\S]*?\*\//g, blank);
|
|
68
|
+
return isCss ? noBlocks : noBlocks.replace(/(^|[^:])(\/\/[^\n]*)/g, (_, pre: string, c: string) => pre + blank(c));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The text with every `@theme … inline … { … }` block blanked out, braces and all. */
|
|
72
|
+
function withoutInlineTheme(css: string): string {
|
|
73
|
+
let out = css;
|
|
74
|
+
for (const m of css.matchAll(/@theme\b[^{;]*\binline\b[^{;]*\{/g)) {
|
|
75
|
+
let depth = 0;
|
|
76
|
+
let end = m.index + m[0].length - 1;
|
|
77
|
+
for (; end < css.length; end++) {
|
|
78
|
+
if (css[end] === "{") depth++;
|
|
79
|
+
else if (css[end] === "}" && --depth === 0) break;
|
|
80
|
+
}
|
|
81
|
+
out = out.slice(0, m.index) + css.slice(m.index, end + 1).replace(/[^\n]/g, " ") + out.slice(end + 1);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Every custom property a stylesheet declares (`--name:`), in any block — except a name
|
|
88
|
+
* declared only inside `@theme inline`, which Tailwind never emits as a variable. Comments
|
|
89
|
+
* are skipped. The same rule builds {@link KIT_CSS_VARIABLES} from `tokens.css`.
|
|
90
|
+
*/
|
|
91
|
+
export function declaredCustomProperties(css: string): Set<string> {
|
|
92
|
+
const code = withoutInlineTheme(stripComments(css, true));
|
|
93
|
+
return new Set([...code.matchAll(/(--[a-zA-Z0-9_-]+)\s*:/g)].map((m) => m[1]));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** What an app's source writes at runtime: `setProperty("--name"`, a React style key
|
|
97
|
+
* `"--name":`, a Tailwind arbitrary property `[--name:…]`. */
|
|
98
|
+
function runtimeWrites(code: string): string[] {
|
|
99
|
+
return [
|
|
100
|
+
...code.matchAll(/setProperty\(\s*["'`](--[a-zA-Z0-9_-]+)["'`]/g),
|
|
101
|
+
...code.matchAll(/["'](--[a-zA-Z0-9_-]+)["']\s*:/g),
|
|
102
|
+
...code.matchAll(/\[(--[a-zA-Z0-9_-]+):/g),
|
|
103
|
+
].map((m) => m[1]);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** A chart series' colour (`var(--color-total)`) is declared in a file with a `total: {`
|
|
107
|
+
* config entry: the kit's ChartContainer writes `--color-<key>` for each key. */
|
|
108
|
+
function chartKeys(code: string): Set<string> {
|
|
109
|
+
return new Set([...code.matchAll(/(?:^|[\s{,])["']?([A-Za-z_][\w-]*)["']?\s*:\s*\{/g)].map((m) => m[1]));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const isCssPath = (path: string) => /\.css(?:\?|$)/.test(path);
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Every `var(--name)` without a fallback that nothing declares, as `"path:line --name"`.
|
|
116
|
+
*
|
|
117
|
+
* `sources` maps a path to its text: the app's `.ts`, `.tsx` and `.css` source, tests
|
|
118
|
+
* left out. A name counts as declared when it is:
|
|
119
|
+
* 1. the kit's ({@link KIT_CSS_VARIABLES});
|
|
120
|
+
* 2. declared in any `.css` entry of `sources` (outside `@theme inline`);
|
|
121
|
+
* 3. written by the source at runtime (`setProperty`, a style key, `[--name:…]`);
|
|
122
|
+
* 4. one of Tailwind's theme variables — unless `refusePalette` and it is a palette colour;
|
|
123
|
+
* 5. a chart series' `--color-<key>`, in a file with a `<key>: {` entry;
|
|
124
|
+
* 6. listed in `options.runtime`.
|
|
125
|
+
*
|
|
126
|
+
* Not counted: comments; a use with a fallback, unless `checkFallbacks`; a name built at
|
|
127
|
+
* runtime (`` `var(--chart-${n})` `` — a name must be followed by `)` or `,`).
|
|
128
|
+
*
|
|
129
|
+
* It REFUSES A VACUOUS RUN: it throws when `sources` holds fewer than ten files, or when
|
|
130
|
+
* a `.css` entry is empty. Vitest stubs every stylesheet with an empty string by default,
|
|
131
|
+
* `?raw` included, so an app that reads its CSS that way without opting it out of the stub
|
|
132
|
+
* (`test.css.include`) would otherwise lose every declaration and every use in it, and
|
|
133
|
+
* this would pass or fail for the wrong reason.
|
|
134
|
+
*/
|
|
135
|
+
export function undeclaredCssVariables(
|
|
136
|
+
sources: Record<string, string>,
|
|
137
|
+
options: UndeclaredCssVariablesOptions = {},
|
|
138
|
+
): string[] {
|
|
139
|
+
const { runtime = [], checkFallbacks = false, refusePalette = false } = options;
|
|
140
|
+
const entries = Object.entries(sources);
|
|
141
|
+
if (entries.length < 10) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`undeclaredCssVariables: only ${entries.length} source file(s) — the glob or the file list found ` +
|
|
144
|
+
`(almost) nothing, and a check over nothing passes. Pass the app's .ts/.tsx/.css source.`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
const emptyCss = entries.filter(([path, text]) => isCssPath(path) && text.trim() === "").map(([path]) => path);
|
|
148
|
+
if (emptyCss.length) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`undeclaredCssVariables: ${emptyCss.join(", ")} read as empty. Vitest stubs stylesheets with "" ` +
|
|
151
|
+
`(\`?raw\` too) unless the file is listed in \`test.css.include\`.`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const code = entries.map(([path, text]) => [path, stripComments(text, isCssPath(path))] as const);
|
|
156
|
+
const declared = new Set<string>();
|
|
157
|
+
for (const [path, text] of code) {
|
|
158
|
+
if (isCssPath(path)) for (const name of declaredCustomProperties(text)) declared.add(name);
|
|
159
|
+
for (const name of runtimeWrites(text)) declared.add(name);
|
|
160
|
+
}
|
|
161
|
+
const listed = (name: string) =>
|
|
162
|
+
runtime.some((r) => (typeof r === "string" ? r === name : r.test(name)));
|
|
163
|
+
|
|
164
|
+
const out: string[] = [];
|
|
165
|
+
for (const [path, text] of code) {
|
|
166
|
+
let keys: Set<string> | null = null;
|
|
167
|
+
for (const m of text.matchAll(/var\(\s*(--[a-zA-Z0-9_-]+)\s*([,)])/g)) {
|
|
168
|
+
const [, name, next] = m;
|
|
169
|
+
const fallback = next === ",";
|
|
170
|
+
if (fallback && !checkFallbacks) continue;
|
|
171
|
+
if (KIT_CSS_VARIABLES.has(name) || declared.has(name) || listed(name)) continue;
|
|
172
|
+
if (fallback && name === APP_NAV_H) continue;
|
|
173
|
+
if (TAILWIND_THEME.has(name)) {
|
|
174
|
+
if (refusePalette && PALETTE_VARIABLE.test(name)) {
|
|
175
|
+
out.push(`${path}:${lineOf(text, m.index)} ${name} (a Tailwind palette colour; refusePalette is on)`);
|
|
176
|
+
}
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const chart = /^--color-([A-Za-z_][\w-]*)$/.exec(name);
|
|
180
|
+
if (chart && (keys ??= chartKeys(text)).has(chart[1])) continue;
|
|
181
|
+
out.push(`${path}:${lineOf(text, m.index)} ${name}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function lineOf(text: string, index: number): number {
|
|
188
|
+
let line = 1;
|
|
189
|
+
for (let i = 0; i < index; i++) if (text.charCodeAt(i) === 10) line++;
|
|
190
|
+
return line;
|
|
191
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// `@eifi1/ui-kit/testing` — helpers an app runs from its OWN test suite (0.33).
|
|
2
|
+
//
|
|
3
|
+
// Not part of the main barrel: nothing here renders, and an app's production bundle has
|
|
4
|
+
// no business carrying it. Browser-safe like the rest of the kit (no `node:fs`): each
|
|
5
|
+
// helper takes texts, so an app collects them with `import.meta.glob` or `node:fs`, as
|
|
6
|
+
// its setup allows. The first content is the guard against undeclared CSS custom
|
|
7
|
+
// properties (docs/colour-roles-harmonization.md §8).
|
|
8
|
+
export {
|
|
9
|
+
KIT_CSS_VARIABLES,
|
|
10
|
+
declaredCustomProperties,
|
|
11
|
+
undeclaredCssVariables,
|
|
12
|
+
type UndeclaredCssVariablesOptions,
|
|
13
|
+
} from "./css-variables";
|
|
@@ -49,8 +49,9 @@ function toLab(hex: string): Lab | null {
|
|
|
49
49
|
|
|
50
50
|
/** `color-mix(in oklab, a p, b)` as a hex — the same formula tokens.css's derived roles
|
|
51
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
|
-
|
|
52
|
+
* not hex (a consumer's own token set) comes back unmixed. The deriver mixes with it
|
|
53
|
+
* too (`--bg-hover`, `--brand-muted`, `--brand-bg`, `--brand-bg-hover`). */
|
|
54
|
+
export function mix(a: string, b: string, p: number): string {
|
|
54
55
|
const A = toLab(a);
|
|
55
56
|
const B = toLab(b);
|
|
56
57
|
if (!A || !B) return a;
|
|
@@ -62,6 +63,56 @@ function mix(a: string, b: string, p: number): string {
|
|
|
62
63
|
return toHex(oklchToRgb({ l, c, h }));
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
/**
|
|
67
|
+
* `--bg-hover` in tokens.css: this share of `--text-primary` mixed into `--bg-surface`.
|
|
68
|
+
*
|
|
69
|
+
* 5.5 %, down from 7 % in 0.33 (docs/colour-roles-harmonization.md §12.10): a hovered row
|
|
70
|
+
* on a card is the darkest fill (in dark, the lightest) that text sits on, and at 7 % the
|
|
71
|
+
* light status colours, the money colours and Ink/Moss/Plum's muted text fell to
|
|
72
|
+
* 4.25–4.49:1 on it. The status literals are fixed, the same in every preset, so the
|
|
73
|
+
* hover moved instead: text wins over the hover's step. At 5.5 % the step from the card
|
|
74
|
+
* is still 1.09–1.13:1, above the 1.08:1 the kit's hover rule asks (§12.2). The deriver
|
|
75
|
+
* solves the money colours against this fill, and tokens-css-audit.test.ts measures
|
|
76
|
+
* every text role on it in every shipped preset, so the two cannot part.
|
|
77
|
+
*/
|
|
78
|
+
export const HOVER_INK = 0.055;
|
|
79
|
+
|
|
80
|
+
/** The card's hover fill (`--bg-hover`) for `t`, as tokens.css mixes it. */
|
|
81
|
+
export function hoverFill(t: Pick<TokenSet, "textPrimary" | "bgSurface">): string {
|
|
82
|
+
return mix(t.textPrimary, t.bgSurface, HOVER_INK);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* `--brand-muted` in tokens.css: this share of `--brand` mixed into `--text-primary`.
|
|
87
|
+
*
|
|
88
|
+
* 62 %, down from 70 % in 0.33 (docs/colour-roles-harmonization.md §12.11): the brand as
|
|
89
|
+
* text sits on its soft chip HOVERED too (`--brand-bg-hover` — ButtonGroupLink's current
|
|
90
|
+
* page, a selected day), and at 70 % it read 4.08–4.42:1 there in Default and Moss light
|
|
91
|
+
* and in Ink, Moss, Plum and Contrast dark. The brand could not move instead: the dark
|
|
92
|
+
* brands would have flipped their white label to the dark ink. Nor could the chip's hover
|
|
93
|
+
* lighten: by the time the text cleared 4.5:1 the hover sat 1.00:1 from the chip it
|
|
94
|
+
* hovers. So the text moves toward the ink, as `--bg-hover` moved for its text.
|
|
95
|
+
*/
|
|
96
|
+
export const BRAND_MUTED_SHARE = 0.62;
|
|
97
|
+
/** `--brand-bg` and `--brand-bg-hover` in tokens.css: these shares of `--brand` mixed
|
|
98
|
+
* into `--bg-surface`. */
|
|
99
|
+
export const BRAND_BG_SHARE = 0.14;
|
|
100
|
+
export const BRAND_BG_HOVER_SHARE = 0.24;
|
|
101
|
+
|
|
102
|
+
/** `--brand-muted`, `--brand-bg` and `--brand-bg-hover` for `t`, as tokens.css mixes them.
|
|
103
|
+
* tokens-css-audit.test.ts holds the three shares equal to the stylesheet's. */
|
|
104
|
+
export function brandSoft(t: Pick<TokenSet, "brand" | "textPrimary" | "bgSurface">): {
|
|
105
|
+
brandMuted: string;
|
|
106
|
+
brandBg: string;
|
|
107
|
+
brandBgHover: string;
|
|
108
|
+
} {
|
|
109
|
+
return {
|
|
110
|
+
brandMuted: mix(t.brand, t.textPrimary, BRAND_MUTED_SHARE),
|
|
111
|
+
brandBg: mix(t.brand, t.bgSurface, BRAND_BG_SHARE),
|
|
112
|
+
brandBgHover: mix(t.brand, t.bgSurface, BRAND_BG_HOVER_SHARE),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
65
116
|
/** WCAG 1.4.11 for a component's boundary. */
|
|
66
117
|
const BORDER_TARGET = 3;
|
|
67
118
|
/** `--border-strong` in tokens.css: 55 % border, 45 % text. */
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
solveLightness,
|
|
14
14
|
} from "./color";
|
|
15
15
|
import type { CvdType, Oklch } from "./color";
|
|
16
|
+
import { brandSoft, hoverFill } from "./contrast-tokens";
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Build a whole palette from one brand colour, plus any number of colours pinned by
|
|
@@ -161,7 +162,14 @@ function placeHue(home: number, slack: number, taken: number[]): number {
|
|
|
161
162
|
const SURFACE_L = {
|
|
162
163
|
// Light: the PAGE is slightly darker than the cards on it, so a card reads as raised
|
|
163
164
|
// without needing a shadow to say so. Matches the shipped presets' relationship.
|
|
164
|
-
|
|
165
|
+
//
|
|
166
|
+
// The light WELL is the page itself (0.33, docs/colour-roles-harmonization.md §5.3,
|
|
167
|
+
// k26). Between the card and the page it sat 1.02–1.05:1 from the card it is inset
|
|
168
|
+
// in, which is no well at all; at the page's lightness it stands out from the card
|
|
169
|
+
// exactly as far as the card does from the page, and nothing re-solves, because the
|
|
170
|
+
// page is already the surface every text role is solved against. Dark keeps its
|
|
171
|
+
// well a step lighter than the card (1.11–1.13:1).
|
|
172
|
+
light: { page: 0.912, surface: 0.945, surface2: 0.912 },
|
|
165
173
|
dark: { page: 0.158, surface: 0.206, surface2: 0.249 },
|
|
166
174
|
} as const;
|
|
167
175
|
|
|
@@ -272,6 +280,19 @@ export function derivePalette({
|
|
|
272
280
|
const textSecondary = solveAgainst(brand.h, textChroma, targets.secondary, worstSurface, mode);
|
|
273
281
|
const textMuted = solveAgainst(brand.h, textChroma, targets.muted, worstSurface, mode);
|
|
274
282
|
|
|
283
|
+
// The darkest fill text sits on (the lightest, in dark): the worst surface, or a
|
|
284
|
+
// hovered row on the card (`--bg-hover`), which in light is a shade darker than the
|
|
285
|
+
// page. The colours that are solved exactly to 4.5:1 — money, the semantic text — are
|
|
286
|
+
// solved against this, so they hold on a hovered row too (0.33, §12.10). The text
|
|
287
|
+
// roles above keep their margin (4.6:1 and up) and need no second solve.
|
|
288
|
+
const hover = hoverFill({ textPrimary, bgSurface: surfaces.bgSurface });
|
|
289
|
+
const worstText = [worstSurface, hover].reduce((a, b) =>
|
|
290
|
+
contrast(a, mode === "light" ? "#000000" : "#ffffff") <
|
|
291
|
+
contrast(b, mode === "light" ? "#000000" : "#ffffff")
|
|
292
|
+
? a
|
|
293
|
+
: b,
|
|
294
|
+
);
|
|
295
|
+
|
|
275
296
|
// ── Brand. Keep the hue the caller asked for; move only the lightness, and only as
|
|
276
297
|
// far as 3:1 against the worst surface — WCAG 1.4.11, since a brand-filled control is
|
|
277
298
|
// a graphical object whose boundary has to be perceivable.
|
|
@@ -280,7 +301,7 @@ export function derivePalette({
|
|
|
280
301
|
// NORMALISED, never the caller's raw string. `parseHex` accepts "4f46e5" and "#abc",
|
|
281
302
|
// and passing either through unchanged put a value in the TokenSet that is not a CSS
|
|
282
303
|
// colour — it then fails silently at paint time, three layers from here.
|
|
283
|
-
|
|
304
|
+
let brandHex =
|
|
284
305
|
brandRatio >= 3
|
|
285
306
|
? toHex(parseHex(anchors.brand)!)
|
|
286
307
|
: solveAgainst(brand.h, brandChroma, 3, worstSurface, mode);
|
|
@@ -291,13 +312,39 @@ export function derivePalette({
|
|
|
291
312
|
`${brandHex}; hue and chroma are unchanged.`,
|
|
292
313
|
);
|
|
293
314
|
}
|
|
315
|
+
// `--brand-muted` is the brand AS TEXT — a soft brand chip's label, an outline chip, a
|
|
316
|
+
// current link — and tokens.css derives it: 62 % brand into the ink. A brand solved
|
|
317
|
+
// to 3:1 left it at 4.43–4.51:1 in three dark presets (0.33, §5.5), so it gets the
|
|
318
|
+
// text floor here: the brand moves away from the surfaces, a step at a time, until its
|
|
319
|
+
// muted form clears 4.5:1 on the worst fill and on its own soft chip, at rest
|
|
320
|
+
// (`--brand-bg`) and hovered (`--brand-bg-hover`, the harder of the two: §12.11).
|
|
321
|
+
const brandMutedWorst = (hex: string) => {
|
|
322
|
+
const soft = brandSoft({ brand: hex, textPrimary, bgSurface: surfaces.bgSurface });
|
|
323
|
+
return Math.min(
|
|
324
|
+
contrast(soft.brandMuted, worstText),
|
|
325
|
+
contrast(soft.brandMuted, soft.brandBg),
|
|
326
|
+
contrast(soft.brandMuted, soft.brandBgHover),
|
|
327
|
+
);
|
|
328
|
+
};
|
|
329
|
+
for (let i = 0; i < 40 && brandMutedWorst(brandHex) < 4.5; i++) {
|
|
330
|
+
const lch = hexToOklch(brandHex)!;
|
|
331
|
+
brandHex = oklchToHex({ ...lch, l: mode === "light" ? lch.l - 0.005 : lch.l + 0.005 });
|
|
332
|
+
}
|
|
333
|
+
if (brandMutedWorst(brandHex) < 4.5) {
|
|
334
|
+
warnings.push(
|
|
335
|
+
`the brand as text (--brand-muted) reaches only ${brandMutedWorst(brandHex).toFixed(2)}:1 ` +
|
|
336
|
+
`against the ${mode} surfaces (needs 4.5:1).`,
|
|
337
|
+
);
|
|
338
|
+
}
|
|
294
339
|
const brandLch = hexToOklch(brandHex)!;
|
|
340
|
+
const brandContrast = contrastOn(brandHex);
|
|
295
341
|
const brandHover = oklchToHex({
|
|
296
342
|
...brandLch,
|
|
297
|
-
// Hover moves AWAY from the
|
|
298
|
-
|
|
343
|
+
// Hover moves AWAY from the colour of the text on it (0.33, §5.5): darker under
|
|
344
|
+
// white, lighter under a dark ink. It used to move away from the page, which in dark
|
|
345
|
+
// lightened a fill that carries WHITE text, toward its own label: 4.07:1.
|
|
346
|
+
l: brandContrast === "#ffffff" ? brandLch.l - 0.06 : brandLch.l + 0.06,
|
|
299
347
|
});
|
|
300
|
-
const brandContrast = contrastOn(brandHex);
|
|
301
348
|
const onBrand = contrast(brandHex, brandContrast);
|
|
302
349
|
if (onBrand < 4.5) {
|
|
303
350
|
warnings.push(
|
|
@@ -352,7 +399,7 @@ export function derivePalette({
|
|
|
352
399
|
const bg = tintOn(hue, chroma, surfaces.bgSurface, mode);
|
|
353
400
|
const onSurface = solveLightness(
|
|
354
401
|
4.5,
|
|
355
|
-
parseHex(
|
|
402
|
+
parseHex(worstText)!,
|
|
356
403
|
hue,
|
|
357
404
|
chroma,
|
|
358
405
|
mode === "light" ? "darker" : "lighter",
|
|
@@ -425,11 +472,19 @@ export function derivePalette({
|
|
|
425
472
|
// red/green pair — which is why the shipped presets use teal/amber/violet. Derived
|
|
426
473
|
// here at fixed hues rather than from the brand, for the same reason `--danger` is:
|
|
427
474
|
// a consumer who rebrands does not thereby change what "money out" looks like.
|
|
428
|
-
const money = (hue: number, target: number) => solveAgainst(hue, 0.12, target,
|
|
475
|
+
const money = (hue: number, target: number) => solveAgainst(hue, 0.12, target, worstText, mode);
|
|
429
476
|
const moneyIncome = money(175, 4.5);
|
|
430
477
|
const moneyExpense = money(70, 4.5);
|
|
431
478
|
const moneyNet = money(300, 4.5);
|
|
432
|
-
|
|
479
|
+
// "No money has moved": a quiet grey at a fixed lightness, but printed as TEXT (the
|
|
480
|
+
// numpad's sign, a future row's figure), so it gets the text floor too (0.33). The
|
|
481
|
+
// fixed lightness left it at 4.24:1 on four light pages.
|
|
482
|
+
const neutralFloor = solveLightness(4.5, parseHex(worstText)!, brand.h, 0.012, mode === "light" ? "darker" : "lighter");
|
|
483
|
+
const moneyNeutral = oklchToHex({
|
|
484
|
+
l: mode === "light" ? Math.min(0.52, neutralFloor) : Math.max(0.72, neutralFloor),
|
|
485
|
+
c: 0.012,
|
|
486
|
+
h: brand.h,
|
|
487
|
+
});
|
|
433
488
|
|
|
434
489
|
const tokens: TokenSet = {
|
|
435
490
|
...surfaces,
|
|
@@ -498,6 +553,13 @@ export function auditPalette(t: TokenSet, semantic?: SemanticTokens): ContrastRe
|
|
|
498
553
|
["bgSurface", t.bgSurface],
|
|
499
554
|
["bgSurface2", t.bgSurface2],
|
|
500
555
|
];
|
|
556
|
+
// A hovered row on the card (`--bg-hover`) carries the same text as the card, and in
|
|
557
|
+
// light it is the darkest fill that text sits on (0.33, §12.10). Text is measured on
|
|
558
|
+
// it; the brand fill and the hairline, which are not text, are not.
|
|
559
|
+
const hover = hoverFill(t);
|
|
560
|
+
const textGrounds: Array<[string, string]> = [...surfaces, ["bgHover", hover]];
|
|
561
|
+
// `--brand-muted`, `--brand-bg` and `--brand-bg-hover`, mixed as tokens.css mixes them.
|
|
562
|
+
const { brandMuted, brandBg, brandBgHover } = brandSoft(t);
|
|
501
563
|
|
|
502
564
|
const add = (pair: string, fg: string, bg: string, required: number, rule: string) => {
|
|
503
565
|
const ratio = contrast(fg, bg);
|
|
@@ -505,21 +567,35 @@ export function auditPalette(t: TokenSet, semantic?: SemanticTokens): ContrastRe
|
|
|
505
567
|
};
|
|
506
568
|
|
|
507
569
|
for (const [name, bg] of surfaces) {
|
|
570
|
+
add(`brand on ${name}`, t.brand, bg, 3, "WCAG 1.4.11 graphical object");
|
|
571
|
+
add(`border on ${name}`, t.border, bg, 1.1, "house rule: a hairline must be findable");
|
|
572
|
+
}
|
|
573
|
+
for (const [name, bg] of textGrounds) {
|
|
508
574
|
add(`textPrimary on ${name}`, t.textPrimary, bg, 7, "WCAG 1.4.6 AAA body text");
|
|
509
575
|
add(`textSecondary on ${name}`, t.textSecondary, bg, 4.5, "WCAG 1.4.3 AA body text");
|
|
510
576
|
add(`textMuted on ${name}`, t.textMuted, bg, 4.5, "WCAG 1.4.3 AA body text");
|
|
511
|
-
add(`
|
|
512
|
-
add(`border on ${name}`, t.border, bg, 1.1, "house rule: a hairline must be findable");
|
|
577
|
+
add(`brandMuted on ${name}`, brandMuted, bg, 4.5, "WCAG 1.4.3 — the brand as text");
|
|
513
578
|
for (const [key, hex] of [
|
|
514
579
|
["moneyIncome", t.moneyIncome],
|
|
515
580
|
["moneyExpense", t.moneyExpense],
|
|
516
581
|
["moneyNet", t.moneyNet],
|
|
582
|
+
// Not a figure's colour but still printed as one (the numpad's sign, a future
|
|
583
|
+
// row's amount) — measured since 0.33, when it was found at 4.24:1.
|
|
584
|
+
["moneyNeutral", t.moneyNeutral],
|
|
517
585
|
] as const) {
|
|
518
586
|
add(`${key} on ${name}`, hex, bg, 4.5, "WCAG 1.4.3 — these are rendered as TEXT");
|
|
519
587
|
}
|
|
520
588
|
}
|
|
521
589
|
|
|
522
590
|
add("brandContrast on brand", t.brandContrast, t.brand, 4.5, "WCAG 1.4.3 AA body text");
|
|
591
|
+
// A hovered brand button carries the same label (0.33): the derived dark presets
|
|
592
|
+
// lightened the hover toward their white label and left it at 4.07:1.
|
|
593
|
+
add("brandContrast on brandHover", t.brandContrast, t.brandHover, 4.5, "WCAG 1.4.3 AA body text");
|
|
594
|
+
add("brandMuted on brandBg", brandMuted, brandBg, 4.5, "WCAG 1.4.3 — a soft brand chip's label");
|
|
595
|
+
// The same label on the chip hovered (§12.11): a current link, a selected day. At 70 %
|
|
596
|
+
// brand the muted text read 4.08–4.42:1 here (Default and Moss light; Ink, Moss, Plum
|
|
597
|
+
// and Contrast dark).
|
|
598
|
+
add("brandMuted on brandBgHover", brandMuted, brandBgHover, 4.5, "WCAG 1.4.3 — a hovered soft brand chip's label");
|
|
523
599
|
|
|
524
600
|
if (semantic) {
|
|
525
601
|
for (const [name, fg] of [
|
|
@@ -529,6 +605,7 @@ export function auditPalette(t: TokenSet, semantic?: SemanticTokens): ContrastRe
|
|
|
529
605
|
["success", semantic.success],
|
|
530
606
|
] as const) {
|
|
531
607
|
add(`${name} on bgSurface`, fg, t.bgSurface, 4.5, "WCAG 1.4.3 — rendered as text");
|
|
608
|
+
add(`${name} on bgHover`, fg, hover, 4.5, "WCAG 1.4.3 — rendered as text, on a hovered row");
|
|
532
609
|
}
|
|
533
610
|
add("danger on dangerBg", semantic.danger, semantic.dangerBg, 4.5, "WCAG 1.4.3 — badge text");
|
|
534
611
|
add("warning on warningBg", semantic.warning, semantic.warningBg, 4.5, "WCAG 1.4.3 — badge text");
|