Files
ttstd-infinite-canvas/web/src/lib/app-theme.ts

315 lines
13 KiB
TypeScript

import type { ThemeConfig } from "antd";
import { theme as antdTheme } from "antd";
// 主操作、普通选择和开关是不同交互语义,必须各自维护成对的背景/前景色。
const controlTheme = {
light: {
solidBg: "#171717",
solidHoverBg: "#303030",
solidActiveBg: "#404040",
solidFg: "#ffffff",
checkBg: "#171717",
checkHoverBg: "#303030",
checkActiveBg: "#404040",
checkFg: "#ffffff",
switchCheckedBg: "#171717",
switchCheckedHoverBg: "#303030",
switchCheckedHandle: "#ffffff",
selectedBg: "rgba(17, 17, 17, 0.09)",
selectedHoverBg: "rgba(17, 17, 17, 0.13)",
selectedActiveBg: "rgba(17, 17, 17, 0.16)",
selectedFg: "#171717",
controlSurface: "#ffffff",
controlBorder: "rgba(17, 17, 17, 0.18)",
controlDisabledBg: "rgba(17, 17, 17, 0.05)",
controlDisabledFg: "rgba(17, 17, 17, 0.36)",
controlFocus: "rgba(17, 17, 17, 0.24)",
switchOffBg: "rgba(17, 17, 17, 0.22)",
switchOffHoverBg: "rgba(17, 17, 17, 0.3)",
switchOffHandle: "#ffffff",
menuBg: "#f5f5f5",
menuText: "#171717",
selectActiveBg: "rgba(17, 17, 17, 0.035)",
selectSelectedBg: "rgba(17, 17, 17, 0.065)",
selectText: "#171717",
tableSelectedBg: "rgba(17, 17, 17, 0.05)",
tableSelectedHoverBg: "rgba(17, 17, 17, 0.08)",
},
dark: {
solidBg: "#f5f5f5",
solidHoverBg: "#ffffff",
solidActiveBg: "#e5e5e5",
solidFg: "#171717",
checkBg: "#f5f5f5",
checkHoverBg: "#ffffff",
checkActiveBg: "#e5e5e5",
checkFg: "#131313",
switchCheckedBg: "#f5f5f5",
switchCheckedHoverBg: "#ffffff",
switchCheckedHandle: "#131313",
selectedBg: "rgba(255, 255, 255, 0.12)",
selectedHoverBg: "rgba(255, 255, 255, 0.16)",
selectedActiveBg: "rgba(255, 255, 255, 0.2)",
selectedFg: "#f5f5f5",
controlSurface: "rgba(255, 255, 255, 0.035)",
controlBorder: "rgba(255, 255, 255, 0.24)",
controlDisabledBg: "rgba(255, 255, 255, 0.06)",
controlDisabledFg: "rgba(255, 255, 255, 0.38)",
controlFocus: "rgba(255, 255, 255, 0.24)",
switchOffBg: "rgba(255, 255, 255, 0.22)",
switchOffHoverBg: "rgba(255, 255, 255, 0.3)",
switchOffHandle: "#f5f5f5",
menuBg: "#262626",
menuText: "#fafafa",
selectActiveBg: "rgba(255, 255, 255, 0.055)",
selectSelectedBg: "rgba(255, 255, 255, 0.09)",
selectText: "#fafafa",
tableSelectedBg: "rgba(255, 255, 255, 0.08)",
tableSelectedHoverBg: "rgba(255, 255, 255, 0.12)",
},
};
export function getAntThemeConfig(dark: boolean): ThemeConfig {
const color = dark ? controlTheme.dark : controlTheme.light;
const elevatedBackground = dark ? "rgba(31, 31, 32, 0.96)" : "rgba(255, 255, 255, 0.96)";
const subtleBackground = dark ? "rgba(255, 255, 255, 0.055)" : "rgba(17, 17, 17, 0.035)";
const interactiveBorder = dark ? "rgba(255, 255, 255, 0.18)" : "rgba(17, 17, 17, 0.18)";
// 黑白主题使用边框表达焦点,避免输入控件周围出现蓝紫色光圈。
const focusShadow = "none";
return {
algorithm: dark ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
cssVar: { key: dark ? "infinite-canvas-dark" : "infinite-canvas-light" },
token: {
colorPrimary: color.solidBg,
colorPrimaryHover: color.solidHoverBg,
colorPrimaryActive: color.solidActiveBg,
colorPrimaryBg: color.selectedBg,
colorPrimaryBgHover: color.selectedHoverBg,
colorInfo: color.solidBg,
colorLink: color.solidBg,
colorLinkHover: color.solidHoverBg,
colorLinkActive: color.solidActiveBg,
colorTextLightSolid: color.solidFg,
colorBgElevated: elevatedBackground,
colorBorderSecondary: dark ? "rgba(255, 255, 255, 0.1)" : "rgba(17, 17, 17, 0.09)",
boxShadowSecondary: dark ? "0 24px 72px rgba(0, 0, 0, 0.48)" : "0 22px 64px rgba(15, 23, 42, 0.14)",
borderRadius: 6,
borderRadiusLG: 8,
borderRadiusSM: 5,
controlHeight: 36,
controlHeightLG: 42,
controlHeightSM: 30,
fontSize: 13,
fontSizeSM: 12,
motionDurationFast: "0.12s",
motionDurationMid: "0.18s",
motionDurationSlow: "0.24s",
},
components: {
Button: {
primaryShadow: "none",
fontWeight: 500,
paddingInline: 14,
paddingInlineLG: 16,
paddingInlineSM: 10,
colorPrimary: color.solidBg,
colorPrimaryHover: color.solidHoverBg,
colorPrimaryActive: color.solidActiveBg,
primaryColor: color.solidFg,
defaultBg: "transparent",
defaultColor: color.selectedFg,
defaultBorderColor: color.controlBorder,
defaultHoverBg: color.selectedBg,
defaultHoverColor: color.selectedFg,
defaultHoverBorderColor: color.controlBorder,
defaultActiveBg: color.selectedHoverBg,
defaultActiveColor: color.selectedFg,
defaultActiveBorderColor: color.controlBorder,
},
Input: {
paddingInline: 11,
activeBg: elevatedBackground,
hoverBg: elevatedBackground,
activeBorderColor: interactiveBorder,
hoverBorderColor: interactiveBorder,
activeShadow: focusShadow,
},
InputNumber: {
activeBg: elevatedBackground,
hoverBg: elevatedBackground,
activeBorderColor: interactiveBorder,
hoverBorderColor: interactiveBorder,
activeShadow: focusShadow,
},
Switch: {
handleBg: color.switchOffHandle,
handleShadow: dark ? "0 1px 4px rgba(0, 0, 0, 0.42)" : "0 1px 2px rgba(0, 0, 0, 0.2)",
colorPrimary: color.switchCheckedBg,
colorPrimaryActive: color.switchCheckedBg,
colorPrimaryHover: color.switchCheckedHoverBg,
colorTextQuaternary: color.switchOffBg,
colorTextTertiary: color.switchOffHoverBg,
controlOutline: color.controlFocus,
},
Checkbox: {
colorBgContainer: color.controlSurface,
colorBgContainerDisabled: color.controlDisabledBg,
colorBorder: color.controlBorder,
colorPrimary: color.checkBg,
colorPrimaryActive: color.checkActiveBg,
colorPrimaryHover: color.checkHoverBg,
controlOutline: color.controlFocus,
},
Radio: {
radioSize: 16,
dotSize: 6,
buttonBg: color.controlSurface,
buttonCheckedBg: color.selectedBg,
buttonCheckedBgDisabled: color.controlDisabledBg,
buttonCheckedColorDisabled: color.controlDisabledFg,
buttonSolidCheckedColor: color.checkFg,
buttonSolidCheckedActiveBg: color.checkActiveBg,
buttonSolidCheckedBg: color.checkBg,
buttonSolidCheckedHoverBg: color.checkHoverBg,
colorBgContainer: color.controlSurface,
colorBgContainerDisabled: color.controlDisabledBg,
colorBorder: color.controlBorder,
colorPrimary: color.checkBg,
colorPrimaryActive: color.checkActiveBg,
colorPrimaryHover: color.checkHoverBg,
controlOutline: color.controlFocus,
},
Menu: {
itemActiveBg: color.menuBg,
itemHoverBg: color.menuBg,
itemSelectedBg: color.menuBg,
itemSelectedColor: color.menuText,
darkItemHoverBg: controlTheme.dark.menuBg,
darkItemSelectedBg: controlTheme.dark.menuBg,
darkItemSelectedColor: controlTheme.dark.menuText,
},
Select: {
selectorBg: elevatedBackground,
optionHeight: 36,
optionPadding: "8px 10px",
multipleItemHeight: 24,
activeBorderColor: interactiveBorder,
hoverBorderColor: interactiveBorder,
activeOutlineColor: "transparent",
optionActiveBg: color.selectActiveBg,
optionSelectedBg: color.selectSelectedBg,
optionSelectedColor: color.selectText,
},
Table: {
headerBg: subtleBackground,
headerColor: dark ? "rgba(250, 250, 250, 0.62)" : "rgba(23, 23, 23, 0.58)",
headerBorderRadius: 0,
rowHoverBg: dark ? "rgba(255, 255, 255, 0.035)" : "rgba(17, 17, 17, 0.025)",
borderColor: dark ? "rgba(255, 255, 255, 0.08)" : "rgba(17, 17, 17, 0.075)",
cellPaddingBlockMD: 13,
cellPaddingInlineMD: 14,
rowSelectedBg: color.tableSelectedBg,
rowSelectedHoverBg: color.tableSelectedHoverBg,
},
Pagination: {
itemBg: "transparent",
itemLinkBg: "transparent",
itemActiveBg: color.solidBg,
itemActiveColor: color.solidFg,
itemActiveColorHover: color.solidFg,
},
Segmented: {
trackBg: subtleBackground,
trackPadding: 3,
itemColor: color.controlDisabledFg,
itemHoverColor: color.selectedFg,
itemHoverBg: color.selectedBg,
itemActiveBg: color.selectedActiveBg,
itemSelectedBg: color.selectedBg,
itemSelectedColor: color.selectedFg,
},
Modal: {
headerBg: "transparent",
contentBg: elevatedBackground,
footerBg: "transparent",
titleFontSize: 16,
},
Form: {
itemMarginBottom: 18,
labelFontSize: 12,
verticalLabelPadding: "0 0 6px",
},
Dropdown: {
paddingBlock: 6,
},
Skeleton: {
gradientFromColor: dark ? "rgba(255, 255, 255, 0.055)" : "rgba(15, 23, 42, 0.055)",
gradientToColor: dark ? "rgba(255, 255, 255, 0.11)" : "rgba(15, 23, 42, 0.1)",
},
Card: {
headerBg: "transparent",
headerFontSize: 15,
bodyPadding: 18,
},
},
};
}
/**
* 管理后台使用更高密度的控件节奏;只在 AdminShell 内挂载,避免改变创作端的视觉契约。
*/
export function getAdminAntThemeConfig(dark: boolean): ThemeConfig {
const base = getAntThemeConfig(dark);
const mutedForeground = dark ? "rgba(250, 250, 250, 0.58)" : "rgba(23, 23, 23, 0.58)";
return {
...base,
token: {
...base.token,
borderRadius: 6,
borderRadiusLG: 8,
colorBgContainer: "var(--color-surface)",
colorBorder: "var(--color-border)",
fontFamily: "var(--font-sans)",
padding: 12,
paddingSM: 8,
fontSize: 13,
},
components: {
...base.components,
Card: {
...base.components?.Card,
boxShadow: "none",
boxShadowTertiary: "none",
},
Drawer: {
...base.components?.Drawer,
colorBgElevated: "var(--color-surface)",
},
Form: {
...base.components?.Form,
itemMarginBottom: 16,
},
Menu: {
...base.components?.Menu,
itemHeight: 34,
itemSelectedBg: "transparent",
},
Table: {
...base.components?.Table,
cellPaddingBlock: 8,
cellPaddingBlockMD: 8,
cellPaddingBlockSM: 6,
cellPaddingInline: 12,
cellPaddingInlineMD: 12,
cellPaddingInlineSM: 8,
headerBg: "transparent",
headerColor: mutedForeground,
headerSplitColor: "transparent",
borderColor: "var(--color-border)",
},
},
};
}