原子样式实践第10篇 原子样式到原子设计的发展使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原子样式实践第10篇 原子样式到原子设计的发展使用相关的知识,希望对你有一定的参考价值。

在各种知名样式表或者组件库开箱即用的优势下,原子样式依然流行,说明了成品样式(半定制样式)存在升级、变更或者管理的需求,在维护时与发展或者规范形成了冲突。在大粒度上不能较好的解决问题时,势必下钻寻求更好的解决方式。通过页面的 CSS 定义来生成有效的样式,量体裁衣,避免了样式的臃肿或者浪费。那么原子样式的未来在哪里呢?


1 原子样式的特点

原子样式在实际使用受欢迎,如果用一句话表示,设计先行理念下的设计规范的适应性表达。

从表达方式来看,既有习惯约定配置,也有量化表达。

【原子样式实践】第10篇


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 小结

动态样式是在静态样式上继承发展的,一张图示例展示如下。

【原子样式实践】第10篇

通过扩展规则,我们可以实现单个元素的多状态的样式定义。往上一步,原子样式在组件中如何工作呢?


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 中实现如下:

【原子样式实践】第10篇

状态机的所有参数定义放入 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篇 原子样式到原子设计的发展使用的主要内容,如果未能解决你的问题,请参考以下文章

原子样式实践第3篇 原子样式文件构成

原子样式实践第12篇 一次搞定微信开发者工具的原子样式扩展

10.22-10.28博客精彩回顾

梦回2008样式原子化:Tailwind是如何让Facebook拜倒在她的石榴裙下?

梦回2008样式原子化:Tailwind是如何让Facebook拜倒在她的石榴裙下?

使用UnoCSS原子化CSS