2024-10-02 20:57:06 +08:00

363 lines
23 KiB
TypeScript

import type { AppTheme, MD3ColorSchemeTokens, MD3NeutralTones, MD3Palettes, MD3Tones } from './theme.types';
import {
argbFromHex,
hexFromArgb,
TonalPalette,
} from '@material/material-color-utilities';
/**
* Generates a theme based on the provided color values.
*
* @param {object} options - The color options for the theme. - 主题的颜色选项。
* @param {string} options.primary - The primary color. - 主色。
* @param {string} options.secondary - The secondary color. - 次要颜色。
* @param {string} options.tertiary - The tertiary color. - 第三色。
* @param {string} options.error - The error color. - 错误颜色。
* @param {string} options.info - The info color. - 信息颜色。
* @param {string} options.warning - The warning color. - 警告颜色。
* @param {string} options.success - The success color. - 成功颜色。
* @param {string} options.neutral - The neutral color. - 中性颜色。
* @param {string} options.neutralVariant - The variant of the neutral color. - 中性颜色的变体。
*
* @returns {object} - The generated theme object. - 生成的主题对象。
* @property {MD3Palettes} palette - The color palette of the theme. - 主题的颜色调色板。
* The palette includes primary, secondary, and other colors used throughout the theme. - 调色板包括主要的、次要的和主题中使用的其他颜色。
* @property {MD3Tones} palette.primary - The primary color tones. - 主色调。
* @property {MD3Tones} palette.secondary - The secondary color tones. - 次要颜色调。
* @property {MD3Tones} palette.tertiary - The tertiary color tones. - 第三色调。
* @property {MD3NeutralTones} palette.neutral - The neutral color tones. - 中性颜色调。
* @property {MD3Tones} palette.neutralVariant - The neutral variant color tones. - 中性变体颜色调。
* @property {MD3Tones} palette.error - The error color tones. - 错误颜色调。
* @property {MD3Tones} [palette.info] - The info color tones. - 信息颜色调。
* @property {MD3Tones} [palette.warning] - The warning color tones. - 警告颜色调。
* @property {MD3Tones} [palette.success] - The success color tones. - 成功颜色调。
* @property {object} [palette.common] - Common colors. - 常见颜色。
* @property {string} palette.common.black - The black color. - 黑色。
* @property {string} palette.common.white - The white color. - 白色。
* @property {MD3ColorSchemeTokens} lightScheme - The color scheme for light mode. - 亮色模式的颜色方案。
* The light scheme defines the colors used when the theme is in light mode. - 亮色模式定义了主题在亮色模式下使用的颜色。
* @property {string} lightScheme.primary - The primary color used in light mode. - 亮色模式下使用的主色。
* @property {string} lightScheme.onPrimary - The color used for text and icons on top of the primary color. - 用于主色上的文本和图标的颜色。
* @property {string} lightScheme.primaryContainer - The primary container color used in light mode. - 亮色模式下使用的主容器颜色。
* @property {string} lightScheme.onPrimaryContainer - The color used for text and icons on top of the primary container color. - 用于主容器颜色上的文本和图标的颜色。
* @property {string} lightScheme.secondary - The secondary color used in light mode. - 亮色模式下使用的次要颜色。
* @property {string} lightScheme.onSecondary - The color used for text and icons on top of the secondary color. - 用于次要颜色上的文本和图标的颜色。
* @property {string} lightScheme.secondaryContainer - The secondary container color used in light mode. - 亮色模式下使用的次要容器颜色。
* @property {string} lightScheme.onSecondaryContainer - The color used for text and icons on top of the secondary container color. - 用于次要容器颜色上的文本和图标的颜色。
* @property {string} lightScheme.tertiary - The tertiary color used in light mode. - 亮色模式下使用的第三色。
* @property {string} lightScheme.onTertiary - The color used for text and icons on top of the tertiary color. - 用于第三色上的文本和图标的颜色。
* @property {string} lightScheme.tertiaryContainer - The tertiary container color used in light mode. - 亮色模式下使用的第三色容器颜色。
* @property {string} lightScheme.onTertiaryContainer - The color used for text and icons on top of the tertiary container color. - 用于第三色容器颜色上的文本和图标的颜色。
* @property {string} lightScheme.error - The error color used in light mode. - 亮色模式下使用的错误颜色。
* @property {string} lightScheme.onError - The color used for text and icons on top of the error color. - 用于错误颜色上的文本和图标的颜色。
* @property {string} lightScheme.errorContainer - The error container color used in light mode. - 亮色模式下使用的错误容器颜色。
* @property {string} lightScheme.onErrorContainer - The color used for text and icons on top of the error container color. - 用于错误容器颜色上的文本和图标的颜色。
* @property {string} [lightScheme.info] - The info color used in light mode. - 亮色模式下使用的信息颜色。
* @property {string} [lightScheme.onInfo] - The color used for text and icons on top of the info color. - 用于信息颜色上的文本和图标的颜色。
* @property {string} [lightScheme.infoContainer] - The info container color used in light mode. - 亮色模式下使用的信息容器颜色。
* @property {string} [lightScheme.onInfoContainer] - The color used for text and icons on top of the info container color. - 用于信息容器颜色上的文本和图标的颜色。
* @property {string} [lightScheme.warning] - The warning color used in light mode. - 亮色模式下使用的警告颜色。
* @property {string} [lightScheme.onWarning] - The color used for text and icons on top of the warning color. - 用于警告颜色上的文本和图标的颜色。
* @property {string} [lightScheme.warningContainer] - The warning container color used in light mode. - 亮色模式下使用的警告容器颜色。
* @property {string} [lightScheme.onWarningContainer] - The color used for text and icons on top of the warning container color. - 用于警告容器颜色上的文本和图标的颜色。
* @property {string} [lightScheme.success] - The success color used in light mode. - 亮色模式下使用的成功颜色。
* @property {string} [lightScheme.onSuccess] - The color used for text and icons on top of the success color. - 用于成功颜色上的文本和图标的颜色。
* @property {string} [lightScheme.successContainer] - The success container color used in light mode. - 亮色模式下使用的成功容器颜色。
* @property {string} [lightScheme.onSuccessContainer] - The color used for text and icons on top of the success container color. - 用于成功容器颜色上的文本和图标的颜色。
* @property {string} lightScheme.background - The background color used in light mode. - 亮色模式下使用的背景颜色。
* @property {string} lightScheme.onBackground - The color used for text and icons on top of the background color. - 用于背景颜色上的文本和图标的颜色。
* @property {string} lightScheme.surface - The surface color used in light mode. - 亮色模式下使用的表面颜色。
* @property {string} lightScheme.onSurface - The color used for text and icons on top of the surface color. - 用于表面颜色上的文本和图标的颜色。
* @property {string} lightScheme.surfaceVariant - The surface variant color used in light mode. - 亮色模式下使用的表面变体颜色。
* @property {string} lightScheme.onSurfaceVariant - The color used for text and icons on top of the surface variant color. - 用于表面变体颜色上的文本和图标的颜色。
* @property {string} lightScheme.surfaceContainerLowest - The lowest surface container color used in light mode. - 亮色模式下使用的最低表面容器颜色。
* @property {string} lightScheme.surfaceContainerLow - The low surface container color used in light mode. - 亮色模式下使用的低表面容器颜色。
* @property {string} lightScheme.surfaceContainer - The surface container color used in light mode. - 亮色模式下使用的表面容器颜色。
* @property {string} lightScheme.surfaceContainerHigh - The high surface container color used in light mode. - 亮色模式下使用的高表面容器颜色。
* @property {string} lightScheme.surfaceContainerHighest - The highest surface container color used in light mode. - 亮色模式下使用的最高表面容器颜色。
* @property {string} lightScheme.inverseSurface - The inverse surface color used in light mode. - 亮色模式下使用的反色表面颜色。
* @property {string} lightScheme.inverseOnSurface - The color used for text and icons on top of the inverse surface color. - 用于反色表面颜色上的文本和图标的颜色。
* @property {string} lightScheme.inversePrimary - The inverse primary color used in light mode. - 亮色模式下使用的反色主色。
* @property {string} lightScheme.surfaceTint - The surface tint color used in light mode. - 亮色模式下使用的表面色调。
* @property {string} lightScheme.outline - The outline color used in light mode. - 亮色模式下使用的轮廓颜色。
* @property {string} lightScheme.outlineVariant - The outline variant color used in light mode. - 亮色模式下使用的轮廓变体颜色。
* @property {string} lightScheme.shadow - The shadow color used in light mode. - 亮色模式下使用的阴影颜色。
* @property {MD3ColorSchemeTokens} darkScheme - The color scheme for dark mode. - 暗色模式的颜色方案。
* The dark scheme defines the colors used when the theme is in dark mode. - 暗色模式定义了主题在暗色模式下使用的颜色。
* @property {string} darkScheme.primary - The primary color used in dark mode. - 暗色模式下使用的主色。
* @property {string} darkScheme.onPrimary - The color used for text and icons on top of the primary color. - 用于主色上的文本和图标的颜色。
* @property {string} darkScheme.primaryContainer - The primary container color used in dark mode. - 暗色模式下使用的主容器颜色。
* @property {string} darkScheme.onPrimaryContainer - The color used for text and icons on top of the primary container color. - 用于主容器颜色上的文本和图标的颜色。
* @property {string} darkScheme.secondary - The secondary color used in dark mode. - 暗色模式下使用的次要颜色。
* @property {string} darkScheme.onSecondary - The color used for text and icons on top of the secondary color. - 用于次要颜色上的文本和图标的颜色。
* @property {string} darkScheme.secondaryContainer - The secondary container color used in dark mode. - 暗色模式下使用的次要容器颜色。
* @property {string} darkScheme.onSecondaryContainer - The color used for text and icons on top of the secondary container color. - 用于次要容器颜色上的文本和图标的颜色。
* @property {string} darkScheme.tertiary - The tertiary color used in dark mode. - 暗色模式下使用的第三色。
* @property {string} darkScheme.onTertiary - The color used for text and icons on top of the tertiary color. - 用于第三色上的文本和图标的颜色。
* @property {string} darkScheme.tertiaryContainer - The tertiary container color used in dark mode. - 暗色模式下使用的第三色容器颜色。
* @property {string} darkScheme.onTertiaryContainer - The color used for text and icons on top of the tertiary container color. - 用于第三色容器颜色上的文本和图标的颜色。
* @property {string} darkScheme.error - The error color used in dark mode. - 暗色模式下使用的错误颜色。
* @property {string} darkScheme.onError - The color used for text and icons on top of the error color. - 用于错误颜色上的文本和图标的颜色。
* @property {string} darkScheme.errorContainer - The error container color used in dark mode. - 暗色模式下使用的错误容器颜色。
* @property {string} darkScheme.onErrorContainer - The color used for text and icons on top of the error container color. - 用于错误容器颜色上的文本和图标的颜色。
* @property {string} [darkScheme.info] - The info color used in dark mode. - 暗色模式下使用的信息颜色。
* @property {string} [darkScheme.onInfo] - The color used for text and icons on top of the info color. - 用于信息颜色上的文本和图标的颜色。
* @property {string} [darkScheme.infoContainer] - The info container color used in dark mode. - 暗色模式下使用的信息容器颜色。
* @property {string} [darkScheme.onInfoContainer] - The color used for text and icons on top of the info container color. - 用于信息容器颜色上的文本和图标的颜色。
* @property {string} [darkScheme.warning] - The warning color used in dark mode. - 暗色模式下使用的警告颜色。
* @property {string} [darkScheme.onWarning] - The color used for text and icons on top of the warning color. - 用于警告颜色上的文本和图标的颜色。
* @property {string} [darkScheme.warningContainer] - The warning container color used in dark mode. - 暗色模式下使用的警告容器颜色。
* @property {string} [darkScheme.onWarningContainer] - The color used for text and icons on top of the warning container color. - 用于警告容器颜色上的文本和图标的颜色。
* @property {string} [darkScheme.success] - The success color used in dark mode. - 暗色模式下使用的成功颜色。
* @property {string} [darkScheme.onSuccess] - The color used for text and icons on top of the success color. - 用于成功颜色上的文本和图标的颜色。
* @property {string} [darkScheme.successContainer] - The success container color used in dark mode. - 暗色模式下使用的成功容器颜色。
* @property {string} [darkScheme.onSuccessContainer] - The color used for text and icons on top of the success container color. - 用于成功容器颜色上的文本和图标的颜色。
* @property {string} darkScheme.background - The background color used in dark mode. - 暗色模式下使用的背景颜色。
* @property {string} darkScheme.onBackground - The color used for text and icons on top of the background color. - 用于背景颜色上的文本和图标的颜色。
* @property {string} darkScheme.surface - The surface color used in dark mode. - 暗色模式下使用的表面颜色。
* @property {string} darkScheme.onSurface - The color used for text and icons on top of the surface color. - 用于表面颜色上的文本和图标的颜色。
* @property {string} darkScheme.surfaceVariant - The surface variant color used in dark mode. - 暗色模式下使用的表面变体颜色。
* @property {string} darkScheme.onSurfaceVariant - The color used for text and icons on top of the surface variant color. - 用于表面变体颜色上的文本和图标的颜色。
* @property {string} darkScheme.surfaceContainerLowest - The lowest surface container color used in dark mode. - 暗色模式下使用的最低表面容器颜色。
* @property {string} darkScheme.surfaceContainerLow - The low surface container color used in dark mode. - 暗色模式下使用的低表面容器颜色。
* @property {string} darkScheme.surfaceContainer - The surface container color used in dark mode. - 暗色模式下使用的表面容器颜色。
* @property {string} darkScheme.surfaceContainerHigh - The high surface container color used in dark mode. - 暗色模式下使用的高表面容器颜色。
* @property {string} darkScheme.surfaceContainerHighest - The highest surface container color used in dark mode. - 暗色模式下使用的最高表面容器颜色。
* @property {string} darkScheme.inverseSurface - The inverse surface color used in dark mode. - 暗色模式下使用的反色表面颜色。
* @property {string} darkScheme.inverseOnSurface - The color used for text and icons on top of the inverse surface color. - 用于反色表面颜色上的文本和图标的颜色。
* @property {string} darkScheme.inversePrimary - The inverse primary color used in dark mode. - 暗色模式下使用的反色主色。
* @property {string} darkScheme.surfaceTint - The surface tint color used in dark mode. - 暗色模式下使用的表面色调。
* @property {string} darkScheme.outline - The outline color used in dark mode. - 暗色模式下使用的轮廓颜色。
* @property {string} darkScheme.outlineVariant - The outline variant color used in dark mode. - 暗色模式下使用的轮廓变体颜色。
* @property {string} darkScheme.shadow - The shadow color used in dark mode. - 暗色模式下使用的阴影颜色。
*/
export function generateTheme({
primary,
secondary,
tertiary,
error,
info,
warning,
success,
neutral,
neutralVariant,
}: {
primary: string
secondary: string
tertiary: string
error: string
info: string
warning: string
success: string
neutral: string
neutralVariant: string
}): AppTheme {
const primaryTonal = TonalPalette.fromInt(argbFromHex(primary));
const secondaryTonal = TonalPalette.fromInt(argbFromHex(secondary));
const tertiaryTonal = TonalPalette.fromInt(argbFromHex(tertiary));
const errorTonal = TonalPalette.fromInt(argbFromHex(error));
const infoTonal = TonalPalette.fromInt(argbFromHex(info));
const warningTonal = TonalPalette.fromInt(argbFromHex(warning));
const successTonal = TonalPalette.fromInt(argbFromHex(success));
const neutralTonal = TonalPalette.fromInt(argbFromHex(neutral));
const neutralVariantTonal = TonalPalette.fromInt(argbFromHex(neutralVariant));
const themeColors: {
primary: MD3Tones
secondary: MD3Tones
tertiary: MD3Tones
error: MD3Tones
info: MD3Tones
warning: MD3Tones
success: MD3Tones
neutral: MD3NeutralTones
neutralVariant: MD3Tones
} = {
primary: generateTonalColors(primaryTonal),
secondary: generateTonalColors(secondaryTonal),
tertiary: generateTonalColors(tertiaryTonal),
error: generateTonalColors(errorTonal),
info: generateTonalColors(infoTonal),
warning: generateTonalColors(warningTonal),
success: generateTonalColors(successTonal),
neutral: generateNeutralColors(neutralTonal),
neutralVariant: generateTonalColors(neutralVariantTonal),
};
const palette: MD3Palettes = {
...themeColors,
common: {
white: '#fff',
black: '#000',
},
};
const lightScheme: MD3ColorSchemeTokens = {
// primary
primary: palette.primary[40],
onPrimary: palette.primary[100],
primaryContainer: palette.primary[90],
onPrimaryContainer: palette.primary[10],
// secondary
secondary: palette.secondary[40],
onSecondary: palette.secondary[100],
secondaryContainer: palette.secondary[90],
onSecondaryContainer: palette.secondary[10],
// tertiary
tertiary: palette.tertiary[40],
onTertiary: palette.tertiary[100],
tertiaryContainer: palette.tertiary[90],
onTertiaryContainer: palette.tertiary[10],
// error
error: palette.error[40],
onError: palette.error[100],
errorContainer: palette.error[90],
onErrorContainer: palette.error[10],
// info
info: palette.info[40],
onInfo: palette.info[100],
infoContainer: palette.info[90],
onInfoContainer: palette.info[10],
// warning
warning: palette.warning[40],
onWarning: palette.warning[100],
warningContainer: palette.warning[90],
onWarningContainer: palette.warning[10],
// success
success: palette.success[40],
onSuccess: palette.success[100],
successContainer: palette.success[90],
onSuccessContainer: palette.success[10],
// surface
surface: palette.neutral[98],
onSurface: palette.neutral[10],
surfaceVariant: palette.neutralVariant[90],
onSurfaceVariant: palette.neutralVariant[30],
surfaceContainerLowest: palette.neutral[100],
surfaceContainerLow: palette.neutral[96],
surfaceContainer: palette.neutral[94],
surfaceContainerHigh: palette.neutral[92],
surfaceContainerHighest: palette.neutral[90],
surfaceTint: palette.primary[40],
// outline
outline: palette.neutralVariant[50],
outlineVariant: palette.neutralVariant[80],
shadow: palette.neutral[0],
// background
background: palette.neutral[98],
onBackground: palette.neutral[10],
// inverse
inversePrimary: palette.primary[80],
inverseSurface: palette.neutral[20],
inverseOnSurface: palette.neutral[95],
};
const darkScheme: MD3ColorSchemeTokens = {
// primary
primary: palette.primary[80],
onPrimary: palette.primary[20],
primaryContainer: palette.primary[30],
onPrimaryContainer: palette.primary[90],
// secondary
secondary: palette.secondary[80],
onSecondary: palette.secondary[20],
secondaryContainer: palette.secondary[30],
onSecondaryContainer: palette.secondary[90],
// tertiary
tertiary: palette.tertiary[80],
onTertiary: palette.tertiary[20],
tertiaryContainer: palette.tertiary[30],
onTertiaryContainer: palette.tertiary[90],
// error
error: palette.error[80],
onError: palette.error[20],
errorContainer: palette.error[30],
onErrorContainer: palette.error[90],
// info
info: palette.info[80],
onInfo: palette.info[20],
infoContainer: palette.info[30],
onInfoContainer: palette.info[90],
// warning
warning: palette.warning[80],
onWarning: palette.warning[20],
warningContainer: palette.warning[30],
onWarningContainer: palette.warning[90],
// success
success: palette.success[80],
onSuccess: palette.success[20],
successContainer: palette.success[30],
onSuccessContainer: palette.success[90],
// surface
surface: palette.neutral[6],
onSurface: palette.neutral[90],
surfaceVariant: palette.neutralVariant[30],
onSurfaceVariant: palette.neutralVariant[80],
surfaceContainerLowest: palette.neutral[4],
surfaceContainerLow: palette.neutral[10],
surfaceContainer: palette.neutral[12],
surfaceContainerHigh: palette.neutral[17],
surfaceContainerHighest: palette.neutral[24],
surfaceTint: palette.primary[80],
// outline
outline: palette.neutralVariant[60],
outlineVariant: palette.neutralVariant[30],
shadow: palette.neutral[0],
// background
background: palette.neutral[6],
onBackground: palette.neutral[90],
// inverse
inversePrimary: palette.primary[40],
inverseSurface: palette.neutral[90],
inverseOnSurface: palette.neutral[20],
};
return {
palette,
lightScheme,
darkScheme,
};
}
function generateTonalColors(tonal: TonalPalette): MD3Tones {
const tones: (keyof MD3Tones)[] = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100];
const result: MD3NeutralTones = {} as MD3NeutralTones;
tones.forEach((tone) => {
result[tone] = hexFromArgb(tonal.tone(tone));
});
return result;
}
function generateNeutralColors(tonal: TonalPalette): MD3NeutralTones {
const tones: (keyof MD3NeutralTones)[] = [0, 4, 6, 10, 12, 17, 20, 22, 24, 30, 40, 50, 60, 70, 80, 90, 92, 94, 95, 96, 98, 99, 100];
const result: MD3NeutralTones = {} as MD3NeutralTones;
tones.forEach((tone) => {
result[tone] = hexFromArgb(tonal.tone(tone));
});
return result;
}