原子样式种类较多,通过工具生成会方便很多。本文探讨使用 TypeScript 构建原子样式文件。


1 模型定义

核心模型两个,

(1) CssConfig,表示基础配置,按需配置。由开发者根据具体使用环境和应用要求进行配置。

(2)AtomicStyleRule,表示原子样式定义规则。一次定义反复使用。随着浏览器对CSS的改进而变动。

代码如下。

/**
* 表示数值,有些值不能整除,使用 from/to/scale 进行描述
*/
interface ValueRange {
from: number
to: number
scale: number
unit: string
}

/**
* CSS 基础配置
*/
interface CssConfig {
/**
* 默认值
*/
default: StringMap
/**
* 根元素,用于声明变量
*/
root: StringMap,
/**
* 单位一的表示
*/
one: ValueRange
/**
* 基础颜色设置
*/
color: StringMap
/**
* 主题设置
*/
theme: StringArrayMap
}

/**
* 原子样式规则
*/
interface AtomicStyleRule {
/**
* 包名
*/
package: string
/**
* 规则描述
*/
desc: string
/**
* 类名语法规则,允许使用[U]-数值,[C]-颜色,[N]-数
*/
syntax: string
/**
* 如果由其他规则组合而成,填写其他规则的 syntax
*/
compose?: string[]
/**
* 如果是直接表示规则,填写具体到样式信息
*/
expr?: string,
/**
* 如果规则涉及到数值,填写数值
*/
units?: string[]
}

interface StyleRuleMap {
[index: string]: AtomicStyleRule;
}

interface StringMap {
[index: string]: string;
}

interface StringArrayMap {
[index: string]: string[];
}

2 数据配置

2.1 CSS基础设置

此处对微信小程序进行了定义。

主题色彩直接使用了 AntDesign 的调色板。


const DefaultConfig: CssConfig = {
default: {
"unit": "full,d5,1,2,4,8,10,20,24,28,32,36,48,64,72,80,96,120,144,256,512",
"theme": "gray,red,yellow,green,orange,gold,purple,magenta,cyan,lime,volcano"
},
root: {
"wx+xcx": "page"
},
one: {from: 1, to: 7.5, scale: 3, unit: "vmin"},
color: {
"primary": "#1890ff",
"black": "#000000",
"white": "#ffffff",
},
theme: {
"gray": ["#f5f5f5", "#f0f0f0", "#d9d9d9", "#bfbfbf", "#8c8c8c", "#595959", "#434343", "#262626", "#1f1f1f", "#141414"],
"red": ["#fff1f0", "#ffccc7", "#ffa39e", "#ff7875", "#ff4d4f", "#f5222d", "#cf1322", "#a8071a", "#820014", "#5c0011"],
"orange": ["#fff7e6", "#ffe7ba", "#ffd591", "#ffc069", "#ffa940", "#fa8c16", "#d46b08", "#ad4e00", "#873800", "#612500"],
"yellow": ["#feffe6", "#ffffb8", "#fffb8f", "#fff566", "#ffec3d", "#fadb14", "#d4b106", "#ad8b00", "#876800", "#614700"],
"green": ["#f6ffed", "#d9f7be", "#b7eb8f", "#95de64", "#73d13d", "#52c41a", "#389e0d", "#237804", "#135200", "#092b00"],
"blue": ["#e6f7ff", "#bae7ff", "#91d5ff", "#69c0ff", "#40a9ff", "#1890ff", "#096dd9", "#0050b3", "#003a8c", "#002766"],
"purple": ["#f9f0ff", "#efdbff", "#d3adf7", "#b37feb", "#9254de", "#722ed1", "#531dab", "#391085", "#22075e", "#120338"],
"magenta": ["#fff0f6", "#ffd6e7", "#ffadd2", "#ff85c0", "#f759ab", "#eb2f96", "#c41d7f", "#9e1068", "#780650", "#520339"],
"cyan": ["#e6fffb", "#b5f5ec", "#87e8de", "#5cdbd3", "#36cfc9", "#13c2c2", "#08979c", "#006d75", "#00474f", "#002329"],
"lime": ["#fcffe6", "#f4ffb8", "#eaff8f", "#d3f261", "#bae637", "#a0d911", "#7cb305", "#5b8c00", "#3f6600", "#254000"],
"gold": ["#fffbe6", "#fff1b8", "#ffe58f", "#ffd666", "#ffc53d", "#faad14", "#d48806", "#ad6800", "#874d00", "#613400"],
"volcano": ["#fff2e8", "#ffd8bf", "#ffbb96", "#ff9c6e", "#ff7a45", "#fa541c", "#d4380d", "#ad2102", "#871400", "#610b00"],
}
}

2.2 原子样式设置

原子样式参照之前的规则。

为了批量使用,此处增加了包名。

包名为 ​​category.attribute.[core|ext] ​​规则,其中 core 是常用的,ext 是选用的。


const DefaultStyles: AtomicStyleRule[] = [
{
package: "spacing.padding",
desc: "padding-all",
syntax: "p-[U]",
compose: ['pl-[U]', 'pr-[U]', 'pt-[U]', 'pb-[U]']
},
{
package: "spacing.padding",
desc: "padding-x-axis",
syntax: "px-[U]",
compose: ['pl-[U]', 'pr-[U]']
},
{
package: "spacing.padding",
desc: "padding-y-axis",
syntax: "py-[U]",
compose: ['pt-[U]', 'pb-[U]']
},
{
package: "spacing.padding",
desc: "padding-top",
syntax: "pt-[U]",
expr: "padding-top: var(--unit-[U]);"
},
{
package: "spacing.padding",
desc: "padding-bottom",
syntax: "pb-[U]",
expr: "padding-bottom: var(--unit-[U]);"
},
{
package: "spacing.padding",
desc: "padding-left",
syntax: "pl-[U]",
expr: "padding-left: var(--unit-[U]);"
},
{
package: "spacing.padding",
desc: "padding-right",
syntax: "pr-[U]",
expr: "padding-right: var(--unit-[U]);"
},
{
package: "spacing.margin.ext",
desc: "margin-all",
syntax: "m-[U]",
compose: ['ml-[U]', 'mr-[U]', 'mt-[U]', 'mb-[U]']
},
{
package: "spacing.margin.ext",
desc: "margin-x-axis",
syntax: "mx-[U]",
compose: ['ml-[U]', 'mr-[U]']
},
{
package: "spacing.margin.ext",
desc: "margin-y-axis",
syntax: "my-[U]",
compose: ['mt-[U]', 'mb-[U]']
},
{
package: "spacing.margin.ext",
desc: "margin-top",
syntax: "mt-[U]",
expr: "margin-top: var(--unit-[U]);"
},
{
package: "spacing.margin.ext",
desc: "margin-bottom",
syntax: "mb-[U]",
expr: "margin-bottom: var(--unit-[U]);"
},
{
package: "spacing.margin.ext",
desc: "margin-left",
syntax: "ml-[U]",
expr: "margin-left: var(--unit-[U]);"
},
{
package: "spacing.margin.ext",
desc: "margin-right",
syntax: "mr-[U]",
expr: "margin-right: var(--unit-[U]);"
},
{
package: "sizing.size.ext",
desc: "width and height",
syntax: "wh-[U]",
compose: ['w-[U]', 'w-[U]']
},
{
package: "sizing.width.ext",
desc: "width",
syntax: "w-[U]",
expr: "width: var(--unit-[U]);"
},
{
package: "sizing.width.ext",
desc: "min-width",
syntax: "mw-[U]",
expr: "min-width: var(--unit-[U]);"
},
{
package: "sizing.width.ext",
desc: "max-width",
syntax: "xw-[U]",
expr: "max-width: var(--unit-[U]);"
},
{
package: "sizing.height.ext",
desc: "height",
syntax: "h-[U]",
expr: "height: var(--unit-[U]);"
},
{
package: "sizing.height.ext",
desc: "min-height",
syntax: "mh-[U]",
expr: "min-height: var(--unit-[U]);"
},
{
package: "sizing.height.ext",
desc: "max-height",
syntax: "xh-[U]",
expr: "max-height: var(--unit-[U]);"
},
{
package: "size.gap.ext",
desc: "gap",
syntax: "gap-[U]",
expr: "grid-gap: var(--unit-[U]);"
},


{
package: "layout.float.core",
desc: "position absolute",
syntax: "pos-abs",
expr: "position: absolute;"
},
{
package: "layout.float.core",
desc: "position relative",
syntax: "pos-rel",
expr: "position: relative;"
},
{
package: "layout.float.core",
desc: "position fixed",
syntax: "pos-fix",
expr: "position: fixed;"
},
{
package: "layout.float.core",
desc: "position sticky",
syntax: "pos-sticky",
expr: "position: sticky;"
},
{
package: "layout.float.ext",
desc: "top left corner",
syntax: "pos-tl-[U]",
compose: ['pos-abs', 'top-[U]', 'left-[U]']
},
{
package: "layout.float.ext",
desc: "top right corner",
syntax: "pos-tr-[U]",
compose: ['pos-abs', 'top-[U]', 'right-[U]']
},
{
package: "layout.float.ext",
desc: "bottom left corner",
syntax: "pos-bl-[U]",
compose: ['pos-abs', 'bottom-[U]', 'left-[U]']
},
{
package: "layout.float.ext",
desc: "bottom right corner",
syntax: "pos-br-[U]",
compose: ['pos-abs', 'bottom-[U]', 'right-[U]']
},
{
package: "layout.float.ext",
desc: "top",
syntax: "top-[U]",
expr: "top: var(--unit-[U]);"
},
{
package: "layout.float.ext",
desc: "bottom",
syntax: "bottom-[U]",
expr: "bottom: var(--unit-[U]);"
},
{
package: "layout.float.ext",
desc: "left",
syntax: "left-[U]",
expr: "left: var(--unit-[U]);"
},
{
package: "layout.float.ext",
desc: "right",
syntax: "right-[U]",
expr: "right: var(--unit-[U]);"
},
{
package: "layout.float.ext",
desc: "zIndex",
syntax: "z-[U]",
expr: "z-Index: [U];"
},
{
package: "layout.flex.core",
desc: "flex row",
syntax: "row",
expr: "display: -webkit-box; display: -webkit-flex; display:flex; flex-direction: row;"
},
{
package: "layout.flex.core",
desc: "flex column",
syntax: "col",
expr: "display: -webkit-box; display: -webkit-flex; display:flex; flex-direction: column;"
},
{
package: "layout.flex.core",
desc: "flex row reverse",
syntax: "row-r",
expr: "display: -webkit-box; display: -webkit-flex; display:flex; flex-direction: row-reverse;"
},
{
package: "layout.flex.core",
desc: "flex column reverse",
syntax: "col-r",
expr: "display: -webkit-box; display: -webkit-flex; display:flex; flex-direction: column-reverse;"
},
{
package: "layout.flex.core",
desc: "flex wrap",
syntax: "wrap",
expr: "flex-wrap: wrap;"
},
{
package: "layout.flex.core",
desc: "align items: start",
syntax: "ai-start",
expr: "-webkit-box-align: start;-webkit-align-items: flex-start;align-items: flex-start;"
},
{
package: "layout.flex.core",
desc: "align items: center",
syntax: "ai-center",
expr: "-webkit-box-align: center;-webkit-align-items: center;align-items: center;"
},
{
package: "layout.flex.core",
desc: "align items: end",
syntax: "ai-end",
expr: "-webkit-box-align: end;-webkit-align-items: flex-end;align-items: flex-end;"
},
{
package: "layout.flex.core",
desc: "justify content: start",
syntax: "jc-start",
expr: "-webkit-justify-content: flex-start; justify-content: flex-start;"
},
{
package: "layout.flex.core",
desc: "justify content: center",
syntax: "jc-center",
expr: "-webkit-justify-content: center; justify-content: center;"
},
{
package: "layout.flex.core",
desc: "justify content: end",
syntax: "jc-end",
expr: "-webkit-justify-content: flex-end; justify-content: flex-end;"
},
{
package: "layout.flex.core",
desc: "justify content: space-between",
syntax: "jc-between",
expr: "-webkit-justify-content: space-between; justify-content: space-between;"
},
{
package: "layout.flex.core",
desc: "justify content: space-around",
syntax: "jc-between",
expr: "-webkit-justify-content: space-around; justify-content: space-around;"
},
{
package: "layout.flex.core",
desc: "justify content: space-around",
syntax: "jc-between",
expr: "-webkit-justify-content: space-around; justify-content: space-around;"
},
{
package: "layout.flex.core",
desc: "justify content: space-evenly",
syntax: "jc-between",
expr: "-webkit-justify-content: space-evenly; justify-content: space-evenly;"
},
{
package: "layout.flex.ext",
desc: "flex column",
syntax: "c[N]",
expr: "display:flex; -webkit-box-flex: [N]; -webkit-flex: [N]; flex: [N];"
},
{
package: "layout.grid.ext",
desc: "grid with columns",
syntax: "grid-[N]c",
expr: "display:grid; grid-template-columns: repeat([N], minmax(0, 1fr));"
},
{
package: "text.size.ext",
desc: "text size",
syntax: "text-[U]",
expr: "font-size: var(--unit-[U]);"
},
{
package: "text.color.core",
desc: "text basic color of primary",
syntax: "text-primary",
expr: "color: var(--color-primary);"
},
{
package: "text.color.core",
desc: "text basic color of white",
syntax: "text-white",
expr: "color: var(--color-white);"
},
{
package: "text.color.core",
desc: "text basic color of black",
syntax: "text-black",
expr: "color: var(--color-black);"
},
{
package: "text.color.ext",
desc: "text color",
syntax: "text-[C]-[N]",
expr: "color: var(--color-[C]-[N]);"
},
{
package: "effect.core.ext",
desc: "shadow",
syntax: "shadow",
expr: "box-shadow: 0 var(--number-4) var(--number-10) var(--color-gray-5);",
units: ["4", "10"],
},
]

3 生成工具

工具工作的主要步骤是:

(1)样式表达式解析,匹配包名、组合规则或者原子规则。

直接使用包名的,包内规则与数值无关,不需要动态生成。

(2)通过组合规则查找原子规则。

(3)通过原子规则生成CSS代码。

(4)生成变量CSS代码。

(5)组合(3)和(4)得到原子样式的代码。

/**
* 输出包名和规则名
* @param styles
*/
export const printStyle = (styles: AtomicStyleRule[]): string => {
return styles.map((style: AtomicStyleRule, index: number) => {
return `${index + 1}. ${style.package}:${style.syntax}`
}).join("\n")
}

/**
* 生成样式代码
* @param expressions 样式表达式数组
*/
export const makeCss = (...expressions: string[]): { units: string[], styles: string[] } => {
const styleList: string[] = [];
const unitList: string[] = []
const styleMap = getRuleMap(DefaultStyles);
expressions.forEach((expr: string) => {
const {units, styles} = makeCssForExpr(expr, styleMap, DefaultStyles)
unitList.push(...units)
styleList.push(...styles)

})
return {units: unitList, styles: styleList}
}

/**
* 为某个样式表达式生成CSS代码
* @param expression 样式表达式,可以是包名(例如:layout.flex.core),可以是原始规则名(例如:pos-abs),也可以是包含数值的规则名(例如:h-[1|2])
* @param styleMap 样式字典
* @param styles 样式数组
*/
const makeCssForExpr = (expression: string, styleMap: StyleRuleMap, styles: AtomicStyleRule[]): { units: string[], styles: string[] } => {
if (expression == "") {
return {units: [], styles: []}
}
if (expression.includes(".")) {
const unitArray: string[] = []
const cssItems = styles.filter((style: AtomicStyleRule) => {
return style.package == expression
}).map((style: AtomicStyleRule) => {
const {expressions, units} = getExprList(style.syntax, styleMap)
unitArray.push(...units)
return makeCssForSyntax(style.syntax, expressions)
}).flat()
return {units: unitArray, styles: cssItems}
} else if (expression.includes("[")) {
const parts = expression.match(/([a-z-]+)(-?)\[(.+)\]([a-z]*)/)
if (parts == null || parts.length < 3) {
throw Error(`invalid expr ${expression} with [`)
}
let syntax = `${parts[1]}${parts[2]}[U]${parts[4]}`
if (!styleMap[syntax]) {
syntax = `${parts[1]}${parts[2]}[N]${parts[4]}`
}
const {units, expressions} = getExprList(syntax, styleMap)
const givenUnits = parts[3].split("|")
return {
units: [...units, ...givenUnits],
styles: units.map((numberValue: string) => {
return makeCssForSyntax(syntax, expressions, numberValue)
}).flat()
}
} else {
const syntax = expression
const {expressions, units} = getExprList(syntax, styleMap)
return {units, styles: makeCssForSyntax(syntax, expressions)}
}
}

/**
* 为某个语法规则生成CSS代码
* @param syntax 语法
* @param styleRules 样式规则
* @param numberValue 数值
*/
const makeCssForSyntax = (syntax: string, styleRules: string[], numberValue?: string): string[] => {
if (numberValue == undefined) {
return [`.${syntax} {`, ...styleRules.map((m: string) => "\t" + m), "}"]
}
const placeholder = syntax.includes("[U]") ? "[U]" : "[N]"
const className = syntax.replace(placeholder, numberValue)
const styles = styleRules.map((m: string) => "\t" + m.replace(placeholder, numberValue))
return [`.${className} {`, ...styles, "}"]
}

/**
* 查找某个规则的全部样式
* @param syntax 语法规则
* @param styleMap 样式字典
*/
const getExprList = (syntax: string, styleMap: StyleRuleMap): { expressions: string[], units: string[] } => {
const style = styleMap[syntax]
if (!style) {
throw Error(`not found syntax ${syntax}`)
}
if (style.compose) {
const unitArray: string[] = []
const exprArray: string[] = []
style.compose.forEach((subSyntax: string) => {
const {expressions, units} = getExprList(subSyntax, styleMap)
unitArray.push(...units)
exprArray.push(...expressions)
})
return {expressions: exprArray, units: unitArray}
}
if (style.expr) {
return {expressions: [style.expr], units: style.units || []}
}
throw Error(`syntax ${syntax} missing compose and expr`)
}

/**
* 将样式规则数组转换为字典,便于快速读取
* @param styles 样式规则数组
*/
const getRuleMap = (styles: AtomicStyleRule[]): StyleRuleMap => {
const styleMap = {} as StyleRuleMap
styles.forEach((style: AtomicStyleRule) => {
styleMap[style.syntax] = style
})
return styleMap
}

/**
* 生成变量的CSS
* @param plat 运行环境简称
* @param units 全部数值
* @param colorThemes 主题色
* @param config CSS基础配置
*/
const makeVars = (plat: string, units: string[], colorThemes: string[], config: CssConfig): string => {
const root = config.root[plat]
if (!root) {
throw Error(`missing root for ${plat}`)
}
if (units.length == 0) {
units = config.default["unit"].split(",")
} else {
units = units.filter((m: string, index: number) => units.indexOf(m) == index)
.sort((left: string, right: string) => {
return parseFloat(left.replace("d", "0.")) - parseFloat(right.replace("d", "0."))
})
}
if (colorThemes.length == 0) {
colorThemes = config.default["theme"].split(",")
}

const vars: string[] = []
vars.push(`${root} {`)
units.forEach((unit: string) => {
vars.push(makeUnitVar(unit, config))
})

vars.push("")
vars.push(`--color-primary: ${config.color?.primary};`)
vars.push(`--color-white: ${config.color?.white};`)
vars.push(`--color-black: ${config.color?.black};`)
vars.push("")

colorThemes.forEach((themeName: string) => {
vars.push(...makeColorVars(themeName, config))
vars.push("")
})
vars.push("}")
return vars.join("\n")
}

/**
* 生成数值变量
* @param unit 数值
* @param config CSS基础配置
*/
const makeUnitVar = (unit: string, config: CssConfig): string => {
if (unit == "") {
throw Error("missing value")
}
if (unit == "full") {
return "--unit-full: 100%;"
}
const oriUnit = unit
if (unit.startsWith("d")) {
unit = unit.replace("d", "0.")
}
const numberValue = Number(unit)
if (isNaN(numberValue)) {
throw Error("invalid number value")
}
const scale = Math.pow(10, config.one.scale)
const unitValue = Math.round(numberValue * config.one.from * scale / config.one.to) / scale
return `--unit-${oriUnit}: ${unitValue}${config.one.unit};`
}

/**
* 生成主题色彩变量
* @param themeName 主题名称
* @param config CSS基础配置
*/
const makeColorVars = (themeName: string, config: CssConfig): string[] => {
const vars: string[] = []
if (themeName == "") {
throw Error("missing theme name")
}
if (!config.theme[themeName]) {
throw Error(`missing theme ${themeName}`)
}
const colors = config.theme[themeName] as string[]
colors.forEach((color: string, index: number) => {
vars.push(`--color-${themeName}-${index + 1}: ${color};`)
})
return vars
}

4 示例

4.1 代码调用

通过简单定义,可以通过两个函数生成最终需要的样式。


export const demo = (): string => {
try {

const {units, styles} = makeCss(
"layout.float.core",
"pos-tr-[-18]",
"layout.flex.core",
"c[1|2|3|4|5]",
"grid-[2|3|4|5|6|7]c",
"p-[10|20]",
"px-[4|8|10|20|36]",
"py-[4|10|20|36]",
"m-[10|20]",
"mx-[4|8|10|20|36]",
"my-[4|10|20|36]",
"h-[d5|1|10|20]",
"mh-[150|200]",
"w-[100]",
"gap-[4|10|20]",
"shadow")
const vars = makeVars("wx+xcx", units, [], DefaultConfig)

return vars + styles.join("\n")

} catch (e) {
return `${e}`
}

}

4.2 示例结果

page {
--unit--18: -2.4vmin;
--unit-d5: 0.067vmin;
--unit-1: 0.133vmin;
--unit-2: 0.267vmin;
--unit-3: 0.4vmin;
--unit-4: 0.533vmin;
--unit-5: 0.667vmin;
--unit-6: 0.8vmin;
--unit-7: 0.933vmin;
--unit-8: 1.067vmin;
--unit-10: 1.333vmin;
--unit-20: 2.667vmin;
--unit-36: 4.8vmin;
--unit-100: 13.333vmin;
--unit-150: 20vmin;
--unit-200: 26.667vmin;

--color-primary: #1890ff;
--color-white: #ffffff;
--color-black: #000000;

--color-gray-1: #f5f5f5;
--color-gray-2: #f0f0f0;
--color-gray-3: #d9d9d9;
--color-gray-4: #bfbfbf;
--color-gray-5: #8c8c8c;
--color-gray-6: #595959;
--color-gray-7: #434343;
--color-gray-8: #262626;
--color-gray-9: #1f1f1f;
--color-gray-10: #141414;

--color-red-1: #fff1f0;
--color-red-2: #ffccc7;
--color-red-3: #ffa39e;
--color-red-4: #ff7875;
--color-red-5: #ff4d4f;
--color-red-6: #f5222d;
--color-red-7: #cf1322;
--color-red-8: #a8071a;
--color-red-9: #820014;
--color-red-10: #5c0011;

--color-yellow-1: #feffe6;
--color-yellow-2: #ffffb8;
--color-yellow-3: #fffb8f;
--color-yellow-4: #fff566;
--color-yellow-5: #ffec3d;
--color-yellow-6: #fadb14;
--color-yellow-7: #d4b106;
--color-yellow-8: #ad8b00;
--color-yellow-9: #876800;
--color-yellow-10: #614700;

--color-green-1: #f6ffed;
--color-green-2: #d9f7be;
--color-green-3: #b7eb8f;
--color-green-4: #95de64;
--color-green-5: #73d13d;
--color-green-6: #52c41a;
--color-green-7: #389e0d;
--color-green-8: #237804;
--color-green-9: #135200;
--color-green-10: #092b00;

--color-orange-1: #fff7e6;
--color-orange-2: #ffe7ba;
--color-orange-3: #ffd591;
--color-orange-4: #ffc069;
--color-orange-5: #ffa940;
--color-orange-6: #fa8c16;
--color-orange-7: #d46b08;
--color-orange-8: #ad4e00;
--color-orange-9: #873800;
--color-orange-10: #612500;

--color-gold-1: #fffbe6;
--color-gold-2: #fff1b8;
--color-gold-3: #ffe58f;
--color-gold-4: #ffd666;
--color-gold-5: #ffc53d;
--color-gold-6: #faad14;
--color-gold-7: #d48806;
--color-gold-8: #ad6800;
--color-gold-9: #874d00;
--color-gold-10: #613400;

--color-purple-1: #f9f0ff;
--color-purple-2: #efdbff;
--color-purple-3: #d3adf7;
--color-purple-4: #b37feb;
--color-purple-5: #9254de;
--color-purple-6: #722ed1;
--color-purple-7: #531dab;
--color-purple-8: #391085;
--color-purple-9: #22075e;
--color-purple-10: #120338;

--color-magenta-1: #fff0f6;
--color-magenta-2: #ffd6e7;
--color-magenta-3: #ffadd2;
--color-magenta-4: #ff85c0;
--color-magenta-5: #f759ab;
--color-magenta-6: #eb2f96;
--color-magenta-7: #c41d7f;
--color-magenta-8: #9e1068;
--color-magenta-9: #780650;
--color-magenta-10: #520339;

--color-cyan-1: #e6fffb;
--color-cyan-2: #b5f5ec;
--color-cyan-3: #87e8de;
--color-cyan-4: #5cdbd3;
--color-cyan-5: #36cfc9;
--color-cyan-6: #13c2c2;
--color-cyan-7: #08979c;
--color-cyan-8: #006d75;
--color-cyan-9: #00474f;
--color-cyan-10: #002329;

--color-lime-1: #fcffe6;
--color-lime-2: #f4ffb8;
--color-lime-3: #eaff8f;
--color-lime-4: #d3f261;
--color-lime-5: #bae637;
--color-lime-6: #a0d911;
--color-lime-7: #7cb305;
--color-lime-8: #5b8c00;
--color-lime-9: #3f6600;
--color-lime-10: #254000;

--color-volcano-1: #fff2e8;
--color-volcano-2: #ffd8bf;
--color-volcano-3: #ffbb96;
--color-volcano-4: #ff9c6e;
--color-volcano-5: #ff7a45;
--color-volcano-6: #fa541c;
--color-volcano-7: #d4380d;
--color-volcano-8: #ad2102;
--color-volcano-9: #871400;
--color-volcano-10: #610b00;

}.pos-abs {
position: absolute;
}
.pos-rel {
position: relative;
}
.pos-fix {
position: fixed;
}
.pos-sticky {
position: sticky;
}
.row {
display: -webkit-box; display: -webkit-flex; display:flex; flex-direction: row;
}
.col {
display: -webkit-box; display: -webkit-flex; display:flex; flex-direction: column;
}
.row-r {
display: -webkit-box; display: -webkit-flex; display:flex; flex-direction: row-reverse;
}
.col-r {
display: -webkit-box; display: -webkit-flex; display:flex; flex-direction: column-reverse;
}
.wrap {
flex-wrap: wrap;
}
.ai-start {
-webkit-box-align: start;-webkit-align-items: flex-start;align-items: flex-start;
}
.ai-center {
-webkit-box-align: center;-webkit-align-items: center;align-items: center;
}
.ai-end {
-webkit-box-align: end;-webkit-align-items: flex-end;align-items: flex-end;
}
.jc-start {
-webkit-justify-content: flex-start; justify-content: flex-start;
}
.jc-center {
-webkit-justify-content: center; justify-content: center;
}
.jc-end {
-webkit-justify-content: flex-end; justify-content: flex-end;
}
.jc-between {
-webkit-justify-content: space-evenly; justify-content: space-evenly;
}
.jc-between {
-webkit-justify-content: space-evenly; justify-content: space-evenly;
}
.jc-between {
-webkit-justify-content: space-evenly; justify-content: space-evenly;
}
.jc-between {
-webkit-justify-content: space-evenly; justify-content: space-evenly;
}
.shadow {
box-shadow: 0 var(--number-4) var(--number-10) var(--color-gray-5);
}

5 小结

本文定义了原子样式模型、配置了数据、实现了样式代码生成。

核心模型并不复杂,通过合理的规则定义,一是简化定义,二是简化使用,减少了工作量。

对于颜色的部分,完全定义较为复杂,直接使用调色板可以简化,后文继续探讨。

感谢 AntDesign 的调色板。