最好的CSS方式——OOCSS+ Sass
OOCSS导致HTML难以维护
首先是简单的声明,其次是一大把可能吓坏你的非语义化的词语。这里,实际上我不关心它们被非语义化,我关心的是它们可维护的意义。非语义化的类不需要描叙成那些可能会变成的组件名。
用单纯的CSS建模的唯一方式是定义非语义化的类,然后你把这些类应用到HTML元素中。这就是接触模型的OOCSS方式。但是,问题来了:
1、我不想每次改变样式都得带上HTML。从一开始,事情就在一直变。
2、我甚至不想接触那些我要增加类的那些DOM元素.如果你正在使用Javascript组件渲染页面元素,那你就不能给组件里面的元素增加类(除非你在做一些无法描叙的事情)。除了那些不可维护的HTML,OOCSS其他方面还是完全可行的。抽象重复的代码到模块的唯一方法是让CSS在大型项目中变得可维护。那怎样我们才可以做到百利而无一害呢?
解放OOSass!
OOCSS和Sass的结合给了你超强力量。Sass上的@extent指令让你从另外的选择器上继承样式而不需要复制所有的代码,像一个代码块(@mixin)。如果你内嵌他们或和内嵌选择器一起使用他们,即便那些让人感觉亲切的@extent调用也能导致代码臃肿。
幸运的是Sass3.2增加了一个叫占位符(Placeholder)的新特性。占位符是那种除非他们被扩展否则什么也不输出的选择器。下面是个占位符的例子:%separator border-top: 1px solid black hr @extend %separator .separator @extend %separator它会生成下面这样的CSS:
hr, .separator { border-top: 1px solid black }占位符不会导致像混合类型或@extent调用有的那种代码臃肿的问题。这使得占位符完美适用在那些非语义化CSS模块。我叫这些模块为“模式”。他们是那种小段的样式,可以通过你的样式进行混合搭配。
真实样例展示
采用OOCSS黄金产物作为例子: .media模块。你可能想应用.media模式到你组件的一个分支: .status 、.profile等等。
情况就是这样的,你不想再HTML里面重复.media类,但你想把它的样式复制到.status和.profile类上。使用占位符很容易就可以实现。 下面是我们给出的%media模式。%media overflow: hidden &:first-child float: left &:last-child overflow: hidden现在你不需要复制media类的代码,你只要扩展 %media 模式到你任何想要使用的地方就可以了。
.status @extend %media // Status-specific styles here... .profile @extend %media // Profile-specific styles here...这意味着在HTML里面你只需要增加一些语义化的类:.status和.profile——这些你不用在意类型因为你只在<article>元素中用到它了。
这东西很灵活。如果你要改变状态显示方式让.media模式不再应用只要移除@extend调用就可以了,而不用删除.media类。
眼尖的话应该注意到了 这里我使用了稍稍修改过的.media模式版本。这是因为当使用Javascript组件时不能使用DOM...
OOSass让给Javascript组件定义样式变得容易。
OOCSS的最大问题是它已经假设你对DOM和增加的类完全控制。那是不可能的。当你渲染Javascript组件或其他一些操作时,你只可能拿到组件最顶端的元素。
如果你要将.user-dropdown类应用到下拉视图上,你可以扩展一个.media类到.user-dropdown类上。但是要增加类到下拉菜单的按钮上或它的任一餐单项上是不可能的,因为你不能控制组件里面的DOM元素。
不过有了Sass占位符,这不是问题:.dropdown // Normal styles for every dropdown... .user-dropdown // Extra styles for user-specific dropdowns... .menu-item @extend %media要是你想用纯粹CSS类你就不得不做些粗俗的事情:进入组件里面破坏它们的分装,或使用一些可怕的基于字符串的类名API。使用Sass模式你很容易就增加了DOM元素而不需要直接控制它。
好了,看了例子吧。
我喜欢读别人的CSS模式,就好像已经分享了我自己的一些代码。下面是我在整个Segment.io上使用到得一些模式。Lip
这是一个苹果风格的分隔符,在它下面创建了一个在内容上面的lip类(注意我也用了%reversed-lip来控制在向反方向的lip)%lip clear: both display: block height: 5px background: url('/public/images/patterns/lip/lip.png') no-repeat background-size: 100% 100% %reversed-lip @extend %lip background-image: url('/public/images/patterns/lip/reversed-lip.png')
Valley
这里只是增加两个lip到元素的顶部和底部,让它感觉像是嵌入进去的。%valley position: relative overflow: hidden &::before, &::after content: '' position: absolute left: 0 right: 0 &::before @extend %lip top: 0 &::after @extend %reversed-lip bottom: 0
Plane
一个非常简单的圆角盒子。这是在Segment.io上所有色块的基础。%plane box-shadow: 0 2px 5px rgba($black, .1) border-radius: $border-radius-medium %white-plane @extend %plane background-color: $white %off-white-plane @extend %plane background-color: $off-white ...
Seam
你知道那些人们通过把黑线和白线放在一起做成半透明的边框不?我叫它seam。%seam clear: both display: block height: 0px border-top: 1px solid rgba($black, .12) border-bottom: 1px solid rgba($white, .15)
Well
和valley差不多,它只是页面的一块洼地,对于像<code>这样的例子。(它和这个博客的代码例子很相似。)%well box-shadow: inset 0 1px 5px rgba($black, .14) border-radius: $border-radius-medium %off-white-well @extend %well background-color: $off-white %light-gray-well @extend %well background-color: $light-gray ...
准备开始吧
希望上面这些能给你一些启示——什么事模式能做的及怎样在你的CSS组件中使用他们。它们无处不在。
它们应该只做一件事情并把它们做好。Harry Roberts提到应该给它们一个模糊的非语义化的名称。这样促使你把它们抽象出来,以便在任何地方使用。你还可以每次都在最前面建一些模式,就像我在valley的例子中做的一样。
如果你自己也拥有一些类似的模式那就更好了。