在css的世界,文档没有被得到充分的利用。由于文档对终端用户不可见,因此它的价值常常被忽视。另外,如果你第一次为css编写文档,可能很难确定哪些内容值得记录,以及如何能够高效完成编写。
然而,为CSS编写文档可以为你的项目提供很多好处:促进更好的代码实践、降低新员工的上手难度。在本文中,我将说明编写CSS文档的优点,并分享我认为的最佳实践。让文档为你工作,而不是阻碍你的工作。
1. 制定基本规则
当你和你的团队不清楚CSS文档具体需要编写哪些内容、文档应该如何工作时,你很难跟上编写CSS文档的潮流。因此,第一步是约定好你将使用哪些规则以及如何执行它们。你可以在一个动态文档中记录这些内容,这样团队中的每个人都能够参与其中。通过动态记录,当你的规则发生改变或增加内容时,你可以保持文档及时更新。一个共享的谷歌文档,你自己代码文档的wiki页面,或者(甚至更好)你自己的“实时样式指南”页面都是很好的放置文档的地方。
现在让我们看看你可以实际设置哪些“基本规则”。
2. 说明代码库的结构
了解代码是如何组织的,能够让任何人在第一天就上手代码。一个实现的简单方法是创建一个文件结构的映射文件,你可以在其中介绍文件都包含哪些内容以及去哪儿找到这些内容。这样做时要特别注意那些可能有歧义的地方。例如,标注“button.css”文件里包含buttons的样式并没有很大帮助,但是指出“custom”文件夹是项目自定义样式所在的位置,则可以为阅读的人节省时间。
下面是个例子:
project root
└── srs
├── styles // 基本样式,放在这里的样式应该在应用的任何地方都可用
├── bootstrap-custom // 覆盖基础样式的自定义样式
├── custom // 应用的自定义样式
├── demos // 演示样式和交互的示例
├── fonts
├── img // 只在样式表中使用的图片
├── variables // 自定义主题中使用的变量
└── styles.less // 导入所有基本样式
└── components
├── alerts
└── alert.less // alert组件的自定义样式。每个组件都有自己的样式表,可以定制它,防止代码臃肿和样式泄露
如果你想要覆盖由Bootstrap定义的样式,那么:
找出在Bootstrap框架中定义样式的样式表。
进入“src/styles/bootstrap-custom”。
查找相同的样式表。
如果不存在,请在该目录中创建一个相同名称的新样式表。
加上你重写样式的代码,并注释出重要的内容。
最后,在“src/styles/style.less”中导入新的样式。
场景 #2
如果你想要添加一个新样式,该样式不会覆盖Bootstrap原有样式,并且在应用的任何地方都要能引用到它,那么:
进入“src/styles/custom”。
找到一个可以添加该样式的样式表(请思考:这是特定的定义一个按钮的样式,还是可以复用的样式?),并将其放到最合适的地方。
如果现有的样式表都不适合加上这个新样式,那么就新建一个样式表。
按照我们的命名规则来命名它。
添加你的新样式并注释出重要的内容。
最后,在“src/styles/style.less”中导入新的样式。
场景 #3
如果你想为一个组件增加一个新样式(这意味着只有这个组件会用到这个新样式,无论该组件是应用在什么地方用到的),那么:
进入“src/components”。
找到你想要写样式的组件。
在这个组件的路径下找到改组件的样式表。
最后,在该样式表中添加你的新样式并注释出重要的内容。
这个指南:
帮助我们有条理地组织样式。
按照新样式的继承性质,新样式能够保持有效,因为我们在正确的地方重写的样式。
避免开发人员编写过于复杂的样式。
避免样式影响到非目标元素。
3. 建立你的编码标准
你的编码标准或者CSS样式指南是指你的团队就编写CSS达成的一致意见。这包括编写代码的最佳实践,如格式化、命名规则和语法约定。下面是我认为值得分享的几个有用方法:
不要做 vs 要做 列表
使用这种格式指出你需要避免的事情,并提供一个可行的替代方案。这可以消除歧义并鼓励人们去做一件具体的事。例如:
不要做 | 要做 |
---|---|
不要使用tab进行缩进。 |
要用四个空格进行缩进。 |
不要用下划线_或“驼峰法”来命名类或者ID。 |
要用破折号分隔单词。 |
不要用类或ID名来反映底层标记结构。.container-span 和 .small-header-div 是不好的名字。 |
要从对象的角度考虑CSS,并使用简单的名次作为名称。.global-alert 和 .badge是好的名字。 |
不要用ID或过于特定的选择器来写样式。只有在绝对必要的时候才使用它们(如表单控制或页面锚定)。 |
要用类来写样式,从而增加可复用性,减少CSS选择器冲突。 |
最佳实践列表
将你的指导原则总结为最佳实践,并举例说明。这会让大家更容易阅读和理解。例如:
最佳实践 | 举例 |
---|---|
在不同的行上写多个选择器。 |
.btn,
.btn-link { } |
在选择器和左括号之间间隔一个空格。 |
.selector {
} |
尽可能使用十六进制值的简写。 |
#fff vs #ffffff |
用小写字母写十六进制值。 |
#3d3d3d vs #3D3D3D |
将url放在单引号中。通常单引号要优于双引号,因为它更容易键入。 |
url (‘/image.png’) vs url (“/image.png”) |
除了角度(deg)和时间(s或ms)之外,不要为0值写单位。 |
margin-right: 0; vs margin-right: 0px; |
不同开发人员写代码的方式可能差别很大。这就是为什么团队制定编码标准很重要。这确保了代码在项目中是一致的,这使得阅读、编写和审查变得更加容易。但是请确保你在编码标准中包含的任何内容都是团队同意的实践。
我曾经做过一个项目,在这个项目的动态样式指南中实践了上面的内容。作为代码的一部分,我们会提交上传这些最佳实践。然后为了确保大家都能看到这些内容,在合并代码之前,团队中的每个人都要拉取这些内容。这保证了每个人都有时间来审查和讨论这些实践。
4. 避免冗长的样式表
当你把样式分解为更小、更集中的样式表时,更容易为它们写文档。这样也可节省时间,因为你不用为拆解到一目了然的样式表写文档。
例如,相较于用一个800行的样式表来包含所有的样式类型,你可以为每种样式类型写一个文件。这样可以节省时间,因为你不必在一个文件中上下滚动来查找某些内容,在每次添加或重命名一个新部分时,也不必每次更新索引。
在一个长文件中,一个长索引……
\*-------------------------------------------*/
variables.less
Index
- Color Palette
- Typography
- Buttons
- Forms
- Layout
- Messaging & Status
- General
- Navigation
- Carousel
- Jumbotron
\*-------------------------------------------*/
拆分文件,不再需要索引:
在大型应用中工作时可以参考的另一个示例是modlet workflow https://css-tricks.com/key-building-large-javascript-apps-modlet-workflow/。它解释了为什么使用较小文件可以更容易地在应用程序中测试和组装它们。
5.利用约定的样式指南编写CSS文档
写好CSS编写文档的一个重要原因是写好的CSS样式,反之亦然。这意味着即使你的CSS代码库不是最好的,执行文档规则也可以使你朝着更好的系统前进。
这就是说我们需要用约定的样式指南来编写CSS文档。这背后的理念是,样式指南可以帮助你确定良好的CSS结构,为了创建良好的结构你需要区分:
定义应用外观和感觉的基础样式(包括你正在使用的任何CSS框架)
为特定组件写的自定义样式
为特定页面写的自定义样式
CSS的大部分应该由基础样式组成,因为它们在应用程序中的任何地方都可用,并且影响到处于默认状态的所有元素。自定义样式应该在你添加具有特定外观和行为的组件时才写,或者在页面中某个元素或组件的布局需要特殊处理的情况下才写。
描述这个特定的设置如何在您的应用程序中工作的一个很好的方法,就是创建一个样式指南站点地图。一旦你了解样式指南在应用程序中的样子,就可以利用约定的样式指南来为元素编写文档。例如,如果你已经在样式指南中定义了按钮和导航的外观,那么就很明确应该在哪里为它们添加新的样式和文档(在“buttons.css”和“Navs.css”中)。但是对于用按钮组成的导航呢?
有一个样式指南可以帮助你做出这个决定,你可以从展示和使用标签的角度来比较按钮和导航的外观。
<button type="button" class="btn btn-primary">Primary</button>
<button type="button" class="btn btn-secondary">Secondary</button>
<button type="button" class="btn btn-emphasis">Emphasis</button>
<button type="button" class="btn btn-primary disabled">Primary</button>
<button type="button" class="btn btn-secondary disabled">Secondary</button>
<button type="button" class="btn btn-emphasis disabled">Emphasis</button>
< ul class="nav nav-tabs" >
< li class="active">< a href="#" >Active< /a >< /li >
< li >< a href="#" >Nav Item< /a >< /li >
< li >< a href="#" >Nav Item< /a >< /li >
< li class="disabled ">< a href="#" >Nav Item< /a >< /li >
< /ul >
在这个例子中,CSS有两个点来定义按钮组成的导航:
如果标签遵循导航的结构,使用带链接的列表,或者是看起来像按钮的带连接的< nav >标签,那么将导航样式添加到“navs.css”。
如果你使用的标签是< button >,那么将样式添加到“buttons.css”中。你甚至可以将它添加为单独的样式表(如“buttons-groupp.css”)。在这种情况下,“导航”一词将不再合适,因为HTML按钮作为导航项可访问性较低。
6.将你的样式表分成几个部分
一旦你将样式表分解成更易于管理的小文件,那么你还可以通过将每个样式分解为各个单独的部分来继续这一练习。
首先,每个样式表至少应该包括一个标题和简短的描述(如果有用)。标题可以与文件名一样简单,首字母大写以便更像标题(例如:样式表“buttons.css”的标题可以是“Buttons”),也可以与文件名相同,如下所示:
/**
* icons.css
*/
.icon {
font-family: 'bitovi';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
}
在浏览器调试代码的时候,使用文件名写标题特别有用。在大多数情况下,文件已经和其他文件一起编译了,我可以根据标题找到相关样式写在哪个文件中。
另外请注意,我所使用的注释样式是用/*开头而不是/。这是一个在JSDoc中使用的惯例,用来解析应该包含在自动生成文档中的注释。我建议使用这种风格,因为许多在使用的样式指南生成器使用JSDoc格式,当你准备使用生成器时,你的代码将需要很少的额外改动。
无论如何,你可以使用其他样式来注释一段代码,例如:
/*-------------------------------------------*\
icons.css
\*-------------------------------------------*/
.icon {
font-family: 'bitovi';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
}
在某种程度上,这取决于你的团队认为什么样式最能使一段代码脱颖而出。唯一的要求是在开始的时候使用/ ,在结束时使用 /。真正重要的是,无论使用哪种方法,都要坚持使用它,并在CSS代码中贯彻使用。
如果你觉得一段描述在特定的样式表中很有用,你可以添加它作为第一个注释的一部分。例如:
/**
* icons.css
*
* 图标应该以简单而有意义的方式传达它们所代表的功能的概念。
* 当设计新的图标时,一定要消除任何复杂的内容,并遵循图标集的线性和轻量级外观。
*
*/
.icon {
font-family: 'bitovi';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
}
这样做会加强把代码拆分成几个小部分的想法。此外,试着将描述分解成多行(Harry Roberts推荐最多80个字符),这样在打开多个文件或在Github上阅读时更容易阅读。
在添加了标题和描述之后,你可以更进一步,将样式表中的样式划分为几个部分。要做到这一点,请考虑如何从逻辑上解释样式表的内容。例如,样式表“buttons.css”通常会有一部分内容,通过.btn类名来定义按钮的默认样式。然后将有更多的样式定义不同的颜色、大小和结构,可以结合起来进一步定制按钮的外观和行为。把每一种样式单独放在一小节中将让你更容易理解和找到新样式或覆盖样式应该放在哪儿。此外,当代码以片段形式显示时,查看文件并不那么可怕,而在一个很难分辨样式是从哪里开始、到哪儿结束的长文件中,查找内容是很可怕的事。
让我们来看个比较的例子。首先,一个没有分段的代码块:
.label-condensed-variant(@color) {
&:before {
background-color: @color;
}
}
.label {
border-radius: 0;
font-family: @font-family-bold;
font-size: 85%;
position: relative;
&.label--condensed {
font-size: @font-size-xsmall;
color: @gray;
background: transparent;
padding-right: @padding-small;
&.label--primary {
.label-condensed-variant(@label-primary-bg);
}
&.label--success {
.label-condensed-variant(@label-success-bg);
}
&.label--info {
.label-condensed-variant(@label-info-bg);
}
&.label-warning {
.label--condensed-variant(@label-warning-bg);
}
&.label--danger {
.label-condensed-variant(@label-danger-bg);
}
}
&.label--simple {
font-family: @font-family-base;
font-size: @font-size-xsmall - 1;
color: @gray;
border: 1px solid @gray-light;
padding: 2px;
border-radius: @border-radius-small;
text-transform: uppercase;
}
}
接下来是同样的代码块进行了分段处理:
/**
* bootstrap-custom/_labels.less 标签
*
* 覆盖由bootstrap框架定义的默认样式。
*
*/
.label {
border-radius: 0;
font-family: @font-family-bold;
font-size: 85%;
position: relative;
}
/**
* 简明标签
*
* 修改标签以提供带有彩色圆圈的较小和较窄的版本,以便在空间较小的区域使用(例如,在列表视图中)。
*/
.label {
&.label--condensed {
font-size: @font-size-xsmall;
color: @gray;
background: transparent;
padding-right: @padding-small;
}
}
/**
* 简明标签 - 颜色
*/
.label-condensed-variant(@color) { // 用变量来设置圆的颜色
&:before {
padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: inherit !important; font-size: inherit; line-height: inherit; color: rgb(91, 218, 237); word-break: inherit !important; white-space: inherit !important;">@color;
}
}
.label {
&.label--condensed {
&.label--primary {
.label-condensed-variant(@label-primary-bg);
}
&.label--success {
.label-condensed-variant(@label-success-bg);
}
&.label--info {
.label-condensed-variant(@label-info-bg);
}
&.label--warning {
.label-condensed-variant(@label-warning-bg);
}
&.label--danger {
.label-condensed-variant(@label-danger-bg);
}
}
}
/**
* 简单标签
*
* 修改标签以提供不使用颜色的简单线性版本。
*/
.label {
&.label--simple {
font-family: @font-family-base;
font-size: @font-size-xsmall - 1;
color: @gray;
border: 1px solid @gray-light;
padding: @padding-small;
border-radius: @border-radius-small;
text-transform: uppercase;
}
}
7.为样式表的内容设置索引
这是提供样式表内容快照的好方法,尤其是在那些不知出于什么原因,必须存在长样式表的项目中(虽然不喜欢这样的项目,但是它们确实存在)。
样式表索引通常长这样:
/**
* icons.css
*
* 图标应该以一种简单而有意义的方式表达它们所代表的函数的概念。
* 在设计新图标时,一定要删除任何复杂的内容,并遵循图标集的线性和轻量级外观。
*
* Index
* - Icon Font
* - Icon Variations
* - Icon Animations
*
*/
尽管我很喜欢索引的整洁和实用,但我不得不承认它们很容易被遗忘,因此也就会慢慢过时了。当它们很长时,更新它们也是一件痛苦的事情,尤其是你正在使用数字来写索引(所以请避免使用这些数字!)
使用索引的另一种方法是让样式指南成器通过查看样式表来为你工作,查找你定义的部分,并为你生成索引。在本文的最后,我将进一步讨论这个主题。
8.找到文档的最佳平衡点
这是文档的秘诀所在。刚开始写文档时很容易被带动,进入写文档的*状态,然后忘记它,最后只剩下你的代码库中有一部分代码被过度文档化了,剩下的代码就没有文档了。和生活中的一切事情一样,秘诀就是找到平衡。记录那些需要注意的部分,比如有不可预见的依赖、额外的资源或需要注意的重要事项。也就是说,并不是所有代码都应该被记录下来,但是将代码分解成块,并在必要时解释这些代码块是什么绝对是有用的。通过这种方式,文档变成了一个有用的工具,它是你工作流程的一部分,而不是你避免去做的事后添加的东西。那你到底是应该怎么做呢?下面举个例子:
假设你需要实现下面卡片组件的标记和CSS:
根据这个设计,你会发现需要设计以下各部分样式:
基础卡片样式设计
卡片网格
卡片列表
黑色版卡片
你可以使用这些样式分解CSS实现,并使用文档来指导。首先,你的“cards.css”样式表可以包含一个简单介绍如下:
/**
* cards.css
*
* 以下是card组件的样式。
*
* Index
* - Card Base
* - Card Grid
* - Card List
* - Card Dark
*/
你可以在介绍中包含更多有用的信息,但是由于才刚刚开始,一些简单的介绍就可以帮助建立文档框架。
然后,我们将在其中添加你需要的部分:
/**
* cards.css
*
* 以下是card组件的样式。
*
* Index
* - Card Base
* - Card Grid
* - Card List
* - Card Dark
*/
/**
* 基础卡片
*/
/**
* 卡片网格
*/
/**
* 卡片列表
*/
/**
* 黑色版卡片
*/
考虑到这些部分的样式,你可以可视化代码的结构。你应该知道,卡片的基础样式设计应该灵活且独立,这样你就可以轻松地使卡片在网格、列表或黑色版本中工作。
然后,在编写代码时,你可以通过注释使描述更具体:
/**
* 基础卡片
*
* 主要用来定义默认卡片的外观和行为:
* - Card Image
* - Card Content
* - Card Footer
*/
.card {...}
.card__image {...}
.card__logo {...}
.card__content {...}
.card__footer {...}
我认为这些是文档应该包含的基本内容,因为它可以作为代码布局的指南,并且可以快速地告知下一个工作的人代码是如何组织的。
下一步是添加特定规则的注释,对于不是特别明确的样式,这些注释可以用来解释样式是做什么用的。例如:
/**
* 基础卡片
*
* 主要用来定义默认卡片的外观和行为:
* - Card Image
* - Card Logo
* - Card Content
* - Card Footer
*/
.card {
@media (max-width: {{ screen-xs }} ) {
border: none; /* 因为卡片在手机上占用了100%的屏幕 */
}
}
.card__image {...}
.card__logo {...}
.card__content {
flex: 1 1 auto; /* "auto" 需要加上,以避免在IE浏览器崩溃。*/
}
这种方法的优点在于,文档可以在代码实现的过程中起到支持和指的作用,而不是代码完成后添加的内容。
下面是一些Bootstrap框架的例子,这些例子说明了注释是有用的,是值得深入研究细节的。
示例 #1
// Need .dropdown-toggle since :last-child doesn't apply,
// given that a .dropdown-menu is used immediately after it
.btn-group > .btn:last-child:not(:first-child),
.btn-group > .dropdown-toggle:not(:first-child) {
.border-left-radius(0);
}
这个注释说明了为什么这些样式存在,以及它们在做什么。它也很短,也很关键,用一种随意的语言来表达想法。
示例 #2
// Checkbox and radio options
//
// In order to support the browser's form validation feedback, powered by the
// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use
// `display: none;` or `visibility: hidden;` as that also hides the popover.
// Simply visually hiding the inputs via `opacity` would leave them clickable in
// certain cases which is prevented by using `clip` and `pointer-events`.
// This way, we ensure a DOM element is visible to position the popover from.
//
// See https://github.com/twbs/bootstrap/pull/12794 and
// https://github.com/twbs/bootstrap/pull/14559 for more information.
[data-toggle="buttons"] {
> .btn,
> .btn-group > .btn {
input[type="radio"],
input[type="checkbox"] {
position: absolute;
clip: rect(0,0,0,0);
pointer-events: none;
}
}
}
这是一个更深入的文档示例,解释了实现决策背后的逻辑,并提供了附加信息的链接。
更进一步
了解了这些最佳实践,下一步是将动态样式指南作为文档的一部分。动态样式指南是一个实时文档,它显示你在代码中包含的注释,就像一个网站一样,因此你可以独立于源代码来浏览文档。
动态样式指南强大的地方是,实际的文档与代码一起变化,并可以很容易地随着代码的变化而更新,从而使其保持同步和相关。
另一个好处是,你可以让团队中的其他人使用这些文档,这些人可能不会直接与代码交互(比如设计人员、产品经理或QA工程师)。这些团队成员也会发现,了解UI是如何形成的也很有帮助。
在实时样式指南中,你可以包括代码的交互式演示,并且可以独立于代码结构进一步组织文档。
总结
文档化CSS首先要有明确的规则和结构良好的代码库。当把文档作为工作流程的一部分完成时,它也可以作为一个指南来构造您的代码,并在代码增长时保持组织结构。这样额外的好处是,可以清楚地说明代码的位置,以及应该添加新代码的地方,这样可以在快速跟踪开发的同时降低新成员的上手难度。
最后提醒:8个最佳实践
制定基本规则
说明代码库的结构
建立你的编码标准
避免冗长的样式表
利用约定的样式指南编写CSS文档
将你的样式表分成几个部分
为样式表的内容设置索引
找到文档的最佳平衡点