From 214226da67fd838858edcf1d5a97916481475d3d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 17 Oct 2026 13:56:01 +0000 Subject: [PATCH] feat(basic): extract theme.config SSOT Move brand seeds into src/theme/theme.config.ts with optional colorPrimary. Co-authored-by: zuiidea --- apps/basic/src/hooks/tokenBuilders.ts | 59 ++++++++++++++------------- apps/basic/src/theme/theme.config.ts | 38 +++++++++++++++++ 2 files changed, 69 insertions(+), 28 deletions(-) create mode 100644 apps/basic/src/theme/theme.config.ts diff --git a/apps/basic/src/hooks/tokenBuilders.ts b/apps/basic/src/hooks/tokenBuilders.ts index 5c31292..32f624f 100644 --- a/apps/basic/src/hooks/tokenBuilders.ts +++ b/apps/basic/src/hooks/tokenBuilders.ts @@ -1,30 +1,31 @@ import { theme } from "antd"; import type { ConfigProviderProps, ThemeConfig } from "antd"; +import { themeConfig } from "@/theme/theme.config"; -/** - * Common theme token builders - * Reduces duplication between light and dark theme definitions - */ +const { fontFamily, borderRadius, borderRadiusSM, borderRadiusLG, colorBgLayoutLight } = themeConfig.token; -export const SHARED_DESIGN_TOKENS = { - fontFamily: - "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif", - - borderRadius: 8, - borderRadiusSM: 6, - borderRadiusLG: 12, +const sharedSeed = { + fontFamily, + borderRadius, + borderRadiusSM, + borderRadiusLG, } as const; -export const CONTROL_COMPONENTS = { - Button: { - primaryShadow: "none", - defaultShadow: "none", - dangerShadow: "none", - }, - Input: { - activeShadow: "none", - }, -} as const; +function readOptionalPrimary(): string | undefined { + if (!("colorPrimary" in themeConfig.token)) return undefined; + const value = (themeConfig.token as { colorPrimary?: string }).colorPrimary; + return typeof value === "string" && value.length > 0 ? value : undefined; +} + +function primaryTokenFields(): Pick, "colorPrimary" | "colorLink" | "colorInfo"> { + const colorPrimary = readOptionalPrimary(); + if (!colorPrimary) return {}; + return { + colorPrimary, + colorLink: colorPrimary, + colorInfo: colorPrimary, + }; +} /** * Calculate table row selected color based on theme algorithm @@ -47,7 +48,7 @@ export const MENU_LIGHT = { itemHoverBg: "rgba(0, 0, 0, 0.04)", itemHoverColor: "rgba(0, 0, 0, 0.88)", itemActiveBg: "rgba(0, 0, 0, 0.08)", - itemBorderRadius: SHARED_DESIGN_TOKENS.borderRadiusSM, + itemBorderRadius: borderRadiusSM, itemMarginInline: 8, horizontalItemSelectedColor: "rgba(0, 0, 0, 0.88)", horizontalItemHoverColor: "rgba(0, 0, 0, 0.88)", @@ -63,7 +64,7 @@ export const MENU_DARK = { itemHoverBg: "rgba(255, 255, 255, 0.06)", itemHoverColor: "rgba(255, 255, 255, 0.85)", itemActiveBg: "rgba(255, 255, 255, 0.1)", - itemBorderRadius: SHARED_DESIGN_TOKENS.borderRadiusSM, + itemBorderRadius: borderRadiusSM, itemMarginInline: 8, horizontalItemSelectedColor: "rgba(255, 255, 255, 0.85)", horizontalItemHoverColor: "rgba(255, 255, 255, 0.85)", @@ -74,8 +75,9 @@ export const MENU_DARK = { */ export function buildLightThemeConfig(): ConfigProviderProps { const lightSeed: ThemeConfig["token"] = { - colorBgLayout: "#ffffff", - ...SHARED_DESIGN_TOKENS, + colorBgLayout: colorBgLayoutLight, + ...sharedSeed, + ...primaryTokenFields(), }; return { @@ -83,7 +85,7 @@ export function buildLightThemeConfig(): ConfigProviderProps { algorithm: theme.defaultAlgorithm, token: lightSeed, components: { - ...CONTROL_COMPONENTS, + ...themeConfig.controlComponents, Table: buildTableTokens({ algorithm: theme.defaultAlgorithm, token: lightSeed, @@ -99,7 +101,8 @@ export function buildLightThemeConfig(): ConfigProviderProps { */ export function buildDarkThemeConfig(): ConfigProviderProps { const darkSeed: ThemeConfig["token"] = { - ...SHARED_DESIGN_TOKENS, + ...sharedSeed, + ...primaryTokenFields(), }; return { @@ -107,7 +110,7 @@ export function buildDarkThemeConfig(): ConfigProviderProps { algorithm: theme.darkAlgorithm, token: darkSeed, components: { - ...CONTROL_COMPONENTS, + ...themeConfig.controlComponents, Table: buildTableTokens({ algorithm: theme.darkAlgorithm, token: darkSeed, diff --git a/apps/basic/src/theme/theme.config.ts b/apps/basic/src/theme/theme.config.ts new file mode 100644 index 0000000..08aee38 --- /dev/null +++ b/apps/basic/src/theme/theme.config.ts @@ -0,0 +1,38 @@ +import type { ThemeConfig } from "antd"; + +type ThemeTokenSeeds = { + fontFamily: string; + borderRadius: number; + borderRadiusSM: number; + borderRadiusLG: number; + /** Light layout background only; dark uses algorithm default */ + colorBgLayoutLight: string; + /** Optional brand primary; omit to use Ant Design default */ + colorPrimary?: string; +}; + +/** + * Project theme seeds. Only place for intentional brand/theme literals. + * Customize per project here (e.g. uncomment colorPrimary). + */ +export const themeConfig = { + token: { + fontFamily: + "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif", + borderRadius: 8, + borderRadiusSM: 6, + borderRadiusLG: 12, + colorBgLayoutLight: "#ffffff", + // colorPrimary: "#1677ff", + } satisfies ThemeTokenSeeds, + controlComponents: { + Button: { + primaryShadow: "none", + defaultShadow: "none", + dangerShadow: "none", + }, + Input: { + activeShadow: "none", + }, + } satisfies NonNullable, +} as const;