From 1b7bef6453d5d16df046d1db1aefc6ce843e85cd Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Tue, 18 Aug 2026 17:35:11 +0200 Subject: [PATCH 01/21] fix(sdk): repoint dangling --wb-* custom properties at existing tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 23 CSS variable names were used via var() but defined nowhere — --ax- prefix typos, misspelled token names, and two edge-label variables with no definition. Form message (neutral/warning) and the variables-settings modal were visibly rendering without their intended colors and spacing. Redundant fallbacks that masked the typos are dropped in the same lines; where a used name has no token equivalent, the value-equivalent token is substituted (tab-header 16px gap -> spacing-16). --- .changeset/wb-486-dangling-token-names.md | 5 +++++ .../src/components/form/message/message.module.css | 8 ++++---- .../features/diagram/edges/label-edge/variables.css | 4 +++- .../variable-text/variable-text.module.css | 6 +++--- .../variables/modals/modal-settings.module.css | 2 +- .../modals/settings/settings-navigation.module.css | 12 ++++++------ .../pane-remove-variable.module.css | 13 ++++++------- .../variable-preview/variable-preview.module.css | 7 +++---- .../variables/modals/tab/tab-header.module.css | 2 +- 9 files changed, 32 insertions(+), 27 deletions(-) create mode 100644 .changeset/wb-486-dangling-token-names.md diff --git a/.changeset/wb-486-dangling-token-names.md b/.changeset/wb-486-dangling-token-names.md new file mode 100644 index 000000000..654db4daf --- /dev/null +++ b/.changeset/wb-486-dangling-token-names.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +Fix CSS variable names that pointed at non-existent tokens: neutral and warning form-message variants render their intended colors again, and the variables-settings modal picks up its token-defined colors, spacing, and radii. diff --git a/packages/sdk/src/components/form/message/message.module.css b/packages/sdk/src/components/form/message/message.module.css index d910b50c3..6fcaa185e 100644 --- a/packages/sdk/src/components/form/message/message.module.css +++ b/packages/sdk/src/components/form/message/message.module.css @@ -20,13 +20,13 @@ } .container--neutral { - --color: var(--wb-colors-gray-800); - --background-color: var(--wb-ui-bg-secondary-default); + --color: var(--ax-colors-gray-800); + --background-color: var(--ax-ui-bg-secondary-default); } .container--warning { - --color: var(--wb-chips-acc5-txt); - --background-color: var(--wb-chips-acc5-bg); + --color: var(--ax-chips-acc5-txt); + --background-color: var(--ax-chips-acc5-bg); } .container--error { diff --git a/packages/sdk/src/features/diagram/edges/label-edge/variables.css b/packages/sdk/src/features/diagram/edges/label-edge/variables.css index 177f6f088..1c5253891 100644 --- a/packages/sdk/src/features/diagram/edges/label-edge/variables.css +++ b/packages/sdk/src/features/diagram/edges/label-edge/variables.css @@ -1,9 +1,11 @@ :root { --wb-edge-color: var(--ax-colors-gray-500); /* missing token */ - --wb-edge-color-temporary: var(--ax-colors-gray-500-50); /* missing token */ + --wb-edge-color-temporary: color-mix(in srgb, var(--ax-colors-gray-500) 50%, transparent); --wb-edge-color-hover: var(--ax-colors-gray-600); /* missing token */ --wb-edge-color-select: var(--ax-colors-acc1-500); /* missing token */ --wb-edge-label-color: var(--ax-colors-gray-500); /* missing token */ + --wb-edge-label-color-hover: var(--ax-edge-primary-hover); + --wb-edge-label-color-select: var(--ax-edge-primary-active); --wb-edge-label-background: var(--ax-colors-gray-200); /* missing token */ } diff --git a/packages/sdk/src/features/variables/components/variable-text/variable-text.module.css b/packages/sdk/src/features/variables/components/variable-text/variable-text.module.css index 4794b78fc..2561aeccc 100644 --- a/packages/sdk/src/features/variables/components/variable-text/variable-text.module.css +++ b/packages/sdk/src/features/variables/components/variable-text/variable-text.module.css @@ -177,17 +177,17 @@ body:has(.suggestionsContainer) { display: flex; color: var(--ax-txt-primary-default); align-items: center; - gap: var(--wb-token-spacing-spacing-4, 4px); + gap: var(--ax-token-spacing-spacing-4); flex: 1 0 0; padding: 6px; } .iconWrapper { display: flex; - padding: var(--wb-token-spacing-button-s-v-pad-2, 7px); + padding: var(--ax-token-spacing-button-s-v-pad-2); justify-content: center; align-items: center; - gap: var(--wb-token-spacing-button-s-gap, 8px); + gap: var(--ax-token-spacing-button-s-gap); svg { fill: var(--wb-txt-ghost-primary-default); diff --git a/packages/sdk/src/features/variables/modals/modal-settings.module.css b/packages/sdk/src/features/variables/modals/modal-settings.module.css index 7e9208e82..2d388e5e1 100644 --- a/packages/sdk/src/features/variables/modals/modal-settings.module.css +++ b/packages/sdk/src/features/variables/modals/modal-settings.module.css @@ -15,7 +15,7 @@ @media (width > 800px) { flex-flow: row; height: 500px; - gap: var(--wb-primitive-4rule-48, 48px); + gap: var(--ax-primitive-4rule-48); } } diff --git a/packages/sdk/src/features/variables/modals/settings/settings-navigation.module.css b/packages/sdk/src/features/variables/modals/settings/settings-navigation.module.css index 6b3bdea6f..09fb56ca3 100644 --- a/packages/sdk/src/features/variables/modals/settings/settings-navigation.module.css +++ b/packages/sdk/src/features/variables/modals/settings/settings-navigation.module.css @@ -8,8 +8,8 @@ flex-shrink: 0; align-self: flex-start; text-align: left; - gap: var(--wb-token-spacing-operator-wrap-gap, 4px); - border-radius: var(--wb-token-radius-dropdown-wrap-s, 8px); + gap: var(--ax-token-spacing-spacing-4); + border-radius: var(--ax-token-radius-dropdown-wrap-s); background: var(--wb-settings-navigation-background); } @@ -17,9 +17,9 @@ display: inline-flex; justify-content: flex-start; align-items: center; - color: var(--wb-nav-button-icon-primary-default); + color: var(--ax-nav-button-icon-primary-default); height: 2.25rem; - padding: 0 var(--wb-token-spacing-button-l-h-pad-1, 16px); + padding: 0 var(--ax-token-spacing-button-l-h-pad-1); /* Button/Button Medium */ font-family: Poppins; @@ -29,7 +29,7 @@ line-height: 100%; /* 14px */ font-weight: 600; font-size: 0.875rem; - border-radius: var(--wb-token-radius-button-l, 8px); + border-radius: var(--ax-token-radius-button-l); background: transparent; border: none; transition: all var(--wb-transition); @@ -40,6 +40,6 @@ } &.button--active { - color: var(--wb-nav-button-icon-primary-pressed); + color: var(--ax-nav-button-icon-primary-pressed); } } diff --git a/packages/sdk/src/features/variables/modals/tab-global-variables/pane-remove-variable/pane-remove-variable.module.css b/packages/sdk/src/features/variables/modals/tab-global-variables/pane-remove-variable/pane-remove-variable.module.css index 26d48e326..accf442b1 100644 --- a/packages/sdk/src/features/variables/modals/tab-global-variables/pane-remove-variable/pane-remove-variable.module.css +++ b/packages/sdk/src/features/variables/modals/tab-global-variables/pane-remove-variable/pane-remove-variable.module.css @@ -3,8 +3,8 @@ } .description { - margin-top: var(--wb-token-spacing-spacing-16); - margin-bottom: var(--wb-token-spacing-spacing-20, 20px); + margin-top: var(--ax-token-spacing-spacing-16); + margin-bottom: var(--ax-token-spacing-spacing-20); } .list { @@ -14,15 +14,14 @@ border-radius: 8px; border: 1px solid var(--wb-settings-remove-bg); background: var(--wb-settings-remove-bg); - color: var(--wb-txt-secondary-default); + color: var(--ax-txt-secondary-default); margin: 0; - padding: var(--wb-token-spacing-spacing-12, 12px) var(--wb-token-spacing-spacing-16, 16px) - var(--wb-token-spacing-spacing-12, 12px) 16px; + padding: var(--ax-token-spacing-spacing-12) var(--ax-token-spacing-spacing-16) var(--ax-token-spacing-spacing-12) 16px; li { margin: 0; padding: 0; - margin-left: var(--wb-token-spacing-spacing-16, 16px); + margin-left: var(--ax-token-spacing-spacing-16); } } @@ -37,7 +36,7 @@ .buttons { margin-top: auto; display: flex; - padding-top: var(--wb-token-spacing-spacing-16, 16px); + padding-top: var(--ax-token-spacing-spacing-16); > :last-child { margin-left: auto; diff --git a/packages/sdk/src/features/variables/modals/tab-global-variables/variable-preview/variable-preview.module.css b/packages/sdk/src/features/variables/modals/tab-global-variables/variable-preview/variable-preview.module.css index 20fdaa2e9..d9759dce6 100644 --- a/packages/sdk/src/features/variables/modals/tab-global-variables/variable-preview/variable-preview.module.css +++ b/packages/sdk/src/features/variables/modals/tab-global-variables/variable-preview/variable-preview.module.css @@ -2,15 +2,14 @@ display: flex; flex-flow: column; gap: 0.5rem; - padding: var(--wb-token-spacing-spacing-12, 12px) var(--wb-token-spacing-spacing-16, 16px) - var(--wb-token-spacing-spacing-12, 12px) 16px; + padding: var(--ax-token-spacing-spacing-12) var(--ax-token-spacing-spacing-16) var(--ax-token-spacing-spacing-12) 16px; border-radius: 8px; border: 1px solid var(--ax-ui-bg-secondary-default); background: var(--ax-ui-bg-secondary-default); transition: all var(--wb-transition); &:hover { - border-color: var(--wb-ui-stroke-primary-highlight); + border-color: var(--ax-ui-stroke-primary-highlight); .actions { opacity: 1; @@ -32,7 +31,7 @@ .description { margin: 0; - color: var(--wb-txt-tertiary-default); + color: var(--ax-txt-tertiary-default); display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; diff --git a/packages/sdk/src/features/variables/modals/tab/tab-header.module.css b/packages/sdk/src/features/variables/modals/tab/tab-header.module.css index 63d758035..b5865ff47 100644 --- a/packages/sdk/src/features/variables/modals/tab/tab-header.module.css +++ b/packages/sdk/src/features/variables/modals/tab/tab-header.module.css @@ -11,7 +11,7 @@ border-bottom: var(--settings-tab-header-border); + * { - padding-top: var(--wb-token-spacing-modal-l-content-gap-2, 16px); + padding-top: var(--ax-token-spacing-spacing-16); } } From 75d60050446fd5e9a23415337c901f789d7af8bb Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Tue, 18 Aug 2026 17:35:27 +0200 Subject: [PATCH 02/21] fix(ui): resolve undefined custom properties in icon-switch and text-area MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit icon-switch: the base thumb background referenced a variable that never existed and is always overridden by the primary/secondary variant rule — dropped the dead declaration. text-area: the disabled background variable was consumed but never defined; define it as transparent (today's effective render) so the --ax-public-* override contract actually works. --- .../ui/src/components/switch/icon-switch/icon-switch.module.css | 1 - packages/ui/src/components/text-area/text-area.module.css | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/switch/icon-switch/icon-switch.module.css b/packages/ui/src/components/switch/icon-switch/icon-switch.module.css index a45808451..f6484a6b4 100644 --- a/packages/ui/src/components/switch/icon-switch/icon-switch.module.css +++ b/packages/ui/src/components/switch/icon-switch/icon-switch.module.css @@ -50,7 +50,6 @@ width: var(--ax-public-icon-switch-thumb-size); height: var(--ax-public-icon-switch-thumb-size); border-radius: 50%; - background-color: var(--ax-public-icon-switch-thumb-bg); transition: transform 0.1s ease-in-out; display: flex; align-items: center; diff --git a/packages/ui/src/components/text-area/text-area.module.css b/packages/ui/src/components/text-area/text-area.module.css index 1334872aa..2888055cb 100644 --- a/packages/ui/src/components/text-area/text-area.module.css +++ b/packages/ui/src/components/text-area/text-area.module.css @@ -8,6 +8,7 @@ --ax-public-textarea-root-border-color-error: var(--ax-input-stroke-primary-error); --ax-public-textarea-root-color-error: var(--ax-txt-error-default); --ax-public-textarea-root-color-disabled: var(--ax-txt-quaternary-default); + --ax-public-textarea-root-background-color-disabled: transparent; } @layer ui.component { From 05be0d70916f32eb7e03011678e775c3f7bd21c4 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Tue, 18 Aug 2026 17:37:19 +0200 Subject: [PATCH 03/21] fix(ai-studio): correct --ax-txt-secondary token name in log panel The real token is --ax-txt-secondary-default. The shorter name never existed, so the hardcoded gray fallback was what actually rendered. Remaining dead fallbacks in this file are dropped alongside. --- .../src/components/execution/log-panel.module.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/ai-studio/src/components/execution/log-panel.module.css b/apps/ai-studio/src/components/execution/log-panel.module.css index ec687cd99..7ce616041 100644 --- a/apps/ai-studio/src/components/execution/log-panel.module.css +++ b/apps/ai-studio/src/components/execution/log-panel.module.css @@ -8,7 +8,7 @@ background: var(--wb-app-bar-background); border: 0.0625rem solid var(--wb-app-bar-border-color); border-radius: var(--wb-app-bar-border-radius); - color: var(--ax-txt-primary-default, #151516); + color: var(--ax-txt-primary-default); display: flex; flex-direction: column; z-index: 10; @@ -90,7 +90,7 @@ } .badge--node_started { - color: var(--ax-txt-secondary, #888); + color: var(--ax-txt-secondary-default); } .badge--node_failed, @@ -99,7 +99,7 @@ } .badge--execution_cancelled { - color: var(--ax-txt-secondary, #888); + color: var(--ax-txt-secondary-default); } .node-id { @@ -159,6 +159,6 @@ } .event--highlighted { - background: color-mix(in srgb, var(--ax-colors-acc1-500, #1096e7), transparent 88%); - box-shadow: inset 0.1875rem 0 0 var(--ax-colors-acc1-500, #1096e7); + background: color-mix(in srgb, var(--ax-colors-acc1-500), transparent 88%); + box-shadow: inset 0.1875rem 0 0 var(--ax-colors-acc1-500); } From 9cefb741d77ee3279a4f30a4c077804d8a838adf Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Tue, 18 Aug 2026 17:37:21 +0200 Subject: [PATCH 04/21] style: drop dead fallbacks on system design tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every one of these var(--ax-…, fallback) fallbacks is unreachable — the token is defined in the shipped tokens.css, so the fallback never wins. They also mask exactly the typo class the new token-usage lint catches, which now rejects unannotated fallbacks on system tokens. --- .../disclaimer/disclaimer-modal.module.css | 32 +++++++------- .../components/visualize/renderers.module.css | 42 +++++++++---------- .../visualize/visualize-card.module.css | 20 ++++----- .../visualize/visualize-modal.module.css | 20 ++++----- .../src/components/switch/switch.module.css | 2 +- 5 files changed, 58 insertions(+), 58 deletions(-) diff --git a/apps/ai-studio/src/components/disclaimer/disclaimer-modal.module.css b/apps/ai-studio/src/components/disclaimer/disclaimer-modal.module.css index 24fe2fe2b..ae3cad078 100644 --- a/apps/ai-studio/src/components/disclaimer/disclaimer-modal.module.css +++ b/apps/ai-studio/src/components/disclaimer/disclaimer-modal.module.css @@ -16,11 +16,11 @@ max-width: 460px; padding: 2rem; font-family: var(--wb-font-family); - background: var(--ax-ui-bg-primary-default, #ffffff); - border: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); - border-radius: var(--wb-app-bar-border-radius, 0.75rem); + background: var(--ax-ui-bg-primary-default); + border: 0.0625rem solid var(--ax-ui-stroke-primary-default); + border-radius: var(--wb-app-bar-border-radius); box-shadow: 0 1.25rem 3.75rem rgba(0, 0, 0, 0.25); - color: var(--ax-txt-primary-default, #151516); + color: var(--ax-txt-primary-default); } .close { @@ -35,13 +35,13 @@ border: none; border-radius: 0.5rem; background: transparent; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--ax-txt-tertiary-default); cursor: pointer; } .close:hover { - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-txt-primary-default, #151516); + background: var(--ax-ui-bg-secondary-default); + color: var(--ax-txt-primary-default); } .heading { @@ -59,22 +59,22 @@ height: 2.25rem; flex-shrink: 0; border-radius: 0.6rem; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-colors-acc1-500, #1096e7); + background: var(--ax-ui-bg-secondary-default); + color: var(--ax-colors-acc1-500); } .title { margin: 0; font-size: 1.25rem; font-weight: 600; - color: var(--ax-txt-primary-default, #151516); + color: var(--ax-txt-primary-default); } .body { margin: 0 0 1.5rem; font-size: 0.92rem; line-height: 1.6; - color: var(--ax-txt-secondary-default, #4d5059); + color: var(--ax-txt-secondary-default); } .body p { @@ -86,7 +86,7 @@ } .body strong { - color: var(--ax-txt-primary-default, #151516); + color: var(--ax-txt-primary-default); font-weight: 600; } @@ -98,7 +98,7 @@ padding: 0.7rem 1rem; border: none; border-radius: 0.625rem; - background: var(--ax-colors-acc1-500, #1096e7); + background: var(--ax-colors-acc1-500); color: #ffffff; font-size: 0.95rem; font-weight: 600; @@ -107,7 +107,7 @@ } .cta:hover { - background: var(--ax-colors-acc1-600, #0477c5); + background: var(--ax-colors-acc1-600); } .reopen { @@ -123,11 +123,11 @@ border: 0.0625rem solid var(--wb-app-bar-border-color); border-radius: 50%; background: var(--wb-app-bar-background); - color: var(--ax-txt-secondary-default, #4d5059); + color: var(--ax-txt-secondary-default); cursor: pointer; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); } .reopen:hover { - color: var(--ax-colors-acc1-500, #1096e7); + color: var(--ax-colors-acc1-500); } diff --git a/apps/ai-studio/src/components/visualize/renderers.module.css b/apps/ai-studio/src/components/visualize/renderers.module.css index 6d64314e0..cfcbb0d3f 100644 --- a/apps/ai-studio/src/components/visualize/renderers.module.css +++ b/apps/ai-studio/src/components/visualize/renderers.module.css @@ -1,7 +1,7 @@ .markdown { font-size: 0.85rem; line-height: 1.6; - color: var(--ax-txt-primary-default, #151516); + color: var(--ax-txt-primary-default); overflow-wrap: anywhere; } @@ -40,7 +40,7 @@ } .markdown a { - color: var(--ax-colors-acc1-500, #1096e7); + color: var(--ax-colors-acc1-500); text-decoration: underline; } @@ -51,7 +51,7 @@ .markdown code { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.82em; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); + background: var(--ax-ui-bg-secondary-default); padding: 0.1em 0.35em; border-radius: 0.25rem; } @@ -59,7 +59,7 @@ .markdown pre { margin: 0.6em 0; padding: 0.7em 0.85em; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); + background: var(--ax-ui-bg-secondary-default); border-radius: 0.5rem; overflow-x: auto; } @@ -72,8 +72,8 @@ .markdown blockquote { margin: 0.6em 0; padding-left: 0.85em; - border-left: 0.1875rem solid var(--ax-ui-stroke-primary-default, #edeff3); - color: var(--ax-txt-secondary-default, #4d5059); + border-left: 0.1875rem solid var(--ax-ui-stroke-primary-default); + color: var(--ax-txt-secondary-default); } .rich p { @@ -89,13 +89,13 @@ .rich code { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.85em; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); + background: var(--ax-ui-bg-secondary-default); padding: 0.05em 0.3em; border-radius: 0.2rem; } .rich a { - color: var(--ax-colors-acc1-500, #1096e7); + color: var(--ax-colors-acc1-500); text-decoration: underline; } @@ -108,7 +108,7 @@ font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.8rem; line-height: 1.55; - color: var(--ax-txt-primary-default, #151516); + color: var(--ax-txt-primary-default); white-space: pre-wrap; overflow-wrap: anywhere; } @@ -117,7 +117,7 @@ font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.78rem; line-height: 1.5; - color: var(--ax-txt-primary-default, #151516); + color: var(--ax-txt-primary-default); } .json-row { @@ -132,18 +132,18 @@ .json-children { padding-left: 0.9rem; - border-left: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + border-left: 0.0625rem solid var(--ax-ui-stroke-primary-default); margin-left: 0.25rem; } .json-toggle { width: 0.7rem; flex-shrink: 0; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--ax-txt-tertiary-default); } .json-key { - color: var(--ax-colors-acc1-600, #0477c5); + color: var(--ax-colors-acc1-600); } .json-string { @@ -160,7 +160,7 @@ } .json-bracket { - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--ax-txt-tertiary-default); } .table { @@ -171,14 +171,14 @@ .table th, .table td { - border: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + border: 0.0625rem solid var(--ax-ui-stroke-primary-default); padding: 0.3em 0.5em; text-align: left; vertical-align: top; } .table th { - background: var(--ax-ui-bg-secondary-default, #f5f5f7); + background: var(--ax-ui-bg-secondary-default); font-weight: 600; position: sticky; top: 0; @@ -192,15 +192,15 @@ .stat-card { padding: 0.6rem 0.7rem; - border: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + border: 0.0625rem solid var(--ax-ui-stroke-primary-default); border-radius: 0.5rem; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); + background: var(--ax-ui-bg-secondary-default); } .stat-value { font-size: 1.05rem; font-weight: 600; - color: var(--ax-txt-primary-default, #151516); + color: var(--ax-txt-primary-default); overflow-wrap: anywhere; } @@ -209,13 +209,13 @@ font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.03em; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--ax-txt-tertiary-default); } .fallback-note { margin-bottom: 0.4rem; font-size: 0.72rem; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--ax-txt-tertiary-default); } .chart { diff --git a/apps/ai-studio/src/components/visualize/visualize-card.module.css b/apps/ai-studio/src/components/visualize/visualize-card.module.css index 90a45315a..0fddfb7d8 100644 --- a/apps/ai-studio/src/components/visualize/visualize-card.module.css +++ b/apps/ai-studio/src/components/visualize/visualize-card.module.css @@ -2,10 +2,10 @@ width: 100%; margin-top: 0.4rem; padding-top: 0.5rem; - border-top: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + border-top: 0.0625rem solid var(--ax-ui-stroke-primary-default); font-family: var(--wb-font-family); text-align: left; - color: var(--ax-txt-primary-default, #151516); + color: var(--ax-txt-primary-default); &, * { @@ -30,8 +30,8 @@ font-size: 0.64rem; padding: 0.15rem 0.4rem; border-radius: 0.3rem; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-txt-tertiary-default, #6f7480); + background: var(--ax-ui-bg-secondary-default); + color: var(--ax-txt-tertiary-default); } .actions { @@ -50,7 +50,7 @@ border: none; border-radius: 0.3rem; background: transparent; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--ax-txt-tertiary-default); cursor: pointer; } @@ -60,8 +60,8 @@ } .action:hover { - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-txt-primary-default, #151516); + background: var(--ax-ui-bg-secondary-default); + color: var(--ax-txt-primary-default); } .action:disabled { @@ -93,7 +93,7 @@ .empty-icon { width: 1.6rem; height: 1.6rem; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--ax-txt-tertiary-default); opacity: 0.55; } @@ -101,7 +101,7 @@ margin: 0; font-size: 0.74rem; line-height: 1.45; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--ax-txt-tertiary-default); } .dots { @@ -114,7 +114,7 @@ width: 0.45rem; height: 0.45rem; border-radius: 50%; - background: var(--ax-colors-acc1-500, #1096e7); + background: var(--ax-colors-acc1-500); animation: md-pulse 1s ease-in-out infinite; } diff --git a/apps/ai-studio/src/components/visualize/visualize-modal.module.css b/apps/ai-studio/src/components/visualize/visualize-modal.module.css index 7850c1acc..b92dc117f 100644 --- a/apps/ai-studio/src/components/visualize/visualize-modal.module.css +++ b/apps/ai-studio/src/components/visualize/visualize-modal.module.css @@ -16,11 +16,11 @@ flex-direction: column; width: min(56rem, 92vw); max-height: 86vh; - background: var(--ax-ui-bg-primary-default, #ffffff); - border: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + background: var(--ax-ui-bg-primary-default); + border: 0.0625rem solid var(--ax-ui-stroke-primary-default); border-radius: 0.9rem; box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.4); - color: var(--ax-txt-primary-default, #151516); + color: var(--ax-txt-primary-default); overflow: hidden; &, @@ -34,7 +34,7 @@ align-items: center; gap: 0.5rem; padding: 0.75rem 1rem; - border-bottom: 0.0625rem solid var(--ax-ui-stroke-primary-default, #edeff3); + border-bottom: 0.0625rem solid var(--ax-ui-stroke-primary-default); } .title { @@ -47,8 +47,8 @@ font-size: 0.7rem; padding: 0.15rem 0.5rem; border-radius: 0.3rem; - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-txt-tertiary-default, #6f7480); + background: var(--ax-ui-bg-secondary-default); + color: var(--ax-txt-tertiary-default); } .actions { @@ -66,7 +66,7 @@ border: none; border-radius: 0.4rem; background: transparent; - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--ax-txt-tertiary-default); cursor: pointer; } @@ -76,8 +76,8 @@ } .action:hover { - background: var(--ax-ui-bg-secondary-default, #f5f5f7); - color: var(--ax-txt-primary-default, #151516); + background: var(--ax-ui-bg-secondary-default); + color: var(--ax-txt-primary-default); } .body { @@ -87,6 +87,6 @@ } .loading { - color: var(--ax-txt-tertiary-default, #6f7480); + color: var(--ax-txt-tertiary-default); font-size: 0.85rem; } diff --git a/packages/ui/src/components/switch/switch.module.css b/packages/ui/src/components/switch/switch.module.css index 6d7451963..82f420bcc 100644 --- a/packages/ui/src/components/switch/switch.module.css +++ b/packages/ui/src/components/switch/switch.module.css @@ -8,7 +8,7 @@ --ax-public-track-selected-color: var(--ax-ui-bg-tertiary-selected); --ax-public-thumb-no-selected-color: var(--ax-nav-button-icon-primary-default); --ax-public-thumb-disabled-color: var(--ax-nav-button-icon-primary-disabled); - --ax-public-thumb-selected-color: var(--ax-colors-gray-100,); /* missing token */ + --ax-public-thumb-selected-color: var(--ax-colors-gray-100); /* missing token */ --ax-public-switch-focus-border-color: var(--ax-txt-primary-default); --ax-public-switch-border-size: 0.0625rem; From 9db19290bff50fd2d8963ac47f6fe0f9bde7c59b Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Tue, 18 Aug 2026 17:37:36 +0200 Subject: [PATCH 05/21] chore(tokens): add token-usage lint guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lint-token-usage.mjs validates every var(--…) in packages/*/src and apps/*/src against the names that actually exist (tokens dist, source definitions incl. inline styles and setProperty, the provisional registry, and a runtime allowlist). Unknown names and unannotated fallbacks on system tokens are errors. Full-scan and per-file modes, self-builds dist when missing, and reports the var(--ax-*) usage count as the DS 2.0 migration meter. --- packages/tokens/README.md | 27 ++++ packages/tokens/package.json | 1 + packages/tokens/scripts/lint-token-usage.mjs | 154 +++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 packages/tokens/scripts/lint-token-usage.mjs diff --git a/packages/tokens/README.md b/packages/tokens/README.md index 09496385d..6cf0d63ef 100644 --- a/packages/tokens/README.md +++ b/packages/tokens/README.md @@ -44,4 +44,31 @@ the repo root. pnpm --filter @workflowbuilder/ui-tokens test ``` +## Token usage lint + +```bash +pnpm --filter @workflowbuilder/ui-tokens lint:usage +``` + +`scripts/lint-token-usage.mjs` validates every `var(--…)` in `packages/*/src` and +`apps/*/src` (docs excluded) against the names that actually exist: the built token dist, +custom properties defined in sources (CSS declarations, inline `'--x':` styles and +`setProperty` calls in TS/TSX), the provisional registry, and a short allowlist of names +injected at runtime by third-party libraries (e.g. Base UI's `--anchor-width`). + +Two rules, both errors: + +1. `var(--name)` where `--name` is defined nowhere — catches prefix typos and drift after + a token export update. +2. A fallback on a system token (`var(--wb-…, x)` / `var(--ax-…, x)`) — fallbacks silently + mask rule-1 typos. When a fallback is genuinely needed, annotate the line with + `/* fallback-ok: reason */`; the lint prints an inventory of all annotated lines. + +Runs in CI (`pr-check.yml`, after the tokens build) and per-file from lint-staged on +commit. If `dist/` is missing the script builds it first. + +`tokens-provisional.json` (optional, next to `tokens.json`) registers tokens that the +design file does not export yet: `{ "tokens": { "--wb-…": "" } }`. Names listed +there count as defined; the registry is the to-remove list once the real export lands. + Vitest unit tests (currently `src/to-file-name.spec.ts` and `src/manifest.spec.ts`). diff --git a/packages/tokens/package.json b/packages/tokens/package.json index 7d7fa2012..069f8b971 100644 --- a/packages/tokens/package.json +++ b/packages/tokens/package.json @@ -11,6 +11,7 @@ "prepare": "tsx src/index.ts", "typecheck": "tsc --noEmit", "lint": "eslint", + "lint:usage": "node scripts/lint-token-usage.mjs", "test": "vitest run" }, "devDependencies": { diff --git a/packages/tokens/scripts/lint-token-usage.mjs b/packages/tokens/scripts/lint-token-usage.mjs new file mode 100644 index 000000000..ca9f37c0e --- /dev/null +++ b/packages/tokens/scripts/lint-token-usage.mjs @@ -0,0 +1,154 @@ +/** + * Token-usage lint. + * + * Validates every `var(--name)` in app/package sources against the set of + * names that actually exist somewhere: the built token dist, custom + * properties defined in source files (CSS or inline styles / setProperty in + * TS/TSX), the provisional-token registry, and runtime names injected by + * third-party libraries. + * + * Rules: + * 1. `var(--name)` where `--name` is defined nowhere -> error. + * 2. `var(--wb-… , fallback)` / `var(--ax-… , fallback)` -> error unless the + * line carries a `/* fallback-ok: reason *\/` annotation. Fallbacks on + * system tokens mask exactly the typos rule 1 exists to catch. + * + * Usage: + * node scripts/lint-token-usage.mjs # full scan (CI) + * node scripts/lint-token-usage.mjs --files a.css b.tsx # staged files only + */ +import { spawnSync } from 'node:child_process'; +import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'; +import path from 'node:path'; +import process from 'node:process'; +import { fileURLToPath } from 'node:url'; + +const TOKENS_DIRECTORY = path.resolve(fileURLToPath(import.meta.url), '../..'); +const REPO_ROOT = path.resolve(TOKENS_DIRECTORY, '../..'); +const DISTRIBUTION_DIRECTORY = path.join(TOKENS_DIRECTORY, 'dist'); +const PROVISIONAL_FILE = path.join(TOKENS_DIRECTORY, 'tokens-provisional.json'); + +// Custom properties set at runtime by third-party libraries. +const EXTERNAL_NAMES = new Set([ + // Base UI positioner writes the anchor dimensions onto the popup element. + '--anchor-width', + '--anchor-height', +]); + +const SCAN_ROOTS = ['packages', 'apps'] + .flatMap((group) => { + const groupDirectory = path.join(REPO_ROOT, group); + return readdirSync(groupDirectory).map((name) => path.join(groupDirectory, name, 'src')); + }) + .filter((directory) => !directory.includes(`apps${path.sep}docs`) && existsSync(directory)); + +const SOURCE_EXTENSIONS = new Set(['.css', '.scss', '.ts', '.tsx']); + +const USE_RE = /var\(\s*(--[A-Za-z0-9_-]+)/g; +const CSS_DEFINITION_RE = /(--[A-Za-z0-9_-]+)\s*:/g; +const JS_DEFINITION_RE = /['"](--[A-Za-z0-9_-]+)['"]\s*[:,]/g; +const SET_PROPERTY_RE = /setProperty\(\s*['"](--[A-Za-z0-9_-]+)['"]/g; +const SYSTEM_FALLBACK_RE = /var\(\s*(--(?:wb|ax)-[A-Za-z0-9_-]+)\s*,/g; +const FALLBACK_OK_RE = /\/\*\s*fallback-ok:\s*[^*]+\*\//; + +function walk(directory, out = []) { + for (const entry of readdirSync(directory)) { + const full = path.join(directory, entry); + if (statSync(full).isDirectory()) walk(full, out); + else if (SOURCE_EXTENSIONS.has(path.extname(full))) out.push(full); + } + return out; +} + +function collectMatches(regex, text, into) { + for (const match of text.matchAll(regex)) into.add(match[1]); +} + +function ensureDistribution() { + if (existsSync(DISTRIBUTION_DIRECTORY) && readdirSync(DISTRIBUTION_DIRECTORY).length > 0) return; + console.log('lint-token-usage: tokens dist missing, building it first…'); + const result = spawnSync('pnpm', ['run', 'build'], { cwd: TOKENS_DIRECTORY, stdio: 'inherit' }); + if (result.status !== 0) { + throw new Error('lint-token-usage: tokens build failed, cannot lint.'); + } +} + +function loadProvisionalNames() { + if (!existsSync(PROVISIONAL_FILE)) return new Set(); + const registry = JSON.parse(readFileSync(PROVISIONAL_FILE, 'utf8')); + return new Set(Object.keys(registry.tokens ?? {})); +} + +ensureDistribution(); + +const allSourceFiles = SCAN_ROOTS.flatMap((directory) => walk(directory)); + +const defined = new Set(EXTERNAL_NAMES); +for (const name of loadProvisionalNames()) defined.add(name); +for (const file of walk(DISTRIBUTION_DIRECTORY).filter((f) => f.endsWith('.css'))) { + collectMatches(CSS_DEFINITION_RE, readFileSync(file, 'utf8'), defined); +} +for (const file of allSourceFiles) { + const text = readFileSync(file, 'utf8'); + if (file.endsWith('.css') || file.endsWith('.scss')) { + collectMatches(CSS_DEFINITION_RE, text, defined); + } else { + collectMatches(JS_DEFINITION_RE, text, defined); + collectMatches(SET_PROPERTY_RE, text, defined); + } +} + +const filesArgumentIndex = process.argv.indexOf('--files'); +const filesToLint = + filesArgumentIndex === -1 + ? allSourceFiles + : process.argv + .slice(filesArgumentIndex + 1) + .map((file) => path.resolve(file)) + .filter((file) => SOURCE_EXTENSIONS.has(path.extname(file)) && existsSync(file)); + +const errors = []; +const fallbackOkInventory = []; +let axUsageCount = 0; + +for (const file of filesToLint) { + const relative = path.relative(REPO_ROOT, file); + const lines = readFileSync(file, 'utf8').split('\n'); + for (const [index, line] of lines.entries()) { + const location = `${relative}:${index + 1}`; + for (const match of line.matchAll(USE_RE)) { + const name = match[1]; + if (name.startsWith('--ax-')) axUsageCount += 1; + if (!defined.has(name)) { + errors.push( + `${location} unknown custom property ${name} (defined nowhere: not in tokens dist, sources, or the provisional registry)`, + ); + } + } + for (const match of line.matchAll(SYSTEM_FALLBACK_RE)) { + if (FALLBACK_OK_RE.test(line)) { + fallbackOkInventory.push(`${location} ${match[1]}`); + } else { + errors.push( + `${location} fallback on system token ${match[1]} — fallbacks mask typos; drop it or annotate the line with /* fallback-ok: reason */`, + ); + } + } + } +} + +if (fallbackOkInventory.length > 0) { + console.log(`fallback-ok inventory (${fallbackOkInventory.length}):`); + for (const entry of fallbackOkInventory) console.log(` ${entry}`); +} +console.log( + `lint-token-usage: ${filesToLint.length} files, ${axUsageCount} var(--ax-*) usages remaining (DS 2.0 migration meter).`, +); + +if (errors.length > 0) { + console.error(`\n${errors.length} token-usage error(s):`); + for (const error of errors) console.error(` ${error}`); + process.exitCode = 1; +} else { + console.log('lint-token-usage: OK'); +} From 5a85b658cadf76b90e79e536b08280e98ecad821 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Tue, 18 Aug 2026 17:37:37 +0200 Subject: [PATCH 06/21] ci: run the token-usage lint in pr-check and lint-staged CI runs the full scan in the ui job right after pnpm build:ui, so the dist it validates against is the one just built. lint-staged runs the per-file mode on staged css/ts files; the script path is anchored to the repo root because workspace lint-staged configs re-export the root config and run commands from their own directory. --- .github/workflows/pr-check.yml | 3 +++ lint-staged.config.mjs | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index f1b4dc3c2..6ef6cd7d3 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -145,3 +145,6 @@ jobs: # build:ui builds @workflowbuilder/ui-tokens first, then ui, then runs # the check:built-css guard. run: pnpm build:ui + + - name: Token usage lint + run: pnpm --filter @workflowbuilder/ui-tokens lint:usage diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs index c565d24b7..d5a4c9c6b 100644 --- a/lint-staged.config.mjs +++ b/lint-staged.config.mjs @@ -1,7 +1,18 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +// Workspace lint-staged configs re-export this file and run commands from +// their own directory, so the script path must stay anchored to the repo root. +const tokenUsageLint = path.join( + path.dirname(fileURLToPath(import.meta.url)), + 'packages/tokens/scripts/lint-token-usage.mjs', +); + /** * @type {import('lint-staged').Configuration} */ export default { '*.{ts,tsx,js,json,css,astro,md,mdx}': (files) => `prettier --write --log-level=silent ${files.join(' ')}`, '*.{ts,tsx}': [(files) => `eslint --max-warnings=0 --fix ${files.join(' ')}`, () => `tsc --noEmit`], + '*.{css,scss,ts,tsx}': (files) => `node ${tokenUsageLint} --files ${files.join(' ')}`, }; From 5a42d521698396340fab03f758ce6159977b8647 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Tue, 18 Aug 2026 17:37:39 +0200 Subject: [PATCH 07/21] docs(sdk): document the --wb-* prefix ownership convention --- packages/sdk/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 5af2dfc61..c80ad5fd1 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -214,6 +214,8 @@ Available `--wb-*` tokens: `--wb-background-color`, `--wb-font-family`, `--wb-tr Deeper color and spacing customization (palette, semantic UI tokens) goes through the `--ax-*` token layer from `@workflowbuilder/ui`. Full guide: [Design system and customization](https://www.workflowbuilder.io/docs/overview/features/design-system-and-customization/). +**Prefix ownership.** The `--wb-*` namespace is shared by two owners, split by convention: names with a design-domain segment (`--wb-ui-*`, `--wb-components-*`, `--wb-canvas-*`, `--wb-colors-*`, `--wb-space-*`, `--wb-radius-*`, `--wb-size-*`, `--wb-font-size-*`, `--wb-shadow-*`) belong to the design-token export and will arrive with Design System 2.0; everything else under `--wb-*` (like the theming variables above) is defined by the SDK itself. The two sets do not collide because exported names always carry a domain segment. Every `var(--…)` in this repo is validated in CI against the set of names that actually exist (`packages/tokens/scripts/lint-token-usage.mjs`). + ## CSS — global resets `@workflowbuilder/sdk/style.css` contains a small set of global resets, including: From 35e4ea5710b7899d0bb97439d00e25247fcd9ec8 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 11:44:41 +0200 Subject: [PATCH 08/21] refactor(tokens): replace the custom token-usage lint with stylelint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hand-rolled regex scanner (~160 lines, growing with every review finding: comments, multi-line var(), @property, template literals) becomes configuration: csstools/value-no-unknown-custom-properties validates every var(--…) in workspace CSS against definitions collected by a small importFrom module (token dist + all source CSS + a short runtime-defined list), and a ~40-line local stylelint rule keeps the fallback ban on system tokens, with exceptions via the standard stylelint-disable-next-line comment and a mandatory reason. A real CSS parser fixes two scanner bugs for free (var() inside comments no longer flags, multi-line var() no longer escapes) and apps/docs is now excluded consistently in CI, lint-staged, and manual runs via ignoreFiles. The DS 2.0 migration meter stays as a one-liner in the CI step. Lockfile note: adding the stylelint devDependencies also re-resolves stale transitive typescript peer entries (5.9.3 -> 5.6.3) that no declared range permits — a one-time correction; declared ranges are all ~5.6.3. --- .github/workflows/pr-check.yml | 11 +- .stylelintrc.json | 11 + lint-staged.config.mjs | 12 +- package.json | 7 +- packages/sdk/README.md | 2 +- packages/tokens/README.md | 51 +- packages/tokens/package.json | 1 - packages/tokens/scripts/lint-token-usage.mjs | 154 ----- pnpm-lock.yaml | 635 ++++++++++++++++-- tools/stylelint/custom-properties.mjs | 46 ++ tools/stylelint/no-system-token-fallbacks.mjs | 48 ++ 11 files changed, 744 insertions(+), 234 deletions(-) create mode 100644 .stylelintrc.json delete mode 100644 packages/tokens/scripts/lint-token-usage.mjs create mode 100644 tools/stylelint/custom-properties.mjs create mode 100644 tools/stylelint/no-system-token-fallbacks.mjs diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 6ef6cd7d3..727a90764 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -4,8 +4,9 @@ name: PR Check # is safe to merge. Focus on the published packages @workflowbuilder/sdk and # @workflowbuilder/ui (plus its private @workflowbuilder/ui-tokens build) + # global format consistency. apps/docs has its own path-filtered workflow -# (pr-check-docs.yml); demo and ai-studio are not checked here — they're -# internal and have their own broken-state tolerances. +# (pr-check-docs.yml); demo and ai-studio are not built here — they're +# internal and have their own broken-state tolerances — but their CSS goes +# through the style lint below like every other workspace's. on: pull_request: @@ -146,5 +147,7 @@ jobs: # the check:built-css guard. run: pnpm build:ui - - name: Token usage lint - run: pnpm --filter @workflowbuilder/ui-tokens lint:usage + - name: Style lint (token usage + fallbacks) + run: | + pnpm lint:styles + echo "DS 2.0 migration meter: $(grep -rho 'var(--ax-' packages/*/src apps/*/src --include='*.css' --include='*.tsx' --include='*.ts' | wc -l) var(--ax-*) usages remaining" diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 000000000..db2da2dec --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,11 @@ +{ + "plugins": ["stylelint-value-no-unknown-custom-properties", "./tools/stylelint/no-system-token-fallbacks.mjs"], + "ignoreFiles": ["**/node_modules/**", "**/dist/**", "apps/docs/**"], + "rules": { + "csstools/value-no-unknown-custom-properties": [ + true, + { "importFrom": ["./tools/stylelint/custom-properties.mjs"] } + ], + "wb/no-system-token-fallbacks": true + } +} diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs index d5a4c9c6b..82e98add6 100644 --- a/lint-staged.config.mjs +++ b/lint-staged.config.mjs @@ -1,18 +1,8 @@ -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -// Workspace lint-staged configs re-export this file and run commands from -// their own directory, so the script path must stay anchored to the repo root. -const tokenUsageLint = path.join( - path.dirname(fileURLToPath(import.meta.url)), - 'packages/tokens/scripts/lint-token-usage.mjs', -); - /** * @type {import('lint-staged').Configuration} */ export default { '*.{ts,tsx,js,json,css,astro,md,mdx}': (files) => `prettier --write --log-level=silent ${files.join(' ')}`, '*.{ts,tsx}': [(files) => `eslint --max-warnings=0 --fix ${files.join(' ')}`, () => `tsc --noEmit`], - '*.{css,scss,ts,tsx}': (files) => `node ${tokenUsageLint} --files ${files.join(' ')}`, + '*.css': (files) => `stylelint ${files.join(' ')}`, }; diff --git a/package.json b/package.json index 9f35e5198..e2c82072a 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,11 @@ "preview-build": "pnpm --filter @workflow-builder/demo preview-build", "lint": "pnpm -r lint", "lint:fix": "pnpm -r lint:fix", + "lint:styles": "stylelint \"packages/*/src/**/*.css\" \"apps/*/src/**/*.css\"", "format": "prettier --write --log-level silent \"**/*.+(css|ts|tsx|json|md|mdx|astro)\"", "typecheck": "pnpm -r typecheck", "test": "pnpm -r test", - "check": "pnpm lint && pnpm typecheck && pnpm format", + "check": "pnpm lint && pnpm lint:styles && pnpm typecheck && pnpm format", "pre-commit": "lint-staged", "pre-push": "pnpm format", "prepare": "husky" @@ -49,8 +50,12 @@ "jsdom": "^26.0.0", "knip": "^5.56.0", "lint-staged": "^16.0.0", + "postcss": "^8.5.6", + "postcss-value-parser": "^4.2.0", "prettier": "^3.3.3", "prettier-plugin-astro": "^0.14.1", + "stylelint": "^17.14.1", + "stylelint-value-no-unknown-custom-properties": "^6.1.1", "tsx": "^4.19.3", "typescript": "~5.6.3", "typescript-eslint": "^8.22.0" diff --git a/packages/sdk/README.md b/packages/sdk/README.md index c80ad5fd1..7ed4d2bb9 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -214,7 +214,7 @@ Available `--wb-*` tokens: `--wb-background-color`, `--wb-font-family`, `--wb-tr Deeper color and spacing customization (palette, semantic UI tokens) goes through the `--ax-*` token layer from `@workflowbuilder/ui`. Full guide: [Design system and customization](https://www.workflowbuilder.io/docs/overview/features/design-system-and-customization/). -**Prefix ownership.** The `--wb-*` namespace is shared by two owners, split by convention: names with a design-domain segment (`--wb-ui-*`, `--wb-components-*`, `--wb-canvas-*`, `--wb-colors-*`, `--wb-space-*`, `--wb-radius-*`, `--wb-size-*`, `--wb-font-size-*`, `--wb-shadow-*`) belong to the design-token export and will arrive with Design System 2.0; everything else under `--wb-*` (like the theming variables above) is defined by the SDK itself. The two sets do not collide because exported names always carry a domain segment. Every `var(--…)` in this repo is validated in CI against the set of names that actually exist (`packages/tokens/scripts/lint-token-usage.mjs`). +**Prefix ownership.** The `--wb-*` namespace is shared by two owners, split by convention: names with a design-domain segment (`--wb-ui-*`, `--wb-components-*`, `--wb-canvas-*`, `--wb-colors-*`, `--wb-space-*`, `--wb-radius-*`, `--wb-size-*`, `--wb-font-size-*`, `--wb-shadow-*`) belong to the design-token export and will arrive with Design System 2.0; everything else under `--wb-*` (like the theming variables above) is defined by the SDK itself. The two sets do not collide because exported names always carry a domain segment. Every `var(--…)` in workspace CSS is validated in CI against the set of names that actually exist (stylelint, see `packages/tokens/README.md`). ## CSS — global resets diff --git a/packages/tokens/README.md b/packages/tokens/README.md index 6cf0d63ef..a5e1e78b5 100644 --- a/packages/tokens/README.md +++ b/packages/tokens/README.md @@ -44,31 +44,36 @@ the repo root. pnpm --filter @workflowbuilder/ui-tokens test ``` +Vitest unit tests (currently `src/to-file-name.spec.ts` and `src/manifest.spec.ts`). + ## Token usage lint ```bash -pnpm --filter @workflowbuilder/ui-tokens lint:usage +pnpm lint:styles ``` -`scripts/lint-token-usage.mjs` validates every `var(--…)` in `packages/*/src` and -`apps/*/src` (docs excluded) against the names that actually exist: the built token dist, -custom properties defined in sources (CSS declarations, inline `'--x':` styles and -`setProperty` calls in TS/TSX), the provisional registry, and a short allowlist of names -injected at runtime by third-party libraries (e.g. Base UI's `--anchor-width`). - -Two rules, both errors: - -1. `var(--name)` where `--name` is defined nowhere — catches prefix typos and drift after - a token export update. -2. A fallback on a system token (`var(--wb-…, x)` / `var(--ax-…, x)`) — fallbacks silently - mask rule-1 typos. When a fallback is genuinely needed, annotate the line with - `/* fallback-ok: reason */`; the lint prints an inventory of all annotated lines. - -Runs in CI (`pr-check.yml`, after the tokens build) and per-file from lint-staged on -commit. If `dist/` is missing the script builds it first. - -`tokens-provisional.json` (optional, next to `tokens.json`) registers tokens that the -design file does not export yet: `{ "tokens": { "--wb-…": "" } }`. Names listed -there count as defined; the registry is the to-remove list once the real export lands. - -Vitest unit tests (currently `src/to-file-name.spec.ts` and `src/manifest.spec.ts`). +Stylelint (root `.stylelintrc.json`) validates workspace CSS with two rules, +both errors: + +1. `csstools/value-no-unknown-custom-properties` — every `var(--name)` must + resolve to a definition that actually exists. The definition set is built + by `tools/stylelint/custom-properties.mjs`: the token dist, every + custom-property declaration in `packages/*/src` and `apps/*/src` CSS, and a + short inline list of names set at runtime from JS (Base UI's + `--anchor-width`, the node-as-port and log-panel inline styles). Catches + prefix typos and drift after a token export update. +2. `wb/no-system-token-fallbacks` (`tools/stylelint/no-system-token-fallbacks.mjs`) + — no fallbacks on `var(--wb-…)` / `var(--ax-…)`; a fallback silently masks + exactly the typos rule 1 exists to catch. Genuine exceptions use the + standard mechanism with a mandatory reason: + `/* stylelint-disable-next-line wb/no-system-token-fallbacks -- reason */`. + +Runs in CI (`pr-check.yml`, after `pnpm build:ui`), per-file from lint-staged +on commit, and as part of `pnpm check`. Requires a built `dist/` (created by +`pnpm install` and `pnpm build:ui`). `apps/docs` is excluded via `ignoreFiles`, +consistently in every mode. + +Tokens the design file does not export yet are defined provisionally right in +the stylesheet that consumes them (with a comment marking them for removal +once the export lands) — the collector picks source-CSS definitions up +automatically. diff --git a/packages/tokens/package.json b/packages/tokens/package.json index 069f8b971..7d7fa2012 100644 --- a/packages/tokens/package.json +++ b/packages/tokens/package.json @@ -11,7 +11,6 @@ "prepare": "tsx src/index.ts", "typecheck": "tsc --noEmit", "lint": "eslint", - "lint:usage": "node scripts/lint-token-usage.mjs", "test": "vitest run" }, "devDependencies": { diff --git a/packages/tokens/scripts/lint-token-usage.mjs b/packages/tokens/scripts/lint-token-usage.mjs deleted file mode 100644 index ca9f37c0e..000000000 --- a/packages/tokens/scripts/lint-token-usage.mjs +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Token-usage lint. - * - * Validates every `var(--name)` in app/package sources against the set of - * names that actually exist somewhere: the built token dist, custom - * properties defined in source files (CSS or inline styles / setProperty in - * TS/TSX), the provisional-token registry, and runtime names injected by - * third-party libraries. - * - * Rules: - * 1. `var(--name)` where `--name` is defined nowhere -> error. - * 2. `var(--wb-… , fallback)` / `var(--ax-… , fallback)` -> error unless the - * line carries a `/* fallback-ok: reason *\/` annotation. Fallbacks on - * system tokens mask exactly the typos rule 1 exists to catch. - * - * Usage: - * node scripts/lint-token-usage.mjs # full scan (CI) - * node scripts/lint-token-usage.mjs --files a.css b.tsx # staged files only - */ -import { spawnSync } from 'node:child_process'; -import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'; -import path from 'node:path'; -import process from 'node:process'; -import { fileURLToPath } from 'node:url'; - -const TOKENS_DIRECTORY = path.resolve(fileURLToPath(import.meta.url), '../..'); -const REPO_ROOT = path.resolve(TOKENS_DIRECTORY, '../..'); -const DISTRIBUTION_DIRECTORY = path.join(TOKENS_DIRECTORY, 'dist'); -const PROVISIONAL_FILE = path.join(TOKENS_DIRECTORY, 'tokens-provisional.json'); - -// Custom properties set at runtime by third-party libraries. -const EXTERNAL_NAMES = new Set([ - // Base UI positioner writes the anchor dimensions onto the popup element. - '--anchor-width', - '--anchor-height', -]); - -const SCAN_ROOTS = ['packages', 'apps'] - .flatMap((group) => { - const groupDirectory = path.join(REPO_ROOT, group); - return readdirSync(groupDirectory).map((name) => path.join(groupDirectory, name, 'src')); - }) - .filter((directory) => !directory.includes(`apps${path.sep}docs`) && existsSync(directory)); - -const SOURCE_EXTENSIONS = new Set(['.css', '.scss', '.ts', '.tsx']); - -const USE_RE = /var\(\s*(--[A-Za-z0-9_-]+)/g; -const CSS_DEFINITION_RE = /(--[A-Za-z0-9_-]+)\s*:/g; -const JS_DEFINITION_RE = /['"](--[A-Za-z0-9_-]+)['"]\s*[:,]/g; -const SET_PROPERTY_RE = /setProperty\(\s*['"](--[A-Za-z0-9_-]+)['"]/g; -const SYSTEM_FALLBACK_RE = /var\(\s*(--(?:wb|ax)-[A-Za-z0-9_-]+)\s*,/g; -const FALLBACK_OK_RE = /\/\*\s*fallback-ok:\s*[^*]+\*\//; - -function walk(directory, out = []) { - for (const entry of readdirSync(directory)) { - const full = path.join(directory, entry); - if (statSync(full).isDirectory()) walk(full, out); - else if (SOURCE_EXTENSIONS.has(path.extname(full))) out.push(full); - } - return out; -} - -function collectMatches(regex, text, into) { - for (const match of text.matchAll(regex)) into.add(match[1]); -} - -function ensureDistribution() { - if (existsSync(DISTRIBUTION_DIRECTORY) && readdirSync(DISTRIBUTION_DIRECTORY).length > 0) return; - console.log('lint-token-usage: tokens dist missing, building it first…'); - const result = spawnSync('pnpm', ['run', 'build'], { cwd: TOKENS_DIRECTORY, stdio: 'inherit' }); - if (result.status !== 0) { - throw new Error('lint-token-usage: tokens build failed, cannot lint.'); - } -} - -function loadProvisionalNames() { - if (!existsSync(PROVISIONAL_FILE)) return new Set(); - const registry = JSON.parse(readFileSync(PROVISIONAL_FILE, 'utf8')); - return new Set(Object.keys(registry.tokens ?? {})); -} - -ensureDistribution(); - -const allSourceFiles = SCAN_ROOTS.flatMap((directory) => walk(directory)); - -const defined = new Set(EXTERNAL_NAMES); -for (const name of loadProvisionalNames()) defined.add(name); -for (const file of walk(DISTRIBUTION_DIRECTORY).filter((f) => f.endsWith('.css'))) { - collectMatches(CSS_DEFINITION_RE, readFileSync(file, 'utf8'), defined); -} -for (const file of allSourceFiles) { - const text = readFileSync(file, 'utf8'); - if (file.endsWith('.css') || file.endsWith('.scss')) { - collectMatches(CSS_DEFINITION_RE, text, defined); - } else { - collectMatches(JS_DEFINITION_RE, text, defined); - collectMatches(SET_PROPERTY_RE, text, defined); - } -} - -const filesArgumentIndex = process.argv.indexOf('--files'); -const filesToLint = - filesArgumentIndex === -1 - ? allSourceFiles - : process.argv - .slice(filesArgumentIndex + 1) - .map((file) => path.resolve(file)) - .filter((file) => SOURCE_EXTENSIONS.has(path.extname(file)) && existsSync(file)); - -const errors = []; -const fallbackOkInventory = []; -let axUsageCount = 0; - -for (const file of filesToLint) { - const relative = path.relative(REPO_ROOT, file); - const lines = readFileSync(file, 'utf8').split('\n'); - for (const [index, line] of lines.entries()) { - const location = `${relative}:${index + 1}`; - for (const match of line.matchAll(USE_RE)) { - const name = match[1]; - if (name.startsWith('--ax-')) axUsageCount += 1; - if (!defined.has(name)) { - errors.push( - `${location} unknown custom property ${name} (defined nowhere: not in tokens dist, sources, or the provisional registry)`, - ); - } - } - for (const match of line.matchAll(SYSTEM_FALLBACK_RE)) { - if (FALLBACK_OK_RE.test(line)) { - fallbackOkInventory.push(`${location} ${match[1]}`); - } else { - errors.push( - `${location} fallback on system token ${match[1]} — fallbacks mask typos; drop it or annotate the line with /* fallback-ok: reason */`, - ); - } - } - } -} - -if (fallbackOkInventory.length > 0) { - console.log(`fallback-ok inventory (${fallbackOkInventory.length}):`); - for (const entry of fallbackOkInventory) console.log(` ${entry}`); -} -console.log( - `lint-token-usage: ${filesToLint.length} files, ${axUsageCount} var(--ax-*) usages remaining (DS 2.0 migration meter).`, -); - -if (errors.length > 0) { - console.error(`\n${errors.length} token-usage error(s):`); - for (const error of errors) console.error(` ${error}`); - process.exitCode = 1; -} else { - console.log('lint-token-usage: OK'); -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4f4d9231c..cd9df70b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,12 +86,24 @@ importers: lint-staged: specifier: ^16.0.0 version: 16.0.0 + postcss: + specifier: ^8.5.6 + version: 8.5.6 + postcss-value-parser: + specifier: ^4.2.0 + version: 4.2.0 prettier: specifier: ^3.3.3 version: 3.8.1 prettier-plugin-astro: specifier: ^0.14.1 version: 0.14.1 + stylelint: + specifier: ^17.14.1 + version: 17.14.1(typescript@5.6.3) + stylelint-value-no-unknown-custom-properties: + specifier: ^6.1.1 + version: 6.1.1(stylelint@17.14.1(typescript@5.6.3)) tsx: specifier: ^4.19.3 version: 4.21.0 @@ -527,7 +539,7 @@ importers: version: 4.1.0 i18next: specifier: ^24.0.0 - version: 24.2.3(typescript@5.9.3) + version: 24.2.3(typescript@5.6.3) i18next-browser-languagedetector: specifier: ^8.0.0 version: 8.0.5 @@ -548,7 +560,7 @@ importers: version: 19.1.0(react@19.1.0) react-i18next: specifier: ^15.0.0 - version: 15.4.1(i18next@24.2.3(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.4.1(i18next@24.2.3(typescript@5.6.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-mentions-ts: specifier: ^5.4.7 version: 5.4.7(class-variance-authority@0.7.1)(clsx@2.1.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwind-merge@3.5.0) @@ -588,10 +600,10 @@ importers: version: 6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4) vite-plugin-dts: specifier: ^4.5.0 - version: 4.5.4(@types/node@22.12.0)(rollup@4.57.1)(typescript@5.9.3)(vite@6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.5.4(@types/node@22.12.0)(rollup@4.57.1)(typescript@5.6.3)(vite@6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4)) vite-plugin-svgr: specifier: ^4.3.0 - version: 4.3.0(rollup@4.57.1)(typescript@5.9.3)(vite@6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.3.0(rollup@4.57.1)(typescript@5.6.3)(vite@6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4)) vitest: specifier: ^3.0.4 version: 3.0.4(@types/debug@4.1.12)(@types/node@22.12.0)(jiti@2.6.1)(jsdom@26.0.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4) @@ -1068,6 +1080,12 @@ packages: '@bundled-es-modules/postcss-calc-ast-parser@0.1.6': resolution: {integrity: sha512-y65TM5zF+uaxo9OeekJ3rxwTINlQvrkbZLogYvQYVoLtxm4xEiHfZ7e/MyiWbStYyWZVZkVqsaVU6F4SUK5XUA==} + '@cacheable/memory@2.2.0': + resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} + + '@cacheable/utils@2.5.0': + resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==} + '@capsizecss/unpack@4.0.0': resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} engines: {node: '>=18'} @@ -1225,6 +1243,13 @@ packages: '@csstools/css-parser-algorithms': ^3.0.4 '@csstools/css-tokenizer': ^3.0.3 + '@csstools/css-calc@3.3.0': + resolution: {integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + '@csstools/css-color-parser@3.0.7': resolution: {integrity: sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==} engines: {node: '>=18'} @@ -1238,10 +1263,47 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^3.0.3 + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.8': + resolution: {integrity: sha512-CpMLjAvwQg3BL5S0IeqsZNMH7EQrEWi0kLKOC13ZBF0ZwERiLWlibNPJr8G1kdU3Ms/r2KiNrF81pUh2HwAHdg==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + '@csstools/css-tokenizer@3.0.3': resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} engines: {node: '>=18'} + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + + '@csstools/media-query-list-parser@5.0.0': + resolution: {integrity: sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/selector-resolve-nested@4.0.1': + resolution: {integrity: sha512-j3vdQu0XwLME5qOTWxm8cnmvsf423R2YL6DbKklCHZwkDm7UdKNu6RPlw4REIJhSlKBICY3B70/7QZdicLqZgg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss-selector-parser: ^7.1.1 + + '@csstools/selector-specificity@6.0.0': + resolution: {integrity: sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss-selector-parser: ^7.1.1 + '@ctrl/tinycolor@4.2.0': resolution: {integrity: sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==} engines: {node: '>=14'} @@ -1287,11 +1349,11 @@ packages: '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} - deprecated: 'Merged into tsx: https://tsx.is' + deprecated: 'Merged into tsx: https://tsx.hirok.io' '@esbuild-kit/esm-loader@2.6.5': resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} - deprecated: 'Merged into tsx: https://tsx.is' + deprecated: 'Merged into tsx: https://tsx.hirok.io' '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} @@ -2181,6 +2243,15 @@ packages: peerDependencies: tslib: '2' + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -2609,6 +2680,10 @@ packages: resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} engines: {node: '>=18'} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -3418,6 +3493,10 @@ packages: resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} + ansi-regex@6.3.0: + resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -3506,6 +3585,10 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true @@ -3642,6 +3725,9 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cacheable@2.5.0: + resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} + call-bind-apply-helpers@1.0.1: resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} engines: {node: '>= 0.4'} @@ -3818,6 +3904,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colord@2.9.5: + resolution: {integrity: sha512-1N4FZkdj0BvP78UatS8VQHP+DnRy/rAyh+eelOONgZNsgatenTe+uApmVrdfknjqvsPdsBhq5tQpnIvClcNWZA==} + colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -3962,6 +4051,15 @@ packages: typescript: optional: true + cosmiconfig@9.0.2: + resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -3972,6 +4070,10 @@ packages: crypt@0.0.2: resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + css-functions-list@3.3.3: + resolution: {integrity: sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==} + engines: {node: '>=12'} + css-line-break@2.1.0: resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} @@ -3993,6 +4095,10 @@ packages: resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@6.2.2: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} @@ -4805,6 +4911,10 @@ packages: fast-uri@3.0.6: resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + fastq@1.18.0: resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} @@ -4826,6 +4936,9 @@ packages: fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + file-entry-cache@11.1.5: + resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -4857,9 +4970,15 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} + flat-cache@6.1.23: + resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==} + flatted@3.3.2: resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + flattie@1.1.1: resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} engines: {node: '>=8'} @@ -4942,6 +5061,10 @@ packages: resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} engines: {node: '>=18'} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + get-intrinsic@1.2.7: resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} engines: {node: '>= 0.4'} @@ -4968,6 +5091,7 @@ packages: git-raw-commits@5.0.1: resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==} engines: {node: '>=18'} + deprecated: Deprecated and no longer maintained. Use @conventional-changelog/git-client instead. hasBin: true github-slugger@2.0.0: @@ -4999,6 +5123,14 @@ packages: resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==} engines: {node: '>=20'} + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -5019,6 +5151,13 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + globby@16.2.4: + resolution: {integrity: sha512-c8B/VNLmxRcmqqenRA9t+9IyOjf9+V6lTxPaUJLqOCONdQkWZ0ETYgX0qbtJqPsgCNusT9MZ5Jeidw8Eb9tn2g==} + engines: {node: '>=20'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + goober@2.1.16: resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==} peerDependencies: @@ -5048,6 +5187,10 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-flag@5.0.1: + resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} + engines: {node: '>=12'} + has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} @@ -5063,6 +5206,10 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + hashery@1.5.1: + resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} + engines: {node: '>=20'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -5139,6 +5286,12 @@ packages: resolution: {integrity: sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==} engines: {node: '>=16.9.0'} + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} + + hookified@2.2.0: + resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -5149,6 +5302,10 @@ packages: html-parse-stringify@3.0.1: resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + html-tags@5.1.0: + resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==} + engines: {node: '>=20.10'} + html-to-image@1.11.11: resolution: {integrity: sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==} @@ -5225,6 +5382,10 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + engines: {node: '>= 4'} + immer@10.1.1: resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} @@ -5256,6 +5417,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + ini@6.0.0: resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} engines: {node: ^20.17.0 || >=22.9.0} @@ -5423,6 +5587,10 @@ packages: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -5615,9 +5783,16 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + khroma@2.1.0: resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + klaw-sync@6.0.0: resolution: {integrity: sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==} @@ -5719,6 +5894,9 @@ packages: lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -5790,6 +5968,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mathml-tag-names@4.0.0: + resolution: {integrity: sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==} + md5@2.3.0: resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} @@ -5856,6 +6037,9 @@ packages: mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -5872,6 +6056,10 @@ packages: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} + meow@14.1.0: + resolution: {integrity: sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==} + engines: {node: '>=20'} + merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -6087,6 +6275,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -6431,6 +6624,12 @@ packages: peerDependencies: postcss: ^8.2.14 + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} @@ -6439,9 +6638,20 @@ packages: resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} + postcss-selector-parser@7.1.5: + resolution: {integrity: sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==} + engines: {node: '>=4'} + postcss-value-parser@3.3.1: resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -6524,6 +6734,10 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qified@0.10.1: + resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} + engines: {node: '>=20'} + qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} @@ -7012,6 +7226,14 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + slice-ansi@5.0.0: resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} engines: {node: '>=12'} @@ -7118,6 +7340,10 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + engines: {node: '>=20'} + string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} @@ -7154,6 +7380,10 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -7181,12 +7411,27 @@ packages: style-to-object@1.0.14: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + stylelint-value-no-unknown-custom-properties@6.1.1: + resolution: {integrity: sha512-eQ1zidKD5t9zMEaskjGUY4W47lH76qMlmsDSmCAPEwtaGzB4Ls7ORTfysC1D6hamp2zFC+vN1vpQ+GFz3Tw3lw==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: '>=16' + + stylelint@17.14.1: + resolution: {integrity: sha512-xVQwyiuxALUBNB2fBe0tmNemg9KqLtdj3T64mioFDar79B2cU8LIyz+3KL6LdiHs9NkeNfwxpKSaIVOY8f112g==} + engines: {node: '>=20.19.0'} + hasBin: true + stylis@4.4.0: resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} suf-log@2.5.3: resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==} + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -7195,6 +7440,10 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} + supports-hyperlinks@4.5.0: + resolution: {integrity: sha512-ZW2OvfeCXrNTbLakPUzjQG922EeGCOteFSVoek5DKStTh898wf7zgtuFlzQN8HfZCxC3Eh02yJVrRW51hADf+w==} + engines: {node: '>=20'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -7206,6 +7455,9 @@ packages: resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==} engines: {node: '>=12.0.0'} + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + svgo@3.3.3: resolution: {integrity: sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==} engines: {node: '>=14.0.0'} @@ -7229,6 +7481,10 @@ packages: resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + tailwind-merge@3.5.0: resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} @@ -7365,6 +7621,7 @@ packages: tsconfck@3.1.6: resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} engines: {node: ^18 || >=20} + deprecated: unmaintained hasBin: true peerDependencies: typescript: ^5.0.0 @@ -7474,6 +7731,10 @@ packages: resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} engines: {node: '>=20.18.1'} + unicorn-magic@0.4.0: + resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} + engines: {node: '>=20'} + unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -7942,6 +8203,10 @@ packages: resolution: {integrity: sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==} engines: {node: '>= 0.4'} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -7975,6 +8240,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + write-file-atomic@7.0.1: + resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==} + engines: {node: ^20.17.0 || >=22.9.0} + ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -8670,6 +8939,18 @@ snapshots: dependencies: postcss-calc-ast-parser: 0.1.4 + '@cacheable/memory@2.2.0': + dependencies: + '@cacheable/utils': 2.5.0 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/utils@2.5.0': + dependencies: + hashery: 1.5.1 + keyv: 5.6.0 + '@capsizecss/unpack@4.0.0': dependencies: fontkitten: 1.0.2 @@ -8944,6 +9225,11 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 + '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-color-parser@3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': dependencies: '@csstools/color-helpers': 5.0.1 @@ -8955,8 +9241,31 @@ snapshots: dependencies: '@csstools/css-tokenizer': 3.0.3 + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.8(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + '@csstools/css-tokenizer@3.0.3': {} + '@csstools/css-tokenizer@4.0.0': {} + + '@csstools/media-query-list-parser@5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/selector-resolve-nested@4.0.1(postcss-selector-parser@7.1.5)': + dependencies: + postcss-selector-parser: 7.1.5 + + '@csstools/selector-specificity@6.0.0(postcss-selector-parser@7.1.5)': + dependencies: + postcss-selector-parser: 7.1.5 + '@ctrl/tinycolor@4.2.0': {} '@date-fns/tz@1.5.0': {} @@ -9709,6 +10018,14 @@ snapshots: '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) tslib: 2.8.1 + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.5.1 + hookified: 1.15.1 + keyv: 5.6.0 + + '@keyv/serialize@1.1.1': {} + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.29.7 @@ -10122,6 +10439,8 @@ snapshots: '@simple-libs/stream-utils@1.2.0': {} + '@sindresorhus/merge-streams@4.0.0': {} + '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} @@ -10170,6 +10489,17 @@ snapshots: '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.26.7) '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.26.7) + '@svgr/core@8.1.0(typescript@5.6.3)': + dependencies: + '@babel/core': 7.26.7 + '@svgr/babel-preset': 8.1.0(@babel/core@7.26.7) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.6.3) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + '@svgr/core@8.1.0(typescript@5.9.3)': dependencies: '@babel/core': 7.26.7 @@ -10186,6 +10516,16 @@ snapshots: '@babel/types': 7.29.0 entities: 4.5.0 + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.6.3))': + dependencies: + '@babel/core': 7.26.7 + '@svgr/babel-preset': 8.1.0(@babel/core@7.26.7) + '@svgr/core': 8.1.0(typescript@5.6.3) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))': dependencies: '@babel/core': 7.26.7 @@ -10892,19 +11232,6 @@ snapshots: optionalDependencies: typescript: 5.6.3 - '@vue/language-core@2.2.0(typescript@5.9.3)': - dependencies: - '@volar/language-core': 2.4.28 - '@vue/compiler-dom': 3.5.33 - '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.33 - alien-signals: 0.4.14 - minimatch: 9.0.5 - muggle-string: 0.4.1 - path-browserify: 1.0.1 - optionalDependencies: - typescript: 5.9.3 - '@vue/shared@3.5.33': {} '@webassemblyjs/ast@1.14.1': @@ -11113,6 +11440,8 @@ snapshots: ansi-regex@6.1.0: {} + ansi-regex@6.3.0: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -11222,6 +11551,8 @@ snapshots: assertion-error@2.0.1: {} + astral-regex@2.0.0: {} + astring@1.9.0: {} astro-eslint-parser@1.3.0: @@ -11471,6 +11802,14 @@ snapshots: cac@6.7.14: {} + cacheable@2.5.0: + dependencies: + '@cacheable/memory': 2.2.0 + '@cacheable/utils': 2.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.10.1 + call-bind-apply-helpers@1.0.1: dependencies: es-errors: 1.3.0 @@ -11659,6 +11998,8 @@ snapshots: color-name@1.1.4: {} + colord@2.9.5: {} + colorette@2.0.20: {} colorjs.io@0.5.2: {} @@ -11761,6 +12102,15 @@ snapshots: jiti: 2.6.1 typescript: 5.6.3 + cosmiconfig@8.3.6(typescript@5.6.3): + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.6.3 + cosmiconfig@8.3.6(typescript@5.9.3): dependencies: import-fresh: 3.3.0 @@ -11779,6 +12129,15 @@ snapshots: optionalDependencies: typescript: 5.6.3 + cosmiconfig@9.0.2(typescript@5.6.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.0 + js-yaml: 4.1.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.6.3 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -11791,6 +12150,8 @@ snapshots: crypt@0.0.2: {} + css-functions-list@3.3.3: {} + css-line-break@2.1.0: dependencies: utrie: 1.0.2 @@ -11821,6 +12182,11 @@ snapshots: mdn-data: 2.12.2 source-map-js: 1.2.1 + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + css-what@6.2.2: {} cssesc@3.0.0: {} @@ -12777,6 +13143,8 @@ snapshots: fast-uri@3.0.6: {} + fastest-levenshtein@1.0.16: {} + fastq@1.18.0: dependencies: reusify: 1.0.4 @@ -12795,6 +13163,10 @@ snapshots: fflate@0.8.2: {} + file-entry-cache@11.1.5: + dependencies: + flat-cache: 6.1.23 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -12835,8 +13207,16 @@ snapshots: flatted: 3.3.2 keyv: 4.5.4 + flat-cache@6.1.23: + dependencies: + cacheable: 2.5.0 + flatted: 3.4.4 + hookified: 1.15.1 + flatted@3.3.2: {} + flatted@3.4.4: {} + flattie@1.1.1: {} fontace@0.4.1: @@ -12918,6 +13298,8 @@ snapshots: get-east-asian-width@1.3.0: {} + get-east-asian-width@1.6.0: {} + get-intrinsic@1.2.7: dependencies: call-bind-apply-helpers: 1.0.1 @@ -13000,6 +13382,16 @@ snapshots: dependencies: ini: 6.0.0 + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + globals@14.0.0: {} globals@15.14.0: {} @@ -13020,6 +13412,18 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + globby@16.2.4: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.6 + is-path-inside: 4.0.0 + micromatch: 4.0.8 + slash: 5.1.0 + unicorn-magic: 0.4.0 + + globjoin@0.1.4: {} + goober@2.1.16(csstype@3.1.3): dependencies: csstype: 3.1.3 @@ -13048,6 +13452,8 @@ snapshots: has-flag@4.0.0: {} + has-flag@5.0.1: {} + has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 @@ -13062,6 +13468,10 @@ snapshots: dependencies: has-symbols: 1.1.0 + hashery@1.5.1: + dependencies: + hookified: 1.15.1 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -13261,6 +13671,10 @@ snapshots: hono@4.12.14: {} + hookified@1.15.1: {} + + hookified@2.2.0: {} + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -13271,6 +13685,8 @@ snapshots: dependencies: void-elements: 3.1.0 + html-tags@5.1.0: {} + html-to-image@1.11.11: {} html-url-attributes@3.0.1: {} @@ -13330,6 +13746,12 @@ snapshots: dependencies: '@babel/runtime': 7.29.7 + i18next@24.2.3(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.27.0 + optionalDependencies: + typescript: 5.6.3 + i18next@24.2.3(typescript@5.9.3): dependencies: '@babel/runtime': 7.27.0 @@ -13348,6 +13770,8 @@ snapshots: ignore@5.3.2: {} + ignore@7.0.6: {} + immer@10.1.1: {} immer@11.1.15: {} @@ -13369,6 +13793,8 @@ snapshots: inherits@2.0.4: {} + ini@1.3.8: {} + ini@6.0.0: {} inline-style-parser@0.2.7: {} @@ -13514,6 +13940,8 @@ snapshots: is-obj@2.0.0: {} + is-path-inside@4.0.0: {} + is-plain-obj@4.1.0: {} is-potential-custom-element-name@1.0.1: {} @@ -13745,8 +14173,14 @@ snapshots: dependencies: json-buffer: 3.0.1 + keyv@5.6.0: + dependencies: + '@keyv/serialize': 1.1.1 + khroma@2.1.0: {} + kind-of@6.0.3: {} + klaw-sync@6.0.0: dependencies: graceful-fs: 4.2.11 @@ -13855,6 +14289,8 @@ snapshots: lodash.startcase@4.4.0: {} + lodash.truncate@4.4.2: {} + lodash@4.17.21: {} log-update@6.1.0: @@ -13920,6 +14356,8 @@ snapshots: math-intrinsics@1.1.0: {} + mathml-tag-names@4.0.0: {} + md5@2.3.0: dependencies: charenc: 0.0.2 @@ -14115,6 +14553,8 @@ snapshots: mdn-data@2.12.2: {} + mdn-data@2.27.1: {} + mdurl@2.0.0: {} media-typer@1.1.0: {} @@ -14138,6 +14578,8 @@ snapshots: meow@13.2.0: {} + meow@14.1.0: {} + merge-descriptors@2.0.0: {} merge-stream@2.0.0: {} @@ -14518,6 +14960,8 @@ snapshots: nanoid@3.3.11: {} + nanoid@3.3.18: {} + natural-compare@1.4.0: {} negotiator@1.0.0: {} @@ -14769,7 +15213,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.29.7 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -14902,6 +15346,10 @@ snapshots: postcss: 8.5.6 postcss-selector-parser: 6.1.2 + postcss-safe-parser@7.0.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 @@ -14912,8 +15360,21 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 + postcss-selector-parser@7.1.5: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss-value-parser@3.3.1: {} + postcss-value-parser@4.2.0: {} + + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -14996,6 +15457,10 @@ snapshots: punycode@2.3.1: {} + qified@0.10.1: + dependencies: + hookified: 2.2.0 + qs@6.14.0: dependencies: side-channel: 1.1.0 @@ -15043,6 +15508,15 @@ snapshots: react: 19.1.0 scheduler: 0.26.0 + react-i18next@15.4.1(i18next@24.2.3(typescript@5.6.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@babel/runtime': 7.27.0 + html-parse-stringify: 3.0.1 + i18next: 24.2.3(typescript@5.6.3) + react: 19.1.0 + optionalDependencies: + react-dom: 19.1.0(react@19.1.0) + react-i18next@15.4.1(i18next@24.2.3(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.27.0 @@ -15670,6 +16144,14 @@ snapshots: slash@3.0.0: {} + slash@5.1.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + slice-ansi@5.0.0: dependencies: ansi-styles: 6.2.1 @@ -15771,6 +16253,11 @@ snapshots: get-east-asian-width: 1.3.0 strip-ansi: 7.1.0 + string-width@8.2.2: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 @@ -15836,6 +16323,10 @@ snapshots: dependencies: ansi-regex: 6.1.0 + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.3.0 + strip-bom@3.0.0: {} strip-indent@4.0.0: @@ -15872,12 +16363,61 @@ snapshots: dependencies: inline-style-parser: 0.2.7 + stylelint-value-no-unknown-custom-properties@6.1.1(stylelint@17.14.1(typescript@5.6.3)): + dependencies: + postcss-value-parser: 4.2.0 + resolve: 1.22.10 + stylelint: 17.14.1(typescript@5.6.3) + + stylelint@17.14.1(typescript@5.6.3): + dependencies: + '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-syntax-patches-for-csstree': 1.1.8(css-tree@3.2.1) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/selector-resolve-nested': 4.0.1(postcss-selector-parser@7.1.5) + '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.5) + colord: 2.9.5 + cosmiconfig: 9.0.2(typescript@5.6.3) + css-functions-list: 3.3.3 + css-tree: 3.2.1 + debug: 4.4.3 + fast-glob: 3.3.3 + fastest-levenshtein: 1.0.16 + file-entry-cache: 11.1.5 + global-modules: 2.0.0 + globby: 16.2.4 + globjoin: 0.1.4 + html-tags: 5.1.0 + ignore: 7.0.6 + import-meta-resolve: 4.2.0 + mathml-tag-names: 4.0.0 + meow: 14.1.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.26 + postcss-safe-parser: 7.0.1(postcss@8.5.26) + postcss-selector-parser: 7.1.5 + postcss-value-parser: 4.2.0 + string-width: 8.2.2 + supports-hyperlinks: 4.5.0 + svg-tags: 1.0.0 + table: 6.9.0 + write-file-atomic: 7.0.1 + transitivePeerDependencies: + - supports-color + - typescript + stylis@4.4.0: {} suf-log@2.5.3: dependencies: s.color: 0.0.15 + supports-color@10.2.2: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -15886,6 +16426,11 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-hyperlinks@4.5.0: + dependencies: + has-flag: 5.0.1 + supports-color: 10.2.2 + supports-preserve-symlinks-flag@1.0.0: {} svg-parser@2.0.4: {} @@ -15893,6 +16438,8 @@ snapshots: svg-pathdata@6.0.3: optional: true + svg-tags@1.0.0: {} + svgo@3.3.3: dependencies: commander: 7.2.0 @@ -15925,6 +16472,14 @@ snapshots: dependencies: '@pkgr/core': 0.2.9 + table@6.9.0: + dependencies: + ajv: 8.18.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + tailwind-merge@3.5.0: {} tapable@2.3.2: {} @@ -16142,6 +16697,8 @@ snapshots: undici@7.24.4: {} + unicorn-magic@0.4.0: {} + unified@11.0.5: dependencies: '@types/unist': 3.0.3 @@ -16362,25 +16919,6 @@ snapshots: - rollup - supports-color - vite-plugin-dts@4.5.4(@types/node@22.12.0)(rollup@4.57.1)(typescript@5.9.3)(vite@6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4)): - dependencies: - '@microsoft/api-extractor': 7.58.7(@types/node@22.12.0) - '@rollup/pluginutils': 5.3.0(rollup@4.57.1) - '@volar/typescript': 2.4.28 - '@vue/language-core': 2.2.0(typescript@5.9.3) - compare-versions: 6.1.1 - debug: 4.4.3 - kolorist: 1.8.0 - local-pkg: 1.1.2 - magic-string: 0.30.21 - typescript: 5.9.3 - optionalDependencies: - vite: 6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4) - transitivePeerDependencies: - - '@types/node' - - rollup - - supports-color - vite-plugin-lib-inject-css@2.2.2(vite@6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4)): dependencies: '@ast-grep/napi': 0.36.3 @@ -16397,6 +16935,17 @@ snapshots: picocolors: 1.1.1 vite: 6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4) + vite-plugin-svgr@4.3.0(rollup@4.57.1)(typescript@5.6.3)(vite@6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4)): + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.57.1) + '@svgr/core': 8.1.0(typescript@5.6.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.6.3)) + vite: 6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4) + transitivePeerDependencies: + - rollup + - supports-color + - typescript + vite-plugin-svgr@4.3.0(rollup@4.57.1)(typescript@5.9.3)(vite@6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.57.1) @@ -16670,6 +17219,10 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which@1.3.1: + dependencies: + isexe: 2.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -16705,6 +17258,10 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@7.0.1: + dependencies: + signal-exit: 4.1.0 + ws@8.18.0: {} wsl-utils@0.3.1: diff --git a/tools/stylelint/custom-properties.mjs b/tools/stylelint/custom-properties.mjs new file mode 100644 index 000000000..398b1123c --- /dev/null +++ b/tools/stylelint/custom-properties.mjs @@ -0,0 +1,46 @@ +/** + * importFrom source for `csstools/value-no-unknown-custom-properties`. + * + * Collects every custom-property definition the repo actually ships — the + * built token dist plus all workspace source CSS — so the rule validates + * `var(--…)` usages against names that exist somewhere, without a hand-kept + * file list going stale. + * + * Requires a built `packages/tokens/dist` (created by `pnpm install` via the + * tokens package's `prepare` script, and by `pnpm build:ui`). + */ +import { globSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import postcss from 'postcss'; + +const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), '../../..'); + +// Set at runtime from JS, so no stylesheet defines them. +const RUNTIME_DEFINED = { + // Base UI positioner writes the anchor dimensions onto the popup element. + '--anchor-width': '0px', + '--anchor-height': '0px', + // packages/ui node-as-port-wrapper.tsx inline style. + '--ax-node-as-port-width': '0px', + '--ax-node-as-port-height': '0px', + '--ax-node-as-port-position': 'none', + // apps/ai-studio log-panel.tsx inline style. + '--log-panel-right': '0px', +}; + +const customProperties = { ...RUNTIME_DEFINED }; + +const files = globSync(['packages/tokens/dist/**/*.css', 'packages/*/src/**/*.css', 'apps/*/src/**/*.css'], { + cwd: REPO_ROOT, +}).filter((file) => !file.startsWith(`apps${path.sep}docs${path.sep}`)); + +for (const file of files) { + const absolute = path.join(REPO_ROOT, file); + postcss.parse(readFileSync(absolute, 'utf8'), { from: absolute }).walkDecls(/^--/, (decl) => { + customProperties[decl.prop] = decl.value; + }); +} + +export default { customProperties }; diff --git a/tools/stylelint/no-system-token-fallbacks.mjs b/tools/stylelint/no-system-token-fallbacks.mjs new file mode 100644 index 000000000..83c3de5f8 --- /dev/null +++ b/tools/stylelint/no-system-token-fallbacks.mjs @@ -0,0 +1,48 @@ +/** + * Stylelint rule: forbid fallbacks on system design tokens. + * + * `var(--wb-…, x)` / `var(--ax-…, x)` silently masks a mistyped token name — + * the fallback renders and the typo ships. The companion rule + * `csstools/value-no-unknown-custom-properties` cannot catch that case + * because a var() with a fallback is valid CSS regardless of the name. + * + * Legitimate exceptions use the standard mechanism with a mandatory reason: + * /* stylelint-disable-next-line wb/no-system-token-fallbacks -- reason *\/ + */ +import valueParser from 'postcss-value-parser'; +import stylelint from 'stylelint'; + +const ruleName = 'wb/no-system-token-fallbacks'; + +const messages = stylelint.utils.ruleMessages(ruleName, { + rejected: (name) => `Unexpected fallback on system token "${name}" — fallbacks mask typos`, +}); + +const meta = { + url: 'https://github.com/synergycodes/workflowbuilder/blob/main/packages/tokens/README.md', +}; + +const rule = (primary) => (root, result) => { + if (!primary) return; + root.walkDecls((decl) => { + valueParser(decl.value).walk((node) => { + if (node.type !== 'function' || node.value !== 'var') return; + const [first, ...rest] = node.nodes; + if (!first || !/^--(wb|ax)-/.test(first.value)) return; + if (rest.some((argument) => argument.type === 'div' && argument.value === ',')) { + stylelint.utils.report({ + ruleName, + result, + node: decl, + message: messages.rejected(first.value), + }); + } + }); + }); +}; + +rule.ruleName = ruleName; +rule.messages = messages; +rule.meta = meta; + +export default stylelint.createPlugin(ruleName, rule); From 52ccbb2311688ccf58553c2dcdba6d55473408a2 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 12:01:01 +0200 Subject: [PATCH 09/21] fix(sdk): use the primary text token for the neutral message variant --ax-colors-gray-800 is a mode-less primitive; on the dark theme it lands dark-on-dark (1.61:1) against the theme-aware secondary background. --ax-txt-primary-default resolves to the same gray-800 in light and to gray-100 in dark. The sdk changeset is reworded around the changes a consumer can actually see (the message variants are not reachable from the public API yet), and the ui changes from this PR get their missed changeset (a new public TextArea variable is an API addition). --- .changeset/wb-486-dangling-token-names.md | 2 +- .changeset/wb-486-textarea-disabled-background.md | 5 +++++ packages/sdk/src/components/form/message/message.module.css | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/wb-486-textarea-disabled-background.md diff --git a/.changeset/wb-486-dangling-token-names.md b/.changeset/wb-486-dangling-token-names.md index 654db4daf..2f5551eee 100644 --- a/.changeset/wb-486-dangling-token-names.md +++ b/.changeset/wb-486-dangling-token-names.md @@ -2,4 +2,4 @@ '@workflowbuilder/sdk': patch --- -Fix CSS variable names that pointed at non-existent tokens: neutral and warning form-message variants render their intended colors again, and the variables-settings modal picks up its token-defined colors, spacing, and radii. +Fix CSS variable names that pointed at non-existent tokens: the variables-settings modal picks up its token-defined colors, spacing, and radii, and diagram edge labels use their intended hover/selection colors. diff --git a/.changeset/wb-486-textarea-disabled-background.md b/.changeset/wb-486-textarea-disabled-background.md new file mode 100644 index 000000000..b9e88f3d7 --- /dev/null +++ b/.changeset/wb-486-textarea-disabled-background.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': minor +--- + +TextArea exposes `--ax-public-textarea-root-background-color-disabled` (defaults to `transparent`); a never-applied icon-switch thumb background declaration was removed. diff --git a/packages/sdk/src/components/form/message/message.module.css b/packages/sdk/src/components/form/message/message.module.css index 6fcaa185e..29697bc20 100644 --- a/packages/sdk/src/components/form/message/message.module.css +++ b/packages/sdk/src/components/form/message/message.module.css @@ -20,7 +20,7 @@ } .container--neutral { - --color: var(--ax-colors-gray-800); + --color: var(--ax-txt-primary-default); --background-color: var(--ax-ui-bg-secondary-default); } From e33300d3086cbd6bdcdf0307e8ff2d632461da73 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 12:01:02 +0200 Subject: [PATCH 10/21] refactor(sdk): drop unconsumed edge color variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --wb-edge-color, -temporary, -hover, and -select have zero consumers — the live edge colors are the --ax-public-edge-color-* pair in packages/ui. The label-edge variables that are consumed stay; the stale missing-token comments on them pointed at tokens that exist. --- .../src/features/diagram/edges/label-edge/variables.css | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/sdk/src/features/diagram/edges/label-edge/variables.css b/packages/sdk/src/features/diagram/edges/label-edge/variables.css index 1c5253891..3253a4ca8 100644 --- a/packages/sdk/src/features/diagram/edges/label-edge/variables.css +++ b/packages/sdk/src/features/diagram/edges/label-edge/variables.css @@ -1,11 +1,6 @@ :root { - --wb-edge-color: var(--ax-colors-gray-500); /* missing token */ - --wb-edge-color-temporary: color-mix(in srgb, var(--ax-colors-gray-500) 50%, transparent); - --wb-edge-color-hover: var(--ax-colors-gray-600); /* missing token */ - --wb-edge-color-select: var(--ax-colors-acc1-500); /* missing token */ - - --wb-edge-label-color: var(--ax-colors-gray-500); /* missing token */ + --wb-edge-label-color: var(--ax-colors-gray-500); --wb-edge-label-color-hover: var(--ax-edge-primary-hover); --wb-edge-label-color-select: var(--ax-edge-primary-active); - --wb-edge-label-background: var(--ax-colors-gray-200); /* missing token */ + --wb-edge-label-background: var(--ax-colors-gray-200); } From bc18fc686c729ac0831f570c539171be7d61e48c Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 12:01:04 +0200 Subject: [PATCH 11/21] fix(repo): resolve stylelint importFrom from the config file, name the meter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The csstools rule resolves importFrom against process.cwd(), and lint-staged runs commands from workspace directories — caught by the pre-commit hook itself. The config moves to .stylelintrc.mjs and anchors the path via import.meta.url. The DS 2.0 migration meter moves out of an inline CI shell string into tools/migration-meter.mjs behind pnpm migration:meter. --- .github/workflows/pr-check.yml | 7 ++++--- .stylelintrc.json | 11 ----------- .stylelintrc.mjs | 15 +++++++++++++++ package.json | 1 + packages/tokens/README.md | 2 +- tools/migration-meter.mjs | 19 +++++++++++++++++++ 6 files changed, 40 insertions(+), 15 deletions(-) delete mode 100644 .stylelintrc.json create mode 100644 .stylelintrc.mjs create mode 100644 tools/migration-meter.mjs diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 727a90764..5b4f77b6c 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -148,6 +148,7 @@ jobs: run: pnpm build:ui - name: Style lint (token usage + fallbacks) - run: | - pnpm lint:styles - echo "DS 2.0 migration meter: $(grep -rho 'var(--ax-' packages/*/src apps/*/src --include='*.css' --include='*.tsx' --include='*.ts' | wc -l) var(--ax-*) usages remaining" + run: pnpm lint:styles + + - name: DS 2.0 migration meter + run: pnpm migration:meter diff --git a/.stylelintrc.json b/.stylelintrc.json deleted file mode 100644 index db2da2dec..000000000 --- a/.stylelintrc.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "plugins": ["stylelint-value-no-unknown-custom-properties", "./tools/stylelint/no-system-token-fallbacks.mjs"], - "ignoreFiles": ["**/node_modules/**", "**/dist/**", "apps/docs/**"], - "rules": { - "csstools/value-no-unknown-custom-properties": [ - true, - { "importFrom": ["./tools/stylelint/custom-properties.mjs"] } - ], - "wb/no-system-token-fallbacks": true - } -} diff --git a/.stylelintrc.mjs b/.stylelintrc.mjs new file mode 100644 index 000000000..82d696860 --- /dev/null +++ b/.stylelintrc.mjs @@ -0,0 +1,15 @@ +import { fileURLToPath } from 'node:url'; + +// The csstools rule resolves importFrom paths against process.cwd(), and +// lint-staged runs commands from workspace directories — anchor to this file. +const customProperties = fileURLToPath(new URL('./tools/stylelint/custom-properties.mjs', import.meta.url)); + +/** @type {import('stylelint').Config} */ +export default { + plugins: ['stylelint-value-no-unknown-custom-properties', './tools/stylelint/no-system-token-fallbacks.mjs'], + ignoreFiles: ['**/node_modules/**', '**/dist/**', 'apps/docs/**'], + rules: { + 'csstools/value-no-unknown-custom-properties': [true, { importFrom: [customProperties] }], + 'wb/no-system-token-fallbacks': true, + }, +}; diff --git a/package.json b/package.json index e2c82072a..23984cae0 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "lint": "pnpm -r lint", "lint:fix": "pnpm -r lint:fix", "lint:styles": "stylelint \"packages/*/src/**/*.css\" \"apps/*/src/**/*.css\"", + "migration:meter": "node tools/migration-meter.mjs", "format": "prettier --write --log-level silent \"**/*.+(css|ts|tsx|json|md|mdx|astro)\"", "typecheck": "pnpm -r typecheck", "test": "pnpm -r test", diff --git a/packages/tokens/README.md b/packages/tokens/README.md index a5e1e78b5..af0f4826c 100644 --- a/packages/tokens/README.md +++ b/packages/tokens/README.md @@ -52,7 +52,7 @@ Vitest unit tests (currently `src/to-file-name.spec.ts` and `src/manifest.spec.t pnpm lint:styles ``` -Stylelint (root `.stylelintrc.json`) validates workspace CSS with two rules, +Stylelint (root `.stylelintrc.mjs`) validates workspace CSS with two rules, both errors: 1. `csstools/value-no-unknown-custom-properties` — every `var(--name)` must diff --git a/tools/migration-meter.mjs b/tools/migration-meter.mjs new file mode 100644 index 000000000..94e8e4e5c --- /dev/null +++ b/tools/migration-meter.mjs @@ -0,0 +1,19 @@ +/** + * DS 2.0 migration meter: counts remaining `var(--ax-*)` usages in workspace + * sources. The old-system prefix disappears as components migrate to the + * `--wb-*` token export, so this number only goes down; zero means done. + */ +import { globSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), '../..'); + +let count = 0; +for (const file of globSync(['packages/*/src/**/*.{css,ts,tsx}', 'apps/*/src/**/*.{css,ts,tsx}'], { + cwd: REPO_ROOT, +})) { + count += readFileSync(path.join(REPO_ROOT, file), 'utf8').match(/var\(--ax-/g)?.length ?? 0; +} + +console.log(`DS 2.0 migration meter: ${count} var(--ax-*) usages remaining`); From 867100630935621a846a440ed0fb4934b59f069c Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 12:03:49 +0200 Subject: [PATCH 12/21] docs: drop a brittle cross-rule remark from the stylelint plugin --- tools/stylelint/no-system-token-fallbacks.mjs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/stylelint/no-system-token-fallbacks.mjs b/tools/stylelint/no-system-token-fallbacks.mjs index 83c3de5f8..60223d780 100644 --- a/tools/stylelint/no-system-token-fallbacks.mjs +++ b/tools/stylelint/no-system-token-fallbacks.mjs @@ -2,9 +2,7 @@ * Stylelint rule: forbid fallbacks on system design tokens. * * `var(--wb-…, x)` / `var(--ax-…, x)` silently masks a mistyped token name — - * the fallback renders and the typo ships. The companion rule - * `csstools/value-no-unknown-custom-properties` cannot catch that case - * because a var() with a fallback is valid CSS regardless of the name. + * the fallback renders and the typo ships. * * Legitimate exceptions use the standard mechanism with a mandatory reason: * /* stylelint-disable-next-line wb/no-system-token-fallbacks -- reason *\/ From 0871bce77fa161674fcbcd5a11274c1f873b1049 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 12:18:04 +0200 Subject: [PATCH 13/21] ci: drop the migration meter from pr-check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Informational, not a gate — pnpm migration:meter stays as an on-demand command for migration PRs. --- .github/workflows/pr-check.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 5b4f77b6c..1cf2623b4 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -149,6 +149,3 @@ jobs: - name: Style lint (token usage + fallbacks) run: pnpm lint:styles - - - name: DS 2.0 migration meter - run: pnpm migration:meter From a5eeb45bdf36b81b47e204a75bce3b2e58987621 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 12:19:11 +0200 Subject: [PATCH 14/21] docs: explain the stylelint definition source in plain terms --- tools/stylelint/custom-properties.mjs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/stylelint/custom-properties.mjs b/tools/stylelint/custom-properties.mjs index 398b1123c..bb0acc048 100644 --- a/tools/stylelint/custom-properties.mjs +++ b/tools/stylelint/custom-properties.mjs @@ -1,10 +1,11 @@ /** - * importFrom source for `csstools/value-no-unknown-custom-properties`. + * Feeds stylelint the set of custom properties defined anywhere in the repo. * - * Collects every custom-property definition the repo actually ships — the - * built token dist plus all workspace source CSS — so the rule validates - * `var(--…)` usages against names that exist somewhere, without a hand-kept - * file list going stale. + * The unknown-property rule checks names one file at a time, but our + * definitions live outside the files that use them (the built token dist, + * sibling variables.css files). This module is wired into the rule's + * `importFrom` option in .stylelintrc.mjs, so "defined" means defined + * anywhere in the workspace — collected by glob, not by a hand-kept list. * * Requires a built `packages/tokens/dist` (created by `pnpm install` via the * tokens package's `prepare` script, and by `pnpm build:ui`). From 1f31ed899422bcc549eb661018f56ef5e0ebb4eb Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 12:34:11 +0200 Subject: [PATCH 15/21] refactor(repo): replace the runtime-variable allowlist with in-place disables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A central RUNTIME_DEFINED list in the definition collector rots and hides the reason away from the code. Each of the five usage sites of a JS-set variable now carries a stylelint-disable comment with the reason — self-cleaning: remove the usage and the exception goes with it. --- .../components/execution/log-panel.module.css | 1 + packages/tokens/README.md | 11 ++++++----- .../node/node-as-port-wrapper/node-as-port.css | 2 ++ .../ui/src/components/select/select.module.css | 1 + tools/stylelint/custom-properties.mjs | 17 +++-------------- 5 files changed, 13 insertions(+), 19 deletions(-) diff --git a/apps/ai-studio/src/components/execution/log-panel.module.css b/apps/ai-studio/src/components/execution/log-panel.module.css index 7ce616041..e8a962917 100644 --- a/apps/ai-studio/src/components/execution/log-panel.module.css +++ b/apps/ai-studio/src/components/execution/log-panel.module.css @@ -1,6 +1,7 @@ .panel { position: fixed; bottom: 1rem; + /* stylelint-disable-next-line csstools/value-no-unknown-custom-properties -- set from log-panel.tsx inline style */ right: var(--log-panel-right, 1rem); width: 30rem; max-width: calc(100vw - 3rem); diff --git a/packages/tokens/README.md b/packages/tokens/README.md index af0f4826c..1e0c27db6 100644 --- a/packages/tokens/README.md +++ b/packages/tokens/README.md @@ -57,11 +57,12 @@ both errors: 1. `csstools/value-no-unknown-custom-properties` — every `var(--name)` must resolve to a definition that actually exists. The definition set is built - by `tools/stylelint/custom-properties.mjs`: the token dist, every - custom-property declaration in `packages/*/src` and `apps/*/src` CSS, and a - short inline list of names set at runtime from JS (Base UI's - `--anchor-width`, the node-as-port and log-panel inline styles). Catches - prefix typos and drift after a token export update. + by `tools/stylelint/custom-properties.mjs`: the token dist plus every + custom-property declaration in `packages/*/src` and `apps/*/src` CSS. + Catches prefix typos and drift after a token export update. Variables set + at runtime from JS (Base UI's `--anchor-width`, inline-style bridges) have + no CSS definition by design — their usage sites carry a + `stylelint-disable-next-line` comment with the reason. 2. `wb/no-system-token-fallbacks` (`tools/stylelint/no-system-token-fallbacks.mjs`) — no fallbacks on `var(--wb-…)` / `var(--ax-…)`; a fallback silently masks exactly the typos rule 1 exists to catch. Genuine exceptions use the diff --git a/packages/ui/src/components/node/node-as-port-wrapper/node-as-port.css b/packages/ui/src/components/node/node-as-port-wrapper/node-as-port.css index 3e8916b96..1b8ba646b 100644 --- a/packages/ui/src/components/node/node-as-port-wrapper/node-as-port.css +++ b/packages/ui/src/components/node/node-as-port-wrapper/node-as-port.css @@ -2,9 +2,11 @@ .is-connection-target .react-flow__handle.target::before { content: ''; position: absolute; + /* stylelint-disable csstools/value-no-unknown-custom-properties -- set from node-as-port-wrapper.tsx inline style */ width: var(--ax-node-as-port-width); height: var(--ax-node-as-port-height); transform: var(--ax-node-as-port-position); + /* stylelint-enable csstools/value-no-unknown-custom-properties */ border-radius: 0; } } diff --git a/packages/ui/src/components/select/select.module.css b/packages/ui/src/components/select/select.module.css index e0cd26370..bc0d65abb 100644 --- a/packages/ui/src/components/select/select.module.css +++ b/packages/ui/src/components/select/select.module.css @@ -6,6 +6,7 @@ } .popup { + /* stylelint-disable-next-line csstools/value-no-unknown-custom-properties -- set by the Base UI positioner at runtime */ width: var(--anchor-width); } } diff --git a/tools/stylelint/custom-properties.mjs b/tools/stylelint/custom-properties.mjs index bb0acc048..813d6fa83 100644 --- a/tools/stylelint/custom-properties.mjs +++ b/tools/stylelint/custom-properties.mjs @@ -18,20 +18,9 @@ import postcss from 'postcss'; const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), '../../..'); -// Set at runtime from JS, so no stylesheet defines them. -const RUNTIME_DEFINED = { - // Base UI positioner writes the anchor dimensions onto the popup element. - '--anchor-width': '0px', - '--anchor-height': '0px', - // packages/ui node-as-port-wrapper.tsx inline style. - '--ax-node-as-port-width': '0px', - '--ax-node-as-port-height': '0px', - '--ax-node-as-port-position': 'none', - // apps/ai-studio log-panel.tsx inline style. - '--log-panel-right': '0px', -}; - -const customProperties = { ...RUNTIME_DEFINED }; +// Variables set at runtime from JS have no definition anywhere in CSS — +// their usage sites carry a stylelint-disable comment with the reason. +const customProperties = {}; const files = globSync(['packages/tokens/dist/**/*.css', 'packages/*/src/**/*.css', 'apps/*/src/**/*.css'], { cwd: REPO_ROOT, From 86ffeb48455dac8e989ae048b74ad08b1e8c8be8 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 12:37:32 +0200 Subject: [PATCH 16/21] chore: drop the migration meter from the repo Informational-only; lives on as a private local script for the DS 2.0 migration work instead of shipped tooling. --- package.json | 1 - tools/migration-meter.mjs | 19 ------------------- 2 files changed, 20 deletions(-) delete mode 100644 tools/migration-meter.mjs diff --git a/package.json b/package.json index 23984cae0..e2c82072a 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "lint": "pnpm -r lint", "lint:fix": "pnpm -r lint:fix", "lint:styles": "stylelint \"packages/*/src/**/*.css\" \"apps/*/src/**/*.css\"", - "migration:meter": "node tools/migration-meter.mjs", "format": "prettier --write --log-level silent \"**/*.+(css|ts|tsx|json|md|mdx|astro)\"", "typecheck": "pnpm -r typecheck", "test": "pnpm -r test", diff --git a/tools/migration-meter.mjs b/tools/migration-meter.mjs deleted file mode 100644 index 94e8e4e5c..000000000 --- a/tools/migration-meter.mjs +++ /dev/null @@ -1,19 +0,0 @@ -/** - * DS 2.0 migration meter: counts remaining `var(--ax-*)` usages in workspace - * sources. The old-system prefix disappears as components migrate to the - * `--wb-*` token export, so this number only goes down; zero means done. - */ -import { globSync, readFileSync } from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), '../..'); - -let count = 0; -for (const file of globSync(['packages/*/src/**/*.{css,ts,tsx}', 'apps/*/src/**/*.{css,ts,tsx}'], { - cwd: REPO_ROOT, -})) { - count += readFileSync(path.join(REPO_ROOT, file), 'utf8').match(/var\(--ax-/g)?.length ?? 0; -} - -console.log(`DS 2.0 migration meter: ${count} var(--ax-*) usages remaining`); From 9db6e316ec7b6c4bef599cb1e8eeae30cc7e3869 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 12:48:01 +0200 Subject: [PATCH 17/21] chore: shield the DS 2.0 migration changelog from prettier The designer changelog lands verbatim in packages/tokens/migration/ on the integration branch; prettier reformatting it corrupted token paths inside emphasis markers. Prettier reads .prettierignore only from the cwd and lint-staged runs from workspace directories, so the lint-staged command passes the root ignore file explicitly. --- .prettierignore | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.prettierignore b/.prettierignore index 40c7a9838..058c1d594 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,7 +1 @@ -# Add files here to ignore them from prettier formatting -/dist -/coverage -.claude/ -apps/icons/src/utils/icons.gen.ts -# Astro auto-generated content collections + types (gitignored, regenerated on build/dev) -**/.astro/ +packages/tokens/migration/ From 9b2fe9f3ae8003b24ee372af28b48357fcbe3313 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 12:49:36 +0200 Subject: [PATCH 18/21] fix: restore the full .prettierignore and anchor it for lint-staged MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit accidentally replaced the whole ignore file with the single migration entry — the original entries (dist, coverage, .astro, generated icons) are back, migration/ added on top. lint-staged runs prettier from workspace directories where the root .prettierignore is invisible, so the command now passes it explicitly. --- .prettierignore | 8 ++++++++ lint-staged.config.mjs | 10 +++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index 058c1d594..23323b073 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,9 @@ +# Add files here to ignore them from prettier formatting +/dist +/coverage +.claude/ +apps/icons/src/utils/icons.gen.ts +# Astro auto-generated content collections + types (gitignored, regenerated on build/dev) +**/.astro/ +# Designer changelog checked in verbatim — reformatting corrupted token paths in emphasis markers packages/tokens/migration/ diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs index 82e98add6..d6001c864 100644 --- a/lint-staged.config.mjs +++ b/lint-staged.config.mjs @@ -1,8 +1,16 @@ +import { fileURLToPath } from 'node:url'; + +// Workspace lint-staged configs re-export this file and run commands from +// their own directory; prettier reads .prettierignore only from the cwd, +// so the root ignore file must be passed explicitly. +const prettierIgnore = fileURLToPath(new URL('./.prettierignore', import.meta.url)); + /** * @type {import('lint-staged').Configuration} */ export default { - '*.{ts,tsx,js,json,css,astro,md,mdx}': (files) => `prettier --write --log-level=silent ${files.join(' ')}`, + '*.{ts,tsx,js,json,css,astro,md,mdx}': (files) => + `prettier --write --log-level=silent --ignore-path ${prettierIgnore} ${files.join(' ')}`, '*.{ts,tsx}': [(files) => `eslint --max-warnings=0 --fix ${files.join(' ')}`, () => `tsc --noEmit`], '*.css': (files) => `stylelint ${files.join(' ')}`, }; From fc43b9a35887e9968a4f75d3e6e56852404838b1 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 18:29:37 +0200 Subject: [PATCH 19/21] chore: drop tracker ids from changeset filenames --- .../{wb-486-dangling-token-names.md => dangling-token-names.md} | 0 ...rea-disabled-background.md => textarea-disabled-background.md} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .changeset/{wb-486-dangling-token-names.md => dangling-token-names.md} (100%) rename .changeset/{wb-486-textarea-disabled-background.md => textarea-disabled-background.md} (100%) diff --git a/.changeset/wb-486-dangling-token-names.md b/.changeset/dangling-token-names.md similarity index 100% rename from .changeset/wb-486-dangling-token-names.md rename to .changeset/dangling-token-names.md diff --git a/.changeset/wb-486-textarea-disabled-background.md b/.changeset/textarea-disabled-background.md similarity index 100% rename from .changeset/wb-486-textarea-disabled-background.md rename to .changeset/textarea-disabled-background.md From 890d8bf1176d1c1391a737faad4b831042d4b671 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 18:40:38 +0200 Subject: [PATCH 20/21] ci: run PR checks for the DS 2.0 stack branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stacked PRs target the branch below them, and the pull_request branches filter matches the base — without these entries every layer above the bottom runs with no CI until the cascade retargets it. Temporary; removed when the stack lands. --- .github/workflows/pr-check-docs.yml | 4 ++++ .github/workflows/pr-check.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/pr-check-docs.yml b/.github/workflows/pr-check-docs.yml index 209e190a2..2a4a7d63d 100644 --- a/.github/workflows/pr-check-docs.yml +++ b/.github/workflows/pr-check-docs.yml @@ -12,6 +12,10 @@ on: pull_request: branches: - main + # DS 2.0 PR stack: layers target the branch below, and the pull_request + # branches filter matches the base. Remove after the stack lands. + - token-usage-guard + - 'ds2-**' paths: - 'apps/docs/**' - 'packages/ui/**' diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 1cf2623b4..02e226673 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -12,6 +12,10 @@ on: pull_request: branches: - main + # DS 2.0 PR stack: layers target the branch below, and the pull_request + # branches filter matches the base. Remove after the stack lands. + - token-usage-guard + - 'ds2-**' permissions: contents: read From 28cddff33745335738a80e3a4f35834257c69177 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Thu, 20 Aug 2026 19:07:38 +0200 Subject: [PATCH 21/21] ci: run PR checks for every base branch The base allowlist needed a new entry for each integration or stack branch and left dead entries behind; the default pull_request trigger covers them all. pr-check-docs keeps its paths filter. --- .github/workflows/pr-check-docs.yml | 8 ++------ .github/workflows/pr-check.yml | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pr-check-docs.yml b/.github/workflows/pr-check-docs.yml index 2a4a7d63d..6984d7ebf 100644 --- a/.github/workflows/pr-check-docs.yml +++ b/.github/workflows/pr-check-docs.yml @@ -8,14 +8,10 @@ name: PR Check (docs) # Typecheck is deliberately absent: apps/docs tolerates known starlight # virtual-module type errors. +# Every PR gets checks regardless of its base branch — this keeps stacked +# PRs and integration branches covered without a base allowlist to maintain. on: pull_request: - branches: - - main - # DS 2.0 PR stack: layers target the branch below, and the pull_request - # branches filter matches the base. Remove after the stack lands. - - token-usage-guard - - 'ds2-**' paths: - 'apps/docs/**' - 'packages/ui/**' diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 02e226673..012b02774 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -8,14 +8,10 @@ name: PR Check # internal and have their own broken-state tolerances — but their CSS goes # through the style lint below like every other workspace's. +# Every PR gets checks regardless of its base branch — this keeps stacked +# PRs and integration branches covered without a base allowlist to maintain. on: pull_request: - branches: - - main - # DS 2.0 PR stack: layers target the branch below, and the pull_request - # branches filter matches the base. Remove after the stack lands. - - token-usage-guard - - 'ds2-**' permissions: contents: read