feat(styles):定义主题以及全局样式。

This commit is contained in:
徐涛 2024-07-10 22:49:02 +08:00
parent fffd3df059
commit d166f7301e
4 changed files with 333 additions and 14 deletions

View File

@ -1,16 +1,20 @@
import { css } from "@emotion/react";
import "normalize.css";
import { mq } from "./style-predefines";
export const globalStyle = css`
@import-normalize;
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 62.5%;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
`;
export const globalStyle = (theme) =>
css({
[":root"]: {
colorScheme: "light dark",
fontFamily: "Inter, Avenir, Helvetica, Arial, sans-serif",
fontSize: 0.625,
fontSynthesis: "none",
textRendering: "optimizeLegibility",
WebkitFontSmoothing: "antialiased",
MozOsxFontSmoothing: "grayscale",
WebkitTextSizeAdjust: "100%",
backgroundColor: theme.backgroundColor.light,
[mq.dark]: {
backgroundColor: theme.backgroundColor.dark,
},
},
});

104
src/style-predefines.tsx Normal file
View File

@ -0,0 +1,104 @@
import { css, Elevation, FontSizeUnit, MeasureUnit, Theme } from "@emotion/react";
import chroma from "chroma-js";
import type { Property } from "csstype";
export function generatePalette(lightest: string, darkest: string): ColorPalette {
return chroma.scale([lightest, darkest]).mode("lch").colors(10, "hex");
}
export const mq = {
dark: `@media (prefers-color-scheme: dark)`,
[576]: `@media (min-width: 576px)`,
[768]: `@media (min-width: 768px)`,
[992]: `@media (min-width: 992px)`,
[1200]: `@media (min-width: 1200px)`,
};
function transformShadowDefine(
shadow: Elevation,
shadowColor?: chroma.Color = chroma.hsl(0, 0, 0)
): string[] {
return [
`${shadow.offset.width}px`,
`${shadow.offset.height}px`,
`${shadow.blur}px`,
shadowColor.alpha(shadow.opacity).hex(),
];
}
export function elevation(theme: Theme, level: MeasureUnit) {
return css({ boxShadow: transformShadowDefine(theme.elevations[level]) });
}
export function flex(
theme: Theme,
direction: Property.FlexDirection = "row",
justify: Property.JustifyContent = "flex-start",
align: Property.AlignItems = "start",
wrap: Property.FlexWrap = "nowrap",
gap: MeasureUnit = "none"
) {
return css({
display: "flex",
flexDirection: direction,
justifyContent: justify,
alignItems: align,
flexWrap: wrap,
gap: `${theme.spacings[gap]}px`,
});
}
export function paddingHorizontal(theme: Theme, padding: MeasureUnit | number = "none") {
switch (typeof padding) {
case "number":
return css({ paddingLeft: padding, paddingRight: padding });
case "string":
return css({
paddingLeft: theme.spacings[padding],
paddingRight: theme.spacings[padding],
});
}
}
export function paddingVertical(theme: Theme, padding: MeasureUnit | number = "none") {
switch (typeof padding) {
case "number":
return css({ paddingTop: padding, paddingBottom: padding });
case "string":
return css({
paddingTop: theme.spacings[padding],
paddingBottom: theme.spacings[padding],
});
}
}
export function marginHorizontal(theme: Theme, margin: MeasureUnit | number = "none") {
switch (typeof margin) {
case "number":
return css({ marginLeft: margin, marginRight: margin });
case "string":
return css({
marginLeft: theme.spacings[margin],
marginRight: theme.spacings[margin],
});
}
}
export function marginVertical(theme: Theme, margin: MeasureUnit | number = "none") {
switch (typeof margin) {
case "number":
return css({ marginTop: margin, marginBottom: margin });
case "string":
return css({
marginTop: theme.spacings[margin],
marginBottom: theme.spacings[margin],
});
}
}
export function Typography(theme: Theme, fontSize: FontSizeUnit, lineHeightRatio: number = 1.3) {
return css({
fontSize: theme.fontSizes[fontSize],
lineHeight: theme.fontSizes[fontSize] * lineHeightRatio,
});
}

142
src/theme.tsx Normal file
View File

@ -0,0 +1,142 @@
import type { Theme } from "@emotion/react";
import { generatePalette } from "./style-predefines";
const colors: Theme["colors"] = {
dark: generatePalette("#c9c9c9", "#141414"),
gray: generatePalette("#f8f9fa", "#212529"),
red: generatePalette("#fff5f5", "#c92a2a"),
pink: generatePalette("#fff0f6", "#a61e4d"),
grape: generatePalette("#f8f0fc", "#862e9c"),
violet: generatePalette("#f3f0ff", "#5f3dc4"),
indigo: generatePalette("#edf2ff", "#364fc7"),
bllue: generatePalette("#e7f5ff", "#1864ab"),
cyan: generatePalette("#e3fafc", "#0b7285"),
teal: generatePalette("#e6fcf5", "#087f5b"),
green: generatePalette("#ebfbee", "#2b8a3e"),
lime: generatePalette("#f4fce3", "#5c940d"),
yellow: generatePalette("#fff9db", "#e67700"),
orange: generatePalette("#fff4e6", "#d9480f"),
};
export const theme: Partial<Theme> = {
colors,
white: "#ffffff",
black: "#000000",
foregroundColor: {
light: chroma.hsl(0, 0, 0.06).hex(),
dark: chroma.hsl(0, 0, 0.88).hex(),
},
backgroundColor: {
light: chroma.hsl(0, 0, 0.96).hex(),
dark: chroma.hsl(0, 0, 0.18).hex(),
},
successColor: {
light: colors.green[5],
dark: colors.green[7],
},
warnColor: {
light: colors.yellow[5],
dark: colors.yellow[7],
},
dangerColor: {
light: colors.red[5],
dark: colors.red[7],
},
infoColor: {
light: colors.blue[5],
dark: colors.blue[7],
},
spacings: {
none: 0,
xxs: 2,
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
xxl: 40,
},
radius: {
none: 0,
xxs: 1,
xs: 2,
sm: 4,
md: 8,
lg: 16,
xl: 24,
xxl: 32,
},
fontSizes: {
fp: 56,
fps: 48,
f1: 34.7,
f1s: 32,
f2: 29.3,
f2s: 24,
f3: 21.3,
f3s: 20,
f4: 18.7,
f4s: 16,
f5: 14,
f5s: 12,
f6: 10,
f6s: 8.7,
f7: 7.3,
f7s: 6.7,
},
paragraphFontSize: 14,
titleFontSize: {
h1: 56,
h2: 34.7,
h3: 29.3,
h4: 21.3,
h5: 18.7,
h6: 14,
},
elevationColor: {
light: chroma.hsl(0, 0, 0).alpha(0.2).hex(),
dark: chroma.hsl(0, 0, 0).alpha(0.2).hex(),
},
elevations: {
none: {
offset: { width: 0, height: 0 },
blur: 0,
opacity: 0,
},
xxs: {
offset: { width: 1, height: 1 },
blur: 2,
opacity: 0.2,
},
xs: {
offset: { width: 2, height: 2 },
blur: 4,
opacity: 0.2,
},
sm: {
offset: { width: 3, height: 3 },
blur: 6,
opacity: 0.2,
},
md: {
offset: { width: 4, height: 4 },
blur: 8,
opacity: 0.2,
},
lg: {
offset: { width: 5, height: 5 },
blur: 10,
opacity: 0.2,
},
xl: {
offset: { width: 6, height: 6 },
blur: 12,
opacity: 0.2,
},
xxl: {
offset: { width: 7, height: 7 },
blur: 14,
opacity: 0.2,
},
},
};

69
src/typings/theme.d.ts vendored Normal file
View File

@ -0,0 +1,69 @@
import "@emotion/react";
declare module "@emotion/react" {
export type ColorName =
| "dark"
| "gray"
| "red"
| "pink"
| "grape"
| "violet"
| "indigo"
| "blue"
| "cyan"
| "teal"
| "green"
| "lime"
| "yellow"
| "orange";
export type ColorScheme = "light" | "dark";
export type MeasureUnit = "none" | "xxs" | "xs" | "sm" | "md" | "lg" | "xl" | "xxl";
export type FontSizeUnit =
| "fp"
| "fps"
| "f1"
| "f1s"
| "f2"
| "f2s"
| "f3"
| "f3s"
| "f4"
| "f4s"
| "f5"
| "f5s"
| "f6"
| "f6s"
| "f7"
| "f7s";
export type TitleLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
export type ColorPalette = Array<string>;
export type Elevation = {
offset: {
width: number;
height: number;
};
blur: number;
opacity: number;
};
export interface Theme {
colors: Record<ColorName, ColorPalette>;
black: string;
white: string;
primaryColor: Record<ColorScheme, ColorPalette>;
secondaryColor: Record<ColorScheme, ColorPalette>;
backgroundColor: Record<ColorScheme, string>;
foregroundColor: Record<ColorScheme, string>;
successColor: Record<ColorScheme, string>;
warnColor: Record<ColorScheme, string>;
dangerColor: Record<ColorScheme, string>;
infoColor: Record<ColorScheme, string>;
spacings: Record<MeasureUnit, number>;
radius: Record<MeasureUnit, number>;
fontSizes: Record<FontSizeUnit, number>;
paragraphFontSize: number;
titleFontSize: Record<TitleLevel, number>;
elevations: Record<MeasureUnit, Elevation>;
elevationColor: Record<ColorScheme, string>;
}
}