From 950f4b8b01a60205c398f7bca482926607ab6e84 Mon Sep 17 00:00:00 2001 From: Barney W <153742108+barney-w@users.noreply.github.com> Date: Sat, 28 Mar 2026 22:22:51 +1000 Subject: [PATCH 1/3] refactor(theme): extract ThemeContext into dedicated module --- packages/theme/src/ThemeContext.ts | 6 ++++++ packages/theme/src/ThemeProvider.native.tsx | 4 +--- packages/theme/src/ThemeProvider.tsx | 4 +--- packages/theme/src/index.ts | 3 ++- packages/theme/src/useTheme.ts | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) create mode 100644 packages/theme/src/ThemeContext.ts diff --git a/packages/theme/src/ThemeContext.ts b/packages/theme/src/ThemeContext.ts new file mode 100644 index 0000000..a2cc3ee --- /dev/null +++ b/packages/theme/src/ThemeContext.ts @@ -0,0 +1,6 @@ +'use client' + +import { createContext } from 'react' +import type { ThemeContextValue } from './types' + +export const ThemeContext = createContext(null) diff --git a/packages/theme/src/ThemeProvider.native.tsx b/packages/theme/src/ThemeProvider.native.tsx index 92e304a..ff0c314 100644 --- a/packages/theme/src/ThemeProvider.native.tsx +++ b/packages/theme/src/ThemeProvider.native.tsx @@ -1,7 +1,6 @@ 'use client' import React, { - createContext, useCallback, useEffect, useMemo, @@ -14,14 +13,13 @@ import type { Theme, ThemeContextValue, } from './types' +import { ThemeContext } from './ThemeContext' const DEFAULT_THEME: Theme = { name: 'default', cssVars: {}, } -export const ThemeContext = createContext(null) - function getSystemPreference(): ColorMode { return (Appearance.getColorScheme() as ColorMode) ?? 'light' } diff --git a/packages/theme/src/ThemeProvider.tsx b/packages/theme/src/ThemeProvider.tsx index f4f5d39..51c7aed 100644 --- a/packages/theme/src/ThemeProvider.tsx +++ b/packages/theme/src/ThemeProvider.tsx @@ -1,7 +1,6 @@ 'use client' import React, { - createContext, useCallback, useEffect, useMemo, @@ -14,14 +13,13 @@ import type { Theme, ThemeContextValue, } from './types' +import { ThemeContext } from './ThemeContext' const DEFAULT_THEME: Theme = { name: 'default', cssVars: {}, } -export const ThemeContext = createContext(null) - function getSystemPreference(): ColorMode { if (typeof window === 'undefined') return 'light' return window.matchMedia('(prefers-color-scheme: dark)').matches diff --git a/packages/theme/src/index.ts b/packages/theme/src/index.ts index c0ffbf9..0a56ebe 100644 --- a/packages/theme/src/index.ts +++ b/packages/theme/src/index.ts @@ -1,4 +1,5 @@ -export { ThemeProvider, ThemeContext } from './ThemeProvider' +export { ThemeProvider } from './ThemeProvider' +export { ThemeContext } from './ThemeContext' export { useTheme } from './useTheme' export { useColorMode } from './useColorMode' export { createTheme } from './createTheme' diff --git a/packages/theme/src/useTheme.ts b/packages/theme/src/useTheme.ts index a60cfd8..c70c55a 100644 --- a/packages/theme/src/useTheme.ts +++ b/packages/theme/src/useTheme.ts @@ -1,7 +1,7 @@ 'use client' import { useContext } from 'react' -import { ThemeContext } from './ThemeProvider' +import { ThemeContext } from './ThemeContext' import type { ThemeContextValue } from './types' /** From 0497abd9eaf967231d0582658bba94be21fa8bac Mon Sep 17 00:00:00 2001 From: Barney W <153742108+barney-w@users.noreply.github.com> Date: Sat, 28 Mar 2026 22:22:54 +1000 Subject: [PATCH 2/3] feat(theme): add energy colour mode type --- .changeset/energy-colour-mode.md | 6 ++++++ packages/theme/src/types.ts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/energy-colour-mode.md diff --git a/.changeset/energy-colour-mode.md b/.changeset/energy-colour-mode.md new file mode 100644 index 0000000..7184ec2 --- /dev/null +++ b/.changeset/energy-colour-mode.md @@ -0,0 +1,6 @@ +--- +'@surf-kit/theme': minor +'@surf-kit/tokens': minor +--- + +Add energy colour mode with dedicated token set and build pipeline diff --git a/packages/theme/src/types.ts b/packages/theme/src/types.ts index adfe6ed..c469eaf 100644 --- a/packages/theme/src/types.ts +++ b/packages/theme/src/types.ts @@ -1,5 +1,5 @@ /** Resolved color mode applied to the DOM */ -export type ColorMode = 'light' | 'dark' | 'brand' +export type ColorMode = 'light' | 'dark' | 'brand' | 'energy' /** User-facing color mode preference (includes 'system') */ export type ColorModePreference = ColorMode | 'system' From 994c591fc5618957269ad3e8c2d46f48def057e6 Mon Sep 17 00:00:00 2001 From: Barney W <153742108+barney-w@users.noreply.github.com> Date: Sat, 28 Mar 2026 22:22:57 +1000 Subject: [PATCH 3/3] feat(tokens): add energy colour mode token build --- packages/tokens/package.json | 5 +++ packages/tokens/src/config.ts | 54 ++++++++++++++++++++++++ packages/tokens/src/semantic/energy.json | 38 +++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 packages/tokens/src/semantic/energy.json diff --git a/packages/tokens/package.json b/packages/tokens/package.json index cdc3dfd..c5fee4d 100644 --- a/packages/tokens/package.json +++ b/packages/tokens/package.json @@ -22,6 +22,7 @@ "./css/light": "./dist/css/variables-light.css", "./css/dark": "./dist/css/variables-dark.css", "./css/brand": "./dist/css/variables-brand.css", + "./css/energy": "./dist/css/variables-energy.css", "./ts": { "types": "./dist/ts/tokens.d.ts", "default": "./dist/ts/tokens.js" @@ -43,6 +44,10 @@ "types": "./dist/native/tokens-brand.ts", "default": "./dist/native/tokens-brand.ts" }, + "./native/energy": { + "types": "./dist/native/tokens-energy.ts", + "default": "./dist/native/tokens-energy.ts" + }, ".": "./dist/ts/tokens.js" }, "repository": { diff --git a/packages/tokens/src/config.ts b/packages/tokens/src/config.ts index c4f0311..cb79e6b 100644 --- a/packages/tokens/src/config.ts +++ b/packages/tokens/src/config.ts @@ -82,6 +82,21 @@ StyleDictionary.registerFormat({ }, }); +// Register custom format that wraps output in [data-color-mode="energy"] { ... } +StyleDictionary.registerFormat({ + name: "css/variables-energy", + format: ({ dictionary, options }) => { + const selector = '[data-color-mode="energy"]'; + const vars = formattedVariables({ + format: "css", + dictionary, + outputReferences: options?.outputReferences ?? false, + usesDtcg: true, + }); + return `@layer surf.tokens {\n ${selector} {\n${vars}\n }\n}\n`; + }, +}); + // Register format for resolved JS objects (React Native fallback) StyleDictionary.registerFormat({ name: 'js/nested-object', @@ -248,6 +263,45 @@ async function build() { await brandSD.buildAllPlatforms(); console.log("Brand build complete."); + // ── Energy build ──────────────────────────────────────────── + const energySD = new StyleDictionary({ + include: [...primitives, componentTokens], + source: [ + resolve(root, "src/semantic/energy.json"), + ], + platforms: { + css: { + transformGroup: "css", + prefix: "surf", + buildPath: resolve(root, "dist/css") + "/", + files: [ + { + destination: "variables-energy.css", + format: "css/variables-energy", + options: { + outputReferences: false, + }, + }, + ], + }, + 'react-native': { + transformGroup: 'js', + prefix: 'surf', + buildPath: resolve(root, 'dist/native') + '/', + files: [ + { + destination: 'tokens-energy.ts', + format: 'js/nested-object', + }, + ], + }, + }, + }); + + await energySD.hasInitialized; + await energySD.buildAllPlatforms(); + console.log("Energy build complete."); + console.log("All token builds complete."); } diff --git a/packages/tokens/src/semantic/energy.json b/packages/tokens/src/semantic/energy.json new file mode 100644 index 0000000..c358db8 --- /dev/null +++ b/packages/tokens/src/semantic/energy.json @@ -0,0 +1,38 @@ +{ + "color": { + "bg": { + "canvas": { "$value": "#ffffff" }, + "surface": { "$value": "#ffffff" }, + "surface-raised": { "$value": "#ffffff" }, + "surface-sunken": { "$value": "{color.charcoal.50}" } + }, + "text": { + "primary": { "$value": "{color.charcoal.900}" }, + "secondary": { "$value": "{color.charcoal.500}" }, + "muted": { "$value": "{color.charcoal.300}" }, + "inverse": { "$value": "#ffffff" } + }, + "accent": { + "primary": { "$value": "{color.watermelon.500}" }, + "primary-hover": { "$value": "#d00f4a" }, + "primary-active": { "$value": "{color.watermelon.700}" }, + "primary-subtle": { "$value": "rgba(232,23,93,0.12)" }, + "primary-subtlest": { "$value": "rgba(232,23,93,0.08)" } + }, + "border": { + "default": { "$value": "{color.charcoal.100}" }, + "strong": { "$value": "{color.charcoal.300}" }, + "interactive": { "$value": "{color.watermelon.500}" } + }, + "status": { + "success": { "$value": "#16a34a" }, + "success-subtle": { "$value": "rgba(22,163,74,0.12)" }, + "warning": { "$value": "#d97706" }, + "warning-subtle": { "$value": "rgba(217,119,6,0.12)" }, + "error": { "$value": "#b91c1c" }, + "error-subtle": { "$value": "rgba(185,28,28,0.12)" }, + "info": { "$value": "#0284c7" }, + "info-subtle": { "$value": "rgba(2,132,199,0.12)" } + } + } +}