CSS 发展趋势
比如 BEM、OOCSS、Css in js、Js in css、Utility first、Atomic css 等
- BEM(Block-Element-Modifier):一种css class 命名方法,通过链接符号来区分含义。
- OOCSS(Object-Oriented-CSS):面向对象的css,通过抽象的概念抽离css类,带来高复用。有点像 html 的表格标签,比如 table、caption、thead、tbody、tfoot等,形成一种以功能抽象的命名规范。
- Css in js:通过 js 和 css 的耦合,更容易追溯,并且通过锁定作用域避免污染,更方便使用 js 的变量、模块、tree-shaking等。
- Js in css:通过 CSS Houdini 实现 css 中使用 js 脚本。
- Utility first:抽象大量的工具类,声明式理念以减少编写 css 为基础,以工具的方式管理使用 css,规范化、统一管理、高复用。
- Atomic css:极端版 Utility first,除了提供工具类外,还会提供更低细粒度的累,这样可以解决 Utility first 无法覆盖场景的问题。
Atomic CSS 理念实质
通过大量的类和 css 做映射,把 css 的管理变为 css 类的管理,只需要操纵css类,就能得到相关的收益,比如 tree-sharking、css 预载、定制化、响应式设计等。
CSS 原子化相关框架及设计思维
ACSS
-
基于原子化思想,提供大量类工具函数,下面是使用的几个特性demo
// Color <div class="Bgc(#0280ae.5) C(#fff) P(20px)"> Lorem ipsum </div> // Variables 'custom': { 'brandColor': '#0280ae', 'columnWidth': '20px' } <div class="Pos(a) Bgc(brandColor) W(columnWidth) H(90px)"></div> <div class="C(brandColor) BdB Bdc(brandColor) Mstart(columnWidth) P(10px)"> Lorem ipsum </div> // Responsive web design (RWD) 'breakPoints': { 'sm': '@media screen and (min-width:700px)' } <div class="Bgc(#0280ae.5) H(90px) D(ib)--sm W(25%)--sm"></div><!-- --><div class="Bgc(#0280ae) H(90px) D(ib)--sm W(25%)--sm"></div><!-- --><div class="Bgc(#0280ae.5) H(90px) D(ib)--sm W(25%)--sm"></div><!-- --><div class="Bgc(#0280ae) H(90px) D(ib)--sm W(25%)--sm"></div>
TailWind CSS(社区比较推荐,及个人比较推荐)
推荐理由
1. 设计想法基于工具类延伸,解决了工具理念的缺点
2. 整体框架设计,带来很好的拓展性
3. 主流编辑器都有补全插件,减少使用成本
简单看一些daemo
-
响应式
默认的配置的设备分辨率方案
断点前缀 最小宽度 CSS sm
640px @media (min-width: 640px) { ... }
md
768px @media (min-width: 768px) { ... }
lg
1024px @media (min-width: 1024px) { ... }
xl
1280px @media (min-width: 1280px) { ... }
2xl
1536px @media (min-width: 1536px) { ... }
<!-- Width of 16 by default, 32 on medium screens, and 48 on large screens --> <img class="w-16 md:w-32 lg:w-48" src="...">
-
主题色
<div class="bg-white dark:bg-gray-800"> <h1 class="text-gray-900 dark:text-white">Dark mode is here!</h1> <p class="text-gray-600 dark:text-gray-300"> Lorem ipsum... </p> </div>
-
交互状态
常用的都覆盖了,比如 hover、focus、active 等
<form> <input class="border border-transparent focus:outline-none focus:ring-2 focus:ring-purple-600 focus:border-transparent ..."> <button class="bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-600 focus:ring-opacity-50 ..."> Sign up </button> </form>
除了基本之外,还有一些内部支持的,比如 checked,开启后,会让一个单选或复选框应用样式
<input type="checkbox" class="appearance-none checked:bg-blue-600 checked:border-transparent ...">
-
基础样式
可以自定义基础样式,满足视觉统一问题。
@tailwind base; @tailwind components; @tailwind utilities; @layer base { h1 { @apply text-2xl; } h2 { @apply text-xl; } }
-
功能类
这个其实和大多数框架差不多,都是保持“单一原则”、“组合应用”的几个点,设计功能类,就不过多介绍了,感兴趣可以参考社区开源框架。因为如果团队中落地,肯定是要重新设计过的。
<!-- Using utilities --> <button class="py-2 px-4 font-semibold rounded-lg shadow-md text-white bg-green-500 hover:bg-green-700"> Click me </button> <!-- Extracting classes using @apply --> <button class="btn btn-green"> Button </button> <style> .btn { @apply py-2 px-4 font-semibold rounded-lg shadow-md; } .btn-green { @apply text-white bg-green-500 hover:bg-green-700; } </style>
-
函数与指令
因为原子化理念,是种细化思想,所以会有大量的类,那么如果管理这些类,并且使开发者高效的使用。答案是建立一套函数和指令的形式,把相关操作的细节封装,提高开发者的效率和易用性。
- @tailwind - 引入已封装的类
@tailwind base; @tailwind components; @tailwind utilities; @tailwind screens;
- @apply - 内联功能类,组合使用。
.btn { @apply font-bold py-2 px-4 rounded; } .btn-blue { @apply bg-blue-500 hover:bg-blue-700 text-white; }
- @layout - 设置封装的类
@tailwind base; @tailwind components; @tailwind utilities; @layer base { h1 { @apply text-2xl; } h2 { @apply text-xl; } } @layer components { .btn-blue { @apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded; } }
- @variants - 变体声明
@variants focus, hover { .rotate-0 { transform: rotate(0deg); } .rotate-90 { transform: rotate(90deg); } } /* output */ .rotate-0 { transform: rotate(0deg); } .rotate-90 { transform: rotate(90deg); } .focus\:rotate-0:focus { transform: rotate(0deg); } .focus\:rotate-90:focus { transform: rotate(90deg); } .hover\:rotate-0:hover { transform: rotate(0deg); } .hover\:rotate-90:hover { transform: rotate(90deg); }
- @responsive - 响应式声明
@responsive { .bg-gradient-brand { background-image: linear-gradient(blue, green); } } /* output */ .bg-gradient-brand { background-image: linear-gradient(blue, green); } /* ... */ @media (min-width: 640px) { .sm\:bg-gradient-brand { background-image: linear-gradient(blue, green); } /* ... */ } @media (min-width: 768px) { .md\:bg-gradient-brand { background-image: linear-gradient(blue, green); } /* ... */ } @media (min-width: 1024px) { .lg\:bg-gradient-brand { background-image: linear-gradient(blue, green); } /* ... */ } @media (min-width: 1280px) { .xl\:bg-gradient-brand { background-image: linear-gradient(blue, green); } /* ... */ }
- @screen - 引用断点的媒体查询
/* normal responsive screen css */ @media (min-width: 640px) { /* ... */ } @screen sm { /* ... */ }
- theme() - 获取主题配置值
.content-area { height: calc(100vh - theme('spacing.12')); } .content-new-area { height: calc(100vh - theme('spacing[2.5]')); } .btn-blue { background-color: theme('colors.blue.500'); }
CSS 原子化框架使用案例
原子化其实很早就提出了,大概在18年,但是缺少大公司实践背书,所以社区热度比较低。由于react先天性对css的缺乏设计,所以更多的css理念实践在react的项目中会有更多收益,比如 css in js、utility first等。
*2020.04.27 - Facebook 利用 Atomic CSS 重构案例
Facebook 团队基于 Atomic CSS 的理念融合 css-in-js,然后重构了 Facebook 的项目,将 css 体积减少了80%(413kb -> 74kb)。有了国际大厂的背书,使的 Atomic Css 再次进入人们的视野,如果有玩过 vite,会发现社区模板,有很多都是标配 TailwindCss。
基于这次案例,挑了些优化进行分析解读。
分析优化:
- 体积优化,减少 80%
- 原因1:基于类的管理,使的css可以高度复用。通过对类的 tree-sharking 将未使用的css剔除。
- 原因2::类与css的映射足够细,最优的情况是只要加载一个基础css。
- 响应式更可控
- 基于 utility first 的工具统一理念,约束全局响应式规范。
- 多端复用
- 对于移动端,一般都使用 rem 单位,通过内部只转换,可以使的一套css配置,适配多端。
- 主题色、暗黑模式
- 通过直接修改类的映射,可以非常快速全局变化。
- 提高项目质量,降低失误风险
- 因为是通过类设置样式,只要你删除或修改html模板,类会一起变化,避免修改html时,忘记修改css,导致视觉事故。
分析优缺点
特性 | 优点 | 缺点 |
---|---|---|
基于工具化的理念 | 基于工具化的理念,项目后期维护迭代,样式的增量都是逐步放缓的。 | 入手成本较高,首先得知道有什么工具类,才能学会用 |
约束全局规范 | 使得整体视觉设计一致,提供多端适配能力,后期维护成本减低。 | 限制了个性化定制样式,花里胡哨的样式会增大样式冗余,强行抽离工具,反而增大开发成本。 |
css类的形式使用 | 降低同时维护样式和模板的隐患,解决开发命名难的问题。 | 类名增多,主观视觉上比较难看 |
清除无用样式 | 减少打包体积 | 无法使用动态拼接类名 |
提供了大量的基础类 | 能通过组合的形式满足业务场景,避免编写大量工具类 | 因为过度细分,导致Chrome检查样式,比较麻烦。(可以通过devtools可以解决) |
TailwindCSS 基本实现思路
简述
- 本质上 TailwindCSS 是一款 Postcss 的插件(Postcss 是一款用于css转换的工具)
- 因为是 Postcss 插件,底层依赖 Postcss ,所以你项目能支持到多少版本的 Postcss 直接影响到你使用 tailwindcss 的大版本。截止2021年6月,目前有 v0、v1、v2 三个版本,个人推荐v2,原因是灵活的拓展性。
实现思路
围绕着几大特性,来看实现,避免枯燥,对特性进行简单提要,不进行源码解读。
-
按需打包
-
借助 PurgeCSS 对字符串进行匹配,通过一个正则
/[^<>"'`\s]*[^<>"'`\s:]/g
进行筛查 -
移除自己生成未使用的工具类,和通过语法糖引入的样式,比如 @layout
-
大体上是借助 PurgeCSS 的能力,所以也想外直接暴露了 PurgeCSS,开发者可以通过学习 PurgeCSS 直接操纵匹配机制。
-
-
自定义配置
-
源码目录中,有个stubs文件夹,有三个文件,其中有个分别是
defaultConfig.stub.js
、defaultPostCssConfig.stub.js
、simpleConfig.stub.js
export const defaultConfigStubFile = path.resolve(__dirname, '../stubs/defaultConfig.stub.js') export const simpleConfigStubFile = path.resolve(__dirname, '../stubs/simpleConfig.stub.js') export const defaultPostCssConfigStubFile = path.resolve( __dirname, '../stubs/defaultPostCssConfig.stub.js' )
-
统一存储所有相关配置,然后完全暴露给调用方。
-
-
功能类(utility-first)
-
根据样式功能性进行分类
-
然后利用自身的函数和指令能力,组合输出对应类映射
-
交由编译器做对应输出转换
-
源码目录 src/plugins
-
-
指令和函数的语法糖
这里编写了相关指令和函数的规则,然后通过 postcss 的编译,进行校验和处理。
其他
- 如果对 Postcss 插件编写有兴趣的,可以看源码,整个目录结构非常清晰,并且入口文件的编写,都是非常标准的 Postcss 插件风格,学习过后可以提升一定的编码质量。
- 对于看开源代码,主要还是有目的性的看,第一手段是通过 commit 和目录结构找到模块,然后通过分析依赖和相关函数命名,梳理大致的流程,再回顾问题,细化到对应细节实现。
总结
从宏到微的形式,逐步了解学习理念和框架,理念这种东西很玄学,没办法保证大家都认同,算是种拓展思考,也是不错的。如果有分析错误,或者有其他错误的地方,可以及时指出,Peace and Love,我向你敬礼。