最好的CSS方式——OOCSS+ Sass

12年前
面向对象的CSS很不错,但是让非语义化的词标识你的类是很不明智的。这些类散布在你的HTML里面等着去做调整,会变得很没意思。但是如果你把OOCSS和Sass结合就让未经渲染的模块化CSS和难以维护的HTML变得两全其美了。

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的例子中做的一样。

如果你自己也拥有一些类似的模式那就更好了。