diff --git a/web/eslint-plugins/design-tokens/lists.js b/web/eslint-plugins/design-tokens/lists.js
index c5222e1..f72ad22 100644
--- a/web/eslint-plugins/design-tokens/lists.js
+++ b/web/eslint-plugins/design-tokens/lists.js
@@ -6,14 +6,14 @@
// Properties that accept a COLOR.
// =====================================================================
export const COLOR_PROPS =
- /^(color|background|background-color|border|border-color|border-top|border-right|border-bottom|border-left|outline|outline-color|box-shadow|text-shadow|fill|stroke|fill-color|stroke-color|stop-color|flood-color|lighting-color|column-rule|column-rule-color|text-decoration|text-decoration-color|caret-color|accent-color|border-top-color|border-right-color|border-bottom-color|border-left-color)$/;
+ /^(color|background|background-color|background-image|border|border-color|border-top|border-right|border-bottom|border-left|outline|outline-color|box-shadow|text-shadow|fill|stroke|fill-color|stroke-color|stop-color|flood-color|lighting-color|column-rule|column-rule-color|text-decoration|text-decoration-color|caret-color|accent-color|border-top-color|border-right-color|border-bottom-color|border-left-color)$/;
// =====================================================================
// Properties that accept a SIZE (px/rem/em).
// z-index is handled separately (it's an integer, not a length).
// =====================================================================
export const SIZE_PROPS =
- /^(width|height|min-width|max-width|min-height|max-height|padding|padding-top|padding-right|padding-bottom|padding-left|margin|margin-top|margin-right|margin-bottom|margin-left|gap|column-gap|row-gap|top|right|bottom|left|inset|font-size|letter-spacing|word-spacing|line-height|border-radius|border-top-left-radius|border-top-right-radius|border-bottom-left-radius|border-bottom-right-radius|border-width|border-top-width|border-right-width|border-bottom-width|border-left-width|flex-basis|background-size|border-spacing)$/;
+ /^(width|height|min-width|max-width|min-height|max-height|padding|padding-top|padding-right|padding-bottom|padding-left|margin|margin-top|margin-right|margin-bottom|margin-left|gap|column-gap|row-gap|top|right|bottom|left|inset|font|font-size|letter-spacing|word-spacing|line-height|border-radius|border-top-left-radius|border-top-right-radius|border-bottom-left-radius|border-bottom-right-radius|border-width|border-top-width|border-right-width|border-bottom-width|border-left-width|flex-basis|background-size|background-position|border-spacing|grid-template-columns|grid-template-rows)$/;
// =====================================================================
// SHORTHAND properties that accept BOTH a color and a size. The browser
diff --git a/web/eslint-plugins/design-tokens/no-hardcoded-in-svelte.js b/web/eslint-plugins/design-tokens/no-hardcoded-in-svelte.js
index 9053fb7..d652078 100644
--- a/web/eslint-plugins/design-tokens/no-hardcoded-in-svelte.js
+++ b/web/eslint-plugins/design-tokens/no-hardcoded-in-svelte.js
@@ -9,6 +9,7 @@ import {
DURATION_PROPS,
FORBIDDEN_DURATION_TOKEN,
FORBIDDEN_SIZE_TOKEN,
+ MIXED_PROPS,
SIZE_PROPS,
} from "./lists.js";
import { getStyleNodeLoc, getStyleRoot } from "./style-context.js";
@@ -125,13 +126,16 @@ export default {
// Sub-rule 2: sizes.
// Fire only on a real remaining px/rem/em token — percentages
// (width: 80%) and unitless values (line-height: 1.5) stay legal.
- // Only legal absolute length is a single 1px border line.
- if (SIZE_PROPS.test(prop)) {
+ // The only legal absolute length is a 0px reset line.
+ // MIXED_PROPS (border/outline shorthands) are checked for their
+ // SIZE half too — a border width must come from var(--size-*).
+ if (SIZE_PROPS.test(prop) || MIXED_PROPS.test(prop)) {
const withoutVars = stripVars(value);
- const match = withoutVars.match(FORBIDDEN_SIZE_TOKEN);
- if (match) {
+ const reported = new Set();
+ for (const match of withoutVars.matchAll(FORBIDDEN_SIZE_TOKEN)) {
const shown = match[0];
- if (shown !== "1px" && shown !== "0px") {
+ if (shown !== "0px" && !reported.has(shown)) {
+ reported.add(shown);
report(decl, "hardcodedSize", { prop, value: shown });
}
}
@@ -150,12 +154,13 @@ export default {
)
.trim();
if (withoutKeywords !== "" && withoutKeywords !== "0") {
- const match = value.match(COLOR_LITERAL);
- if (match) {
- report(decl, "hardcodedColor", {
- prop,
- value: match[0].trim(),
- });
+ const reported = new Set();
+ for (const match of value.matchAll(COLOR_LITERAL)) {
+ const shown = match[0].trim();
+ if (!reported.has(shown)) {
+ reported.add(shown);
+ report(decl, "hardcodedColor", { prop, value: shown });
+ }
}
}
}
diff --git a/web/src/lib/components/kit/CatalogGroup.svelte b/web/src/lib/components/kit/CatalogGroup.svelte
index 90deefc..0a5b464 100644
--- a/web/src/lib/components/kit/CatalogGroup.svelte
+++ b/web/src/lib/components/kit/CatalogGroup.svelte
@@ -31,7 +31,7 @@
justify-content: space-between;
gap: var(--space-xl);
padding-bottom: var(--space-m);
- border-bottom: 1px solid var(--color-border);
+ border-bottom: var(--size-border) solid var(--color-border);
}
.group-title span {
font: var(--font-size-s) var(--font-mono);
diff --git a/web/src/lib/components/kit/CatalogToolbar.svelte b/web/src/lib/components/kit/CatalogToolbar.svelte
index beb60da..c5e5885 100644
--- a/web/src/lib/components/kit/CatalogToolbar.svelte
+++ b/web/src/lib/components/kit/CatalogToolbar.svelte
@@ -55,7 +55,7 @@
align-items: center;
gap: var(--space-m);
padding: var(--space-m) var(--space-l);
- border: 1px solid var(--color-border);
+ border: var(--size-border) solid var(--color-border);
border-radius: var(--radius-m);
background: var(--color-panel);
color: var(--color-text-muted);
@@ -78,7 +78,7 @@
color: var(--color-text-muted);
}
.catalog-filters button {
- border: 1px solid var(--color-border);
+ border: var(--size-border) solid var(--color-border);
background: transparent;
color: var(--color-text-muted);
font: var(--font-size-s) var(--font-mono);
diff --git a/web/src/lib/components/kit/CheckerCanvas.svelte b/web/src/lib/components/kit/CheckerCanvas.svelte
index ad2b755..1f495e3 100644
--- a/web/src/lib/components/kit/CheckerCanvas.svelte
+++ b/web/src/lib/components/kit/CheckerCanvas.svelte
@@ -22,16 +22,16 @@
overflow: hidden;
background-color: var(--color-text);
background-image:
- linear-gradient(45deg, #1a2129 25%, transparent 25%),
- linear-gradient(-45deg, #1a2129 25%, transparent 25%),
- linear-gradient(45deg, transparent 75%, #1a2129 75%),
- linear-gradient(-45deg, transparent 75%, #1a2129 75%);
+ linear-gradient(45deg, var(--color-checker-main) 25%, transparent 25%),
+ linear-gradient(-45deg, var(--color-checker-main) 25%, transparent 25%),
+ linear-gradient(45deg, transparent 75%, var(--color-checker-main) 75%),
+ linear-gradient(-45deg, transparent 75%, var(--color-checker-main) 75%);
background-size: var(--space-xl) var(--space-xl);
background-position:
0 0,
- 0 10px,
- 10px -10px,
- -10px 0;
+ 0 calc(var(--space-xl) / 2),
+ calc(var(--space-xl) / 2) calc(-1 * var(--space-xl) / 2),
+ calc(-1 * var(--space-xl) / 2) 0;
}
.checker-canvas--sm {
diff --git a/web/src/lib/components/kit/CodeBlock.svelte b/web/src/lib/components/kit/CodeBlock.svelte
index 6f2fee9..2180bba 100644
--- a/web/src/lib/components/kit/CodeBlock.svelte
+++ b/web/src/lib/components/kit/CodeBlock.svelte
@@ -24,7 +24,7 @@
diff --git a/web/src/lib/components/kit/StepCard.svelte b/web/src/lib/components/kit/StepCard.svelte
index 7de116d..a90944a 100644
--- a/web/src/lib/components/kit/StepCard.svelte
+++ b/web/src/lib/components/kit/StepCard.svelte
@@ -45,9 +45,9 @@