原子样式实践第10篇 原子样式到原子设计的发展使用
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原子样式实践第10篇 原子样式到原子设计的发展使用相关的知识,希望对你有一定的参考价值。
在各种知名样式表或者组件库开箱即用的优势下,原子样式依然流行,说明了成品样式(半定制样式)存在升级、变更或者管理的需求,在维护时与发展或者规范形成了冲突。在大粒度上不能较好的解决问题时,势必下钻寻求更好的解决方式。通过页面的 CSS 定义来生成有效的样式,量体裁衣,避免了样式的臃肿或者浪费。那么原子样式的未来在哪里呢?
1 原子样式的特点
原子样式在实际使用受欢迎,如果用一句话表示,设计先行理念下的设计规范的适应性表达。
从表达方式来看,既有习惯约定配置,也有量化表达。
1.1 习惯约定配置
原子样式事实上将复杂的设计转换为文本类的知识点,同时进行了简化、语义化、内容化。
特点1:越常用的缩写越短,反之尽量使用个性化词汇全拼。
例如:p
表示 padding
,而不是 paragraph
。
rorate
同时表示 transform
和 rotate
两个词汇。
shadow
隐含了 box-shadow: 0 var(--unit-10) var(--unit-20) var(--color-gray-5-a04)
三个数值和一个颜色。
简洁又贴切的表达隐含了设计的细节,降低了学习成本。
特点2:样式名称趋近语义,提高了理解程度。
例如:round
使用 border-radius
属性,赋予 50% 的数值实现了圆形效果。round
在语义上比 radius
更能表明效果。
例如:mx
表示 margin-left
和 margin-right
两个属性。x
表示 left
和 right
两个方向,是一种组合表达。
例如:round-top
使用了 border-top-left-radius
和 border-top-right-radius
两个属性,也是一种组合式表达。
例如:top-10
表示 top: 10rpx
,与 round-top
中的 top 在理解中一致,不混淆。
特点3:强化内容,减少修饰词。
例如:text-28
表示字体高 28 rpx,对应样式为 font-size: 28rpx
,text-primary
表示字体为主题色,对应样式为 color: var(--color-primary)
,text-bold
表示粗体,对应样式为 font-weight: bold
。
text 作为使用的主体,增加不同的内容限定词,即可准确完成 font-size 、 color 和 font-weight 三个样式的表达。
1.2 量化表达
原子样式通过量化表达在规范性上提供了扩展性。
特点1:空间数值的量化
例如:m-20
表示了 margin: (--unit-20)
样式,m-32
表示了 margin: (--unit-32)
样式。w-p20
表示了 width: (--unit-percent-20)
样式。
通过建立空间数值的识别规则,动态支持多个值。
例如:shadow-1
表示 box-shadow: 0 var(--unit-2) var(--unit-4) var(--color-gray-5-a07)
样式,shadow-2
表示 box-shadow: 0 var(--unit-4) var(--unit-8) var(--color-gray-5-a06)
样式。
通过对阴影编号,将复杂的数值组合简单的表达。
特点2:色彩的量化
电子设备可表达上千万种颜色,人眼能识别百万种颜色,但若是用于信息的日常表达,具有较强区别度的色彩则可以降低到百种以内,便于开发工程师使用。
通过确定调色板的色系,分为十阶即可完成基本的量化。灰色作为常用中性色,可以适当扩展数量。
例如:text-red-6
表示 color: var(--color-red-6)
,bg-gray-3
表示 background-color: var(--color-gray-3)
。
通过颜色名称和序号指定色号。
例如:ant-design 推荐了 red、orange、yellow、green、blue、purple、magenta、cyan、lime、gold、volcano 11种色彩。
1.3 适应性表达
原子样式具有明确的含义,对于不同的设备或者不同的设计要求,具备适应能力。
方式1 :支持多种主题,支持多种模式
CSS变量在其中发挥重要的作用,可以通过变更变量达到动态变更,可以通过。
原子样式本身支持多个调色板,可以预先定义。
通过更换调色板,支持更换色系,支持黑夜模式,支持高对比模式,适应红绿弱识别等。
通过更换数值变量,支持字体放大缩小。
方式2:支持多种设备
media 本身的约束支持多种设备的同时支持,对于原子样式并无特别的优势,这点就不赘述了。
2 原子样式的组合
原子样式在使用时附着在元素上,元素需要通过多种样式呈现。因此原子样式在使用时,往往是组合的。
2.1 静态样式定义
2.1.1 组件样式简单定义
元素的样式在定义时即可确定,包括预定义交互行为中使用的确定样式。
例如: tag 标签对应的wxml内容为
<view class="bg-primary text-white text-28 px-8 py-4">默认</view>
因此可以直接定义标签样式
tag = [bg-primary, text-white, text-28, px-8, py-4]
2.1.2 多主题样式定义
这里加入颜色支持,将背景色改为变量,组合样式定义变更为
tag-[color] = [bg-[color], text-white, text-28, px-8, py-4]
相应的可以使用 tag-blue-6
, tag-red-6
, tag-orange-6
等表达其他颜色标签。
加入主题约束,可以联合定义如下:
tag-[color] = [bg-[color], text-white, text-28, px-8, py-4]
, color = primary, blue-6, red-6, orange-6
2.1.3 多风格样式定义
对于可能存在的填充(fill)、线框(line)风格,可以进一步强化定义
tag-fill-[color] = [bg-[color], text-white, text-28, px-8, py-4]
, color = primary, blue-6, red-6, orange-6
tag-line-[color] = [border, border-[color], text-[color], text-28, px-8, py-4]
, color = primary, blue-6, red-6, orange-6
加入风格后,可以联合定义如下:
tag-[style]-[color] = [text-28, px-8, py-4]
, color = primary, blue-6, red-6, orange-6
, style = fill, line
, fill = [bg-[color], text-white]
, line = [border, border-[color], text-[color]]
2.2 动态样式定义
2.2.1 静态样式与动态样式区别
元素的样式在运行时才能确定,通常受到数据元素的影响。动态的样式往往是样式字典,需要动态使用。
静态样式与动态样式之间的区别在于,静态样式是单状态下的单样式,动态样式是多状态下的多样式,但某个时刻只呈现一种样式,具体渲染哪种样式由动态属性决定。
动态样式有两种情况,一是根据属性动态组合,二是根据状态机选择。
在脚本中使用动态样式
动态样式的使用有两种方式,一是在页面上根据需要选择,二是在 javascript/typeScript 中定义,渲染到页面中。显然前者可以使用工具分析页面跟踪到,但判断的情况较多,对于页面性能可能存在影响,后者则需要拓展工具的能力,防止不能识别原子样式,导致管理失控。
2.2.2 交互样式
在微信小程序中,元素具有 hover-class 属性,因此其点击状态下属性定义更加方便,
<view ... hover-class="bg-primary-a30 text-black">默认</view>
因此可以直接定义标签样式
tag:hover = [bg-primary-a30, text-black]
对应 tag-[style]-[color]
的定义,hover属性样式可以定义为:
tag-[style]-[color]:hover = [text-28, px-8, py-4]
, color = primary, blue-6, red-6, orange-6
, style = fill, line
, fill = [bg-[color]-a80, text-white]
, line = [bg-[color]-a10]
2.2.2 可选属性样式
对标签的线宽和是否圆角进一步定义,可以联合定义如下:
tag-[style]-[color] = [text-28, px-8, py-4]
, color = primary, blue-6, red-6, orange-6
, style = fill, line
, fill = [bg-[color], text-white]
, line = [border, border-[color], text-[color]]
, optional = [round-[N], border-[N]]
可选属性也可以不合并,当作追加样式或者覆盖样式。
例如,对英文标签可以追加全部大写、首字母大写,
既可以增加在定义内部,
tag-[style]-[color] = [text-28, px-8, py-4, text-uppercase]
, color = primary, blue-6, red-6, orange-6
, style = fill, line
, fill = [bg-[color], text-white]
, line = [border, border-[color], text-[color]]
, optional = [round-[N], border-[N]]
也可以与定义组合。
<view class="tag-red-6 text-uppercase">...</view>
2.2.3 行为状态样式
对于标签,行为状态有选中状态和未选中状态。
# 公共样式
tag-[style]-[color] = [text-28, px-8, py-4, text-uppercase]
, color = primary, blue-6, red-6, orange-6
, optional = [round-[N], border-[N]]
# hover 样式
tag-[style]-[color]:hover = [text-28, px-8, py-4]
, style = fill, line
, fill = [bg-[color]-a80, text-white]
, line = [bg-[color]-a10]
# 状态集合
tag-[style]-[color]:state = checked, unchecked
# 选中状态
tag-[style]-[color]:checked = []
, style = fill, line
, fill = [bg-[color], text-white]
, line = [border, border-[color], text-[color]]
# 未选中状态
tag-[style]-[color]:unchecked = []
, style = fill, line
, fill = [border, border-[color], text-[color]]
, line = [bg-[color], text-white]
2.2.4 内容状态样式
对于标签,属性状态根据内容可以自定义,例如分为主要标签、次要标签,和主题颜色有部分重叠,可以定义如下
# 公共样式
tag-[style]-[color] = [text-28, px-8, py-4, text-uppercase]
, color = primary, blue-6, red-6, orange-6
, optional = [round-[N], border-[N]]
# hover 样式
tag-[style]-[color]:hover = [text-28, px-8, py-4]
, style = fill, line
, fill = [bg-[color]-a80, text-white]
, line = [bg-[color]-a10]
# 行为状态集合
tag-[style]-[color]:state = checked, unchecked
# 选中状态
tag-[style]-[color]:checked = []
, style = fill, line
, fill = [bg-[color], text-white]
, line = [border, border-[color], text-[color]]
# 未选中状态
tag-[style]-[color]:unchecked = []
, style = fill, line
, fill = [border, border-[color], text-[color]]
, line = [bg-[color], text-white]
# 属性状态集合
tag-[style]-[color]:theme = primary, secondary
# 属性状态
tag-[style]-secondary = [tag-[style]-blue-5]
在实际使用中,示例如下:
<view class="tag-fill-primary" hover-class="tag-fill-primary_hover">科技</view>
<view class="tag-fill-secondary" hover-class="tag-fill-secondary_hover">消费电子</view>
2.3 小结
动态样式是在静态样式上继承发展的,一张图示例展示如下。
通过扩展规则,我们可以实现单个元素的多状态的样式定义。往上一步,原子样式在组件中如何工作呢?
3 原子样式到原子设计
元素定制形成组件,组件组合得到模块,模块选用得到页面,这也是原子设计的基础。
在考虑原子样式的组合使用时,需要考虑组件、模块和页面的使用。
3.1 元素的状态样式
原子组件包含一个或者多个元素,每个元素都可以分别定义样式。
以标签为例,标签可以是单个 View 元素,也可以是 View 元素加上 Icon 的组合,Icon 提供关闭功能。
转换为样式函数,伪代码如下:
const componentClassName = (iconName, style = fill, state = unchecked,
theme = primary): setting =>
"container": [gap-10],
"content": [
tag-[style]-[color] = [text-28, px-8, py-4]
, color = primary, blue-6, red-6, orange-6
, optional = [round-[N], border-[N]]
tag-[style]-[color]:hover = [text-28, px-8, py-4]
, style = fill, line
, fill = [bg-[color]-a80, text-white]
, line = [bg-[color]-a10]
# 行为状态集合
tag-[style]-[color]:state = checked, unchecked
# 选中状态
tag-[style]-[color]:checked = []
, style = fill, line
, fill = [bg-[color], text-white]
, line = [border, border-[color], text-[color]]
# 未选中状态
tag-[style]-[color]:unchecked = []
, style = fill, line
, fill = [border, border-[color], text-[color]]
, line = [bg-[color], text-white]
],
"icon": [
icon-[iconName], wh-36, round
单状态的样式定义较为复杂,合并后重新定义状态机,在 TypeScript 中实现如下:
状态机的所有参数定义放入 paraDefs 中,元素的样式规则放入 elements,通过固定的样式 compose 和 条件样式 conditons 合并得到元素的最终样式。
整个定义逻辑简单,只有 name, action, compose, conditons, where, paraDefs, elements 7个关键词,状态在 paraDefs, where 中使用,样式均在 compose 中使用。
页面属性到样式的计算示例。
updateClassName()
const checked, theme, showClose, style = this.data
const classNameDict = cne.applyClassName(
state: checked ? "checked" : "unchecked",
color: theme,
icon: showClose ? "close" : "",
style
, Rule)
console.log(`[tag] classNameDict`, classNameDict)
this.setData(
classNameDict
)
,
页面展示的示例。
<!--components/form/tag/tag.wxml-->
<view class=" classNameDict.container " catchtap="toggleValue">
<view class=" classNameDict.content " hover-class="classNameDict.content_hover"> title </view>
<image wx:if=" showClose " src="../img/clear.png" class=" classNameDict.icon " mode="aspectFill"></image>
</view>
3.2 三种样式生成方式
3.2.1 规则生成
根据 CSS 规则预生成样式,通过参数集合定义最大的样式范围,压缩保存为静态样式文件。
样式生成时机:用户开发以前。
使用成本:用户选择样式使用,学习的时间较多,基本没有配置成本,编码简单。
优点:一次生成,反复使用,长期使用的边际成本非常低。
缺点:需要生成一个全的样式集,分发时具有较大体积,升级维护成本高。
3.2.2 声明生成
开发 CSS 声明提取和 CSS 样式生成工具,通过扫描开发完成的页面文件,提取静态声明的样式名称,生成样式文件。
样式生成时机:在编码时,略微延迟,可以实时预览到效果。
使用成本:用户熟悉样式规则后编写,学习的成本较低,配置成本较低,编码简单。
可以结合静态文件使用。对于特别复杂或者临时使用的部分,可以直接放在静态文件中包含使用,减少工具生成时间和管理成本。
优点:按需生成样式,没有冗余样式,复用效果好。
缺点:对于业务逻辑中动态生成的样式名称难以捕捉,覆盖不全面。
3.2.3 即时编译
开发或者选择 CSS 规则解释工具,配置到开发工具中,在动态页面中编写样式声明,在程序运行时生成样式规则。
样式生成时机:在运行时,按需编译。
使用成本:前置知识较多,准备工作多,有一定配置成本,编码简单。
优点:无需提前编写样式,按需生成,各种情况适应性好。
缺点:在动态页面中使用较好,在静态页面开发中反而带来了成本。
3.3 组件中的样式生成
3.3.1 即时编译是最活跃的方向
在编写页面和自用组件时,方式二即可满足需要。但如果是发布组件,涉及动态样式的部分,由于事先并不能确定参数,要么退到方式一采取预生成策略,要么前进到方式三采取即时编译策略。由于零冗余等其他好处,即时编译的CSS引擎或者工具是最活跃的。
但即时编译的CSS引擎是唯一的方向吗?
3.3.2 样式优化目标
从样式优化的出发点来说,一是减少大小,去除冗余,优化实现,二是提高复用率,将最常用的样式作为关键样式。
但从设计的角度来说,还要讲究设计规范,也就是说,必然会使用一些样式、布局,存在一个空间和色彩的最小集合。从优化的角度上来讲,这部分静态化是效率最高的,这是方式一的长处。
从结果来说,复用率是依靠统计数据计算出来的。CSS复用是页面级,一个页面有多个模块,每个模块有多个组件,复用率的统计跨越多个组件。在动态页面中,复用率统计存在困难,但在页面生成后,复用率统计可能。
那么是否存在一种可能性,在客户端静态生成样式代码?
3.3.2 静态编译的可能性
三个基础:
(1)核心样式规则不多,可以通过 javascript 定义动态生成,只需要设置基本变量即可。
const generateStyleFromClassName = (m) =>
if (/text-(\\w+)/.test(m))
return `.$m color: $m.slice(5) `
if (/text-(\\w+)/.test(m))
return `.$m font-size: $m.slice(5)px `
return "invalid className:" + m
(2)终端的计算能力较强,页面端样式提取方便,计算时间短。
const classNames = [...document.querySelectorAll("*[class]")].map(m=>m.className)
.filter((m,index,arr)=>arr.indexOf(m)==index).sort()
(3)页面端样式可以缓存。
可以直接通过 javascript 变量存储,可以通过 storage 存储。
一个基于html的示例如下:
<!doctype html>
<html lang="\\">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1 class="text-36 text-red">Hello World</h1>
<p class="text-28 text-black">This is a test</p>
<script
/* 插入样式 */
const importStyle = function importStyle(b)
const a = document.createElement("style"), c = document;
c.getElementsByTagName("head")[0].appendChild(a);
if (a.styleSheet)
a.styleSheet.cssText = b
else
a.appendChild(c.createTextNode(b))
;
/* 获得全部样式名 */
const classNames = [...document.querySelectorAll("*[class]")].map(m => m.className)
.filter((m, index, arr) => arr.indexOf(m) === index).sort()
/* 生成样式 */
const content = classNames.map(m =>
if (以上是关于原子样式实践第10篇 原子样式到原子设计的发展使用的主要内容,如果未能解决你的问题,请参考以下文章梦回2008样式原子化:Tailwind是如何让Facebook拜倒在她的石榴裙下?