From 7c13dbbb30a3b3f2ae95651e53f3ad14c4be8540 Mon Sep 17 00:00:00 2001 From: Joel Bystedt Date: Thu, 9 Apr 2026 09:44:56 +0000 Subject: [PATCH] [01253] Add box shadow tokens to design system Add shadow tokens (none, sm, md, lg, xl, 2xl, inner) with CSS variable generation, flat CSS, and C# code generation. Update demo app with shadow preview section and add package.json exports. --- demo/src/main.ts | 37 ++++++++++++++++++++++++++++++++++++ demo/src/style.css | 31 ++++++++++++++++++++++++++++++ figma-tokens/$tokens.json | 32 +++++++++++++++++++++++++++++++ package.json | 2 ++ scripts/build.ts | 23 ++++++++++++++++++++++ scripts/generate-csharp.ts | 4 ++-- scripts/generate-css.ts | 4 ++-- scripts/generate-flat-css.ts | 4 ++-- 8 files changed, 131 insertions(+), 6 deletions(-) diff --git a/demo/src/main.ts b/demo/src/main.ts index 5f2fdb0..3ece1bf 100644 --- a/demo/src/main.ts +++ b/demo/src/main.ts @@ -3,6 +3,7 @@ import '@ivy-interactive/ivy-design-system/css/ivy-framework-light' import '@ivy-interactive/ivy-design-system/css/ivy-framework-dark' import '@ivy-interactive/ivy-design-system/css/ivy-framework-chromatic' import '@ivy-interactive/ivy-design-system/css/ivy-framework-neutral' +import '@ivy-interactive/ivy-design-system/css/ivy-framework-shadow' import tokens from '@ivy-interactive/ivy-design-system/tokens' import './style.css' @@ -290,6 +291,41 @@ function createExamples(): HTMLElement { return section } +function createShadowExamples(): HTMLElement { + const section = document.createElement('section') + section.className = 'color-section shadow-section' + section.innerHTML = ` +

Box Shadow Tokens

+
+
+ sm + var(--shadow-sm) +
+
+ md + var(--shadow-md) +
+
+ lg + var(--shadow-lg) +
+
+ xl + var(--shadow-xl) +
+
+ 2xl + var(--shadow-2xl) +
+
+ inner + var(--shadow-inner) +
+
+ ` + return section +} + function init(): void { const app = document.getElementById('app') if (!app) return @@ -341,6 +377,7 @@ function init(): void { } app.appendChild(createExamples()) + app.appendChild(createShadowExamples()) if (ivyFramework.chromatic?.color) { app.appendChild(createSection('Chromatic Colors', ivyFramework.chromatic.color)) diff --git a/demo/src/style.css b/demo/src/style.css index 3971941..c0a3229 100644 --- a/demo/src/style.css +++ b/demo/src/style.css @@ -515,3 +515,34 @@ header code { background-color: var(--color-primary); } +/* Shadow tokens */ +.shadow-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 2rem; + margin-top: 1rem; +} + +.shadow-card { + background: var(--color-background); + border-radius: 8px; + padding: 2rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + min-height: 120px; + justify-content: center; +} + +.shadow-label { + font-weight: 600; + font-size: 1.125rem; + color: var(--color-foreground); +} + +.shadow-card code { + font-size: 0.875rem; + color: var(--color-muted-foreground); +} + diff --git a/figma-tokens/$tokens.json b/figma-tokens/$tokens.json index 5696196..5825dec 100644 --- a/figma-tokens/$tokens.json +++ b/figma-tokens/$tokens.json @@ -728,6 +728,38 @@ "type": "spacing" } } + }, + "shadow": { + "shadow": { + "none": { + "value": "none", + "type": "boxShadow" + }, + "sm": { + "value": "0 1px 2px 0 rgba(0, 0, 0, 0.05)", + "type": "boxShadow" + }, + "md": { + "value": "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)", + "type": "boxShadow" + }, + "lg": { + "value": "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)", + "type": "boxShadow" + }, + "xl": { + "value": "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)", + "type": "boxShadow" + }, + "2xl": { + "value": "0 25px 50px -12px rgba(0, 0, 0, 0.25)", + "type": "boxShadow" + }, + "inner": { + "value": "inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)", + "type": "boxShadow" + } + } } }, "ivy-web": { diff --git a/package.json b/package.json index fb6e510..186eeec 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,8 @@ "./css/ivy-framework-chromatic.css": "./dist/css/ivy-framework-chromatic.css", "./css/ivy-framework-neutral": "./dist/css/ivy-framework-neutral.css", "./css/ivy-framework-neutral.css": "./dist/css/ivy-framework-neutral.css", + "./css/ivy-framework-shadow": "./dist/css/ivy-framework-shadow.css", + "./css/ivy-framework-shadow.css": "./dist/css/ivy-framework-shadow.css", "./css/ivy-web": "./dist/css/ivy-web-light.css", "./css/ivy-web-light": "./dist/css/ivy-web-light.css", "./css/ivy-web-dark": "./dist/css/ivy-web-dark.css", diff --git a/scripts/build.ts b/scripts/build.ts index 3f08f57..0e3a81e 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -41,6 +41,9 @@ interface PackageTokens { padding?: { padding?: ColorTokens; }; + shadow?: { + shadow?: ColorTokens; + }; } interface NormalizedTokens { @@ -124,6 +127,7 @@ async function build() { const ivyFrameworkChromatic = ivyFramework.chromatic || {}; const ivyFrameworkBorderRadius = ivyFramework["border-radius"] || {}; const ivyFrameworkPadding = ivyFramework.padding || {}; + const ivyFrameworkShadow = ivyFramework.shadow || {}; const ivyFrameworkLightTheme = { theme: { light: ivyFramework.theme?.light || {} }, }; @@ -184,6 +188,12 @@ async function build() { false, ivyFrameworkSource ); + await generateCSS( + ivyFrameworkShadow, + "dist/css/ivy-framework-shadow.css", + false, + ivyFrameworkSource + ); await generateCSS( ivyFrameworkLightTheme, "dist/css/ivy-framework-light.css", @@ -222,6 +232,12 @@ async function build() { true, ivyFrameworkSource ); + await generateFlatCSS( + ivyFrameworkShadow, + "dist/css/ivy-framework-shadow-flat.css", + false, + ivyFrameworkSource + ); // Generate CSS for ivy-web console.log("📝 Generating CSS for ivy-web..."); @@ -309,6 +325,13 @@ async function build() { "Ivy.Themes", ivyFrameworkSource ); + await generateCSharp( + ivyFrameworkShadow, + "dist/csharp/IvyFrameworkShadowTokens.cs", + "IvyFrameworkShadowTokens", + "Ivy.Themes", + ivyFrameworkSource + ); await generateCSharp( ivyFrameworkLightTheme, "dist/csharp/IvyFrameworkLightThemeTokens.cs", diff --git a/scripts/generate-csharp.ts b/scripts/generate-csharp.ts index 622092d..38d6188 100644 --- a/scripts/generate-csharp.ts +++ b/scripts/generate-csharp.ts @@ -42,7 +42,7 @@ export function extractTokens( return tokens; } - const categories = ["color", "sizing", "border-radius", "padding"]; + const categories = ["color", "sizing", "border-radius", "padding", "shadow"]; for (const cat of categories) { if (obj[cat]) { tokens.push(...extractTokens(obj[cat], "", cat)); @@ -80,7 +80,7 @@ function resolveTokenReference( value: string, sourceTokens: Record ): string { - const referenceMatch = value.match(/^\{(ivy-framework|ivy-web)\.source\.(color|sizing)\.([\w.-]+)\}$/); + const referenceMatch = value.match(/^\{(ivy-framework|ivy-web)\.source\.(color|sizing|shadow)\.([\w.-]+)\}$/); if (referenceMatch) { const category = referenceMatch[2]; const tokenName = referenceMatch[3]; diff --git a/scripts/generate-css.ts b/scripts/generate-css.ts index 3bcb309..c9a6003 100644 --- a/scripts/generate-css.ts +++ b/scripts/generate-css.ts @@ -9,7 +9,7 @@ function resolveTokenReference( ): string { if (!sourceTokens) return value; - const referenceMatch = value.match(/^\{(ivy-framework|ivy-web)\.source\.(color|sizing)\.([\w.-]+)\}$/); + const referenceMatch = value.match(/^\{(ivy-framework|ivy-web)\.source\.(color|sizing|shadow)\.([\w.-]+)\}$/); if (referenceMatch) { const category = referenceMatch[2]; const tokenName = referenceMatch[3]; @@ -46,7 +46,7 @@ function tokenToCSS(obj: any, prefix = "", sourceTokens?: Record): return css; } - const categories = ["color", "sizing", "border-radius", "padding"]; + const categories = ["color", "sizing", "border-radius", "padding", "shadow"]; for (const category of categories) { if (obj[category]) { css += tokenToCSS(obj[category], category, sourceTokens); diff --git a/scripts/generate-flat-css.ts b/scripts/generate-flat-css.ts index d909e7e..5137e3c 100644 --- a/scripts/generate-flat-css.ts +++ b/scripts/generate-flat-css.ts @@ -83,7 +83,7 @@ function resolveTokenReference( ): string { if (!sourceTokens) return value; - const referenceMatch = value.match(/^\{(ivy-framework|ivy-web)\.source\.(color|sizing)\.([\w.-]+)\}$/); + const referenceMatch = value.match(/^\{(ivy-framework|ivy-web)\.source\.(color|sizing|shadow)\.([\w.-]+)\}$/); if (referenceMatch) { const category = referenceMatch[2]; const tokenName = referenceMatch[3]; @@ -125,7 +125,7 @@ function tokenToFlatCSS( return css; } - const categories = ["color", "sizing", "border-radius", "padding"]; + const categories = ["color", "sizing", "border-radius", "padding", "shadow"]; for (const category of categories) { if (obj[category]) { css += tokenToFlatCSS(obj[category], category, mapping, sourceTokens);