Github:https://github.com/nzbin/snack-helperDocs:https://nzbin.github.io/snack-helperPreface什么是helper?没有框架是万能的,但业务需求是多种多样的。很多时候,我们只需要改变组件的一些属性,helper就是一个调整细节的工具。我在之前的文章《如何编写轻量级 CSS 框架》中也举了一个例子,我们不需要因为几个属性的不同而去重写新的组件。大部分helpers是一个CSS属性对应的类,属于最小的类。通过对工作的实践总结,我认为编写一套好用易懂的助手是非常重要的。本文的目的是讨论helpers的组件,如何编写,以及如何简化helpers的命名。组件在详细介绍如何编写helper之前,先说说我对组件的看法。之前写一个轻量级的CSS框架的时候,我们都是用组件的方式开发的。写一个helper更像是开发一个part,因为一个helper只有一个属性,多个helper可以组成一个组件。例如下面的例子:假设有一个.boxescomponent.boxes{border:1pxsolid#eee;边界半径:5px;底部边距:15px;overflow:hidden;}假设有如下helper.b-1{border:1pxsolid#eee!important;}.r-5{border-radius:5px!important;}.m-b-15{margin-bottom:15px!important;}.overflow-hidden{overflow:hidden!important;}then.boxes=.b-1+.r-5+.m-b-15+.overflow-hidden我是一个模型爱好者,这个组合让我想起了寿屋的HEXAGEAR系列模型,这个系列的特点是“零件+零件=组件,组件+组件=骨架,骨架+骨架=素体,素体+武装=素体”。在写helper的时候,基于上面的思路,我在想,能不能把helper拆分的足够细,让它自己形成一个框架,即“parts+parts=components,components+components=framework”。遗憾的是,我的想法已经被实践了。前几天在浏览GitHub的时候发现了一个相关的项目tailwindcss。该框架基于helper,通过属性叠加添加样式。基于组件的框架和基于组件的框架是两种完全不同的思想,彼此之间没有区别,各有优缺点。Helper的组成一套完整的Helper应该包含什么?常用的有padding、margin、font-size、font-weight等,为了写出更通用的helper,我们需要更细粒度的划分。虽然我们不打算把它写成一个框架,但我们希望辅助功能足够强大。通过比较和思考,我暂时将helper分为以下几个模块:Colors(颜色,包括bg-color和text-color)Paddings(内边距序列)Margins(外边距序列)Typography(排版,包括font-size和font-weight)Border(边框线)Radius(圆角)Shadow(阴影)Size(尺寸,包括高度和宽度)Gutters(网格间距序列)Alignment(主要是vertical-align)...和写light之前的Quantum框架一样,我们还使用了Sass预编译器。Helper类几乎总是在Sass循环中生成,因此源代码看起来很精简。颜色可变因为颜色有点特殊,所以我会和其他内容分开介绍颜色。在写轻量级框架的时候,我也定义了一些常用的颜色,但是遇到特殊需求的时候就有点单一了,所以需要用helper来扩展颜色簇。但色彩是一个无法量化的概念,所以无论多么强大的帮手,都不能包罗万象,只能是一定程度上的补充。参考了常用的色值,最后设置了红、橙、黄、绿、蓝、蓝、靛、紫、粉、冷灰、暖灰几个色系。每种颜色都有六个亮度值,分别用-lightest、-lighter、-light、-dark、-darker、-darkest表示。这里是引用tailwindcss的颜色命名。这些颜色都是由Sass的颜色函数生成的。以gray为例,Sass代码如下:$gray:#999;$gray-light:lighten($gray,15%);$gray-lighter:lighten($gray,25%);$gray-最亮:变亮($gray,35%);$gray-dark:变暗($gray,15%);$gray-dark:变暗($gray,25%);$gray-darkest:变暗($gray,35%);这些色序看起来很像一套记号笔,只不过记号笔的灰色更加丰富,有冷灰、暖灰、蓝灰、绿灰。背景色循环方法如下。为了方便循环,我们定义一个颜色图,然后使用@each方法进行循环。$color-list:('灰色':$灰色,'棕色':$棕色,'红色':$红色,'橙色':$橙色,'黄色':$黄色,'绿色':$绿色,'蓝绿色':$teal,'blue':$blue,'indigo':$indigo,'purple':$purple,'pink':$pink);@each$name,$colorin$color-list{.bg-#{$name}{背景颜色:$color;}.bg-#{$name}-light{背景颜色:变亮($color,15%);}.bg-#{$name}-lighter{background-color:lighten($color,25%);}.bg-#{$name}-lightest{background-color:lighten($color,35%);}.bg-#{$name}-dark{背景色:变暗($color,15%);}.bg-#{$name}-darker{背景颜色:变暗($color,25%);}.bg-#{$name}-darkest{背景色:变暗($color,35%);}}命名策略当然,我提到了命名策略。在写轻量级框架的时候,我也关注类的命名策略,比较了一些框架的命名方式。无论是framework还是helper,类的命名决定了它的易用性,也会影响到用户的使用习惯,所以我会从简洁、直观、易用的角度来命名。但是helpers的命名比较简单,因为大部分都是单一的CSS样式,所以命名策略基本上是对CSS属性的抽象和简化。数字命名VS。维度命名我在工作中接触过两种helpersequences的表示方式,一种是普通数值型,一种是维度型。以padding为例:numbertype.p-5{padding:5px!important;}.p-10{padding:10px!important;}.p-15{padding:15px!important;}.p-20{padding:20px!important;}.p-25{padding:25px!important;}size.p-xs{padding:5px!important;}.p-sm{padding:10px!important;}.p-md{padding:15px!important;}.p-lg{padding:20px!important;}.p-xl{padding:25px!important;}size类型虽然在实际应用中没有任何问题,但很明显其扩展性是差而且不直观。举个例子,我只写了五个值,但是如果我们要添加更多的填充值,sizetypename就会用完。在我看来,所有可量化的属性,如padding、margin、font-size、border-width等,都应该直接用数值表示,而对于不可量化的属性,如box-shadow,更合适用大小来命名它们。简洁的命名大多数帮助器名称都是CSS属性的首字母缩写词。比如p表示padding,m表示margin,f-s表示font-size等,满足了我们对简单直观的追求。但我们不能只谈论缩写。所有的名字都使用缩写,因为有些属性的缩写会重复,而有些缩写的具体含义在缩写之后就不知道了。我们可以按照前面的规则,对可以量化的属性使用缩写,对不能量化的属性使用简化的全称(比如box-shadow可以用shadow代替)。以填充循环为例:@for$counterfrom0through6{.p-#{$counter*5}{padding:($counter*5px)!important;}.p-t-#{$counter*5}{padding-top:($counter*5px)!important;}.p-r-#{$counter*5}{padding-right:($counter*5px)!important;}.p-b-#{$counter*5}{padding-bottom:($counter*5px)!important;}.p-l-#{$counter*5}{padding-left:($counter*5px)!important;}}对于其他几个类似的助手,循环也很简单。关于MarginNegativemarginhelpers相对于其他的比较特殊,因为它有负值,所以我们必须考虑如何表示负值。一些框架使用n(负)表示负值。例如,m-{t,r,b,l}-n-*的形式:.m-t-n-5{margin-top:-5px!important;}.m-r-n-5{margin-right:-5px!important;}。m-b-n-5{margin-bottom:-5px!important;}.m-l-n-5{margin-left:-5px!important;}我觉得可以简化一步,用-来表示负值,这样比较好理解,如下:.m-t--5{margin-top:-5px!important;}.m-r--5{margin-right:-5px!important;}.m-b--5{margin-bottom:-5px!important;}.m-l--5{margin-left:-5px!important;}这种命名方式虽然很简洁,但是看起来和其他的helper不一致。圆角的CSS属性称为border-radius。如果直接缩写,会和border-right重复。corner-rounded、rounded等表示方法参考其他框架。我们也可以简化一下,比如直接用r同时表示圆角和半径,一石二鸟。这样的表示方法应该没有歧义。毕竟在我们的印象中,r表示半径是一个根深蒂固的概念。Sass代码如下:@for$counterfrom0through10{.r-#{$counter}{border-radius:($counter*1px)!important;}.r-t-l-#{$counter}{border-top-left-radius:($counter*1px)!important;}.r-t-r-#{$counter}{border-top-right-radius:($counter*1px)!important;}.r-b-r-#{$counter}{border-bottom-right-radius:($counter*1px)!important;}.r-b-l-#{$counter}{border-bottom-left-radius:($counter*1px)!important;}}我们使用-full100%,其他框架基本相同。后面会讲r-100%的可行性和问题。.r-full{border-radius:100%}.r-t-l-full{border-top-left-radius:100%}.r-t-r-full{border-top-right-radius:100%}.r-b-r-full{border-bottom-right-radius:100%}.r-b-l-full{border-bottom-left-radius:100%}同样,高宽的100%值也用-full表示,循环类似。关于阴影,我们多次提到阴影是非量化属性,所以我们只能使用基于大小的命名法。当然,用数字也不是不可以。稍后我会详细解释。先看源码:.shadow-xs{box-shadow:01px5px1pxrgba(0,0,0,.15);}.shadow-sm{box-shadow:02px10px2pxrgba(0,0,0,.15);}.shadow-md{box-shadow:03px20px3pxrgba(0,0,0,.15);}.shadow-lg{box-shadow:04px30px4pxrgba(0,0,0,.15);}.shadow-xl{box-shadow:05px40px5pxrgba(0,0,0,.15);}总体来说还是比较简单的,不过我加了值阴影大致可以,实际情况需要调整。说句题外话,我个人认为非量化的属性本身可能用处不大,因为这些属性能满足业务需求的可能性很小,但仍然是不可获得的部分。所以“通用”助手不一定是通用的。关于强度的表示下面说一下通过font-weight来表示强度。font-weight这个CSS属性本身有两种表示,一种是直接的文字命名,比如.f-s-thin、.f-s-normal、.f-s-bold等,另一种是更直接的100~900的数值表示。从我个人的角度来看,我更喜欢数字表示,简单直观,没有歧义。可以看作是约定俗成的规则。font-weight的循环比较简单,取值有限,我们可以直接写100到900的所有helpers。其他类似的helpers也可以用100~900来表示强度,比如颜色。需要注意的是,在写helper的时候,一定要将数值类型、大小类型、强度类型的名称分类统一,切记乱用。类名中的特殊字符允许写成r-100%或w-100%,但在定义CSS时必须转义,如.r-100\%{border-radius:100%}使用的方法如下
