CSS 属性值语法
2018年11月14日 07:48:36
W3C 使用了一套特别的语法来定义 CSS 的属性值,能让所有的 CSS 属性都用简明的方式表示。如果你曾看过 CSS 规范,你可能已经见过这套语法了。就像下面这样:
<'text-shadow'> = none | [ <length>{2,3} && <color>? ]#
如果你不知道这些符号以及它们如何工作的话,这套语法可能非常难理解。然而,这值得花时间来学。如果你理解 W3C 是如何定义这些属性值的,你就可以理解 W3C CSS 规范中任意一个了。
巴科斯范式(BNF)
这套语法是基于 BNF(Backus–Naur Form, 巴科斯范式) 规范的一个变种,在详细介绍这套语法前,我们先简单了解下 BNF 规范。
BNF 规范是一组推导规则的集合,用来描述计算机语言语法的正式符号集,它被设计得很清晰,所以在语言如何表达方面不会造成二义或者模糊。推导规则长的像下面这样:
<symbol> ::= __expression__
式子左边通常是一个非终止符,跟着一个::=
符号,代表着“可被换为”。式子右边表达式__expression__
由一或多个符号序列组成(通过|
连接),这些符号序列被用来推导左侧符号的意义。
BNF 规范从根本上说,“无论左侧式子是什么,也无论右侧式子是什么,左侧的式子都能被右侧的式子替换”。
非终止符和终止符
非终止符是指能在之后被替换或被分解的符号。在 BNF 中,非终止符通常都在尖角括号中,<
与>
。在下面的例子中,<integet>
和<digit>
是非终止符。
<integer> ::= <digit> | <digit><integer>
终止符表明这个值不能被替换或者分解。在下面的例子中,所有的数值都是终止符。
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
属性值定义语法
尽管 CSS 的属性值是基于 BNF 的概念,但他们有一些不同。像 BNF 的是,它起始于一个非终止符。不像 BNF 的是,CSS 中还描述了用在表达式中作为成分值(component value)的符号。
类型
总共有四种成分值类型:关键字值、基础数据类型、属性数据类型和非终结符类型。
总的来说只有两种形式:终结符和非终结符。关键字值可以看作是终结符,基础数据类型和属性数据类型都可以看做是非终结符类型的特殊形式。
关键字值(keyword values)
关键字值以字面值出现,没有引号('
)和尖括号(<>
)包围。它们可直接作为属性值。因为它们不能再被代替或分解,所以它们是终止符。
在下面的例子中,thin、medium 和 thick 都是关键字值。这意味着它们在 CSS 中直接使用。
<line-width> = <length> | thin | medium | thick
基础数据类型(basic data types)
基础数据类型由尖括号(<>
)包围。它定义了一些核心值,如<length>
与<color>
。它们是非终止符,因为它们可以被替换成真实的长度或颜色值。
在下面的例子中<color>
是基础数据类型。
<'background-color'> = <color>
<color>
可以被替换为颜色关键字、十六进制颜色值、RGB、RGBA、HSL、HSLA,或 transparent 关键字。例如:
.example { background-color: red; }
.example { background-color: honeydew; }
.example { background-color: rgb(50%,50%,50%); }
.example { background-color: rgba(100%,100%,100%,.5); }
.example { background-color: hsl(280,100%,50%); }
.example { background-color: hsla(280,100%,50%,0.5); }
.example { background-color: transparent; }
属性数据类型
属性数据类型由尖括号(<>
)包围,括号内是 CSS 属性名(带引号),它表示了该属性的取值范围。
在下面的例子中,<'border-width'>
是属性数据类型。
<'border-width'> = <line-width>{1,4}
属性数据类型可作为属性直接出现在我们的 CSS 文件中。在下面的例子中,border-width 属性给 .exmplate 类定义了 2px 的边框。
.example { border-width: 2px; }
非属性数据类型
和属性数据类型相同的是都由尖括号(<>
)包围,不同的是括号内容不是 CSS 属性名,也不需要带引号('
)。它是非终止符,定义了某个(些)属性的一些方面。
例如<line-width>
不是个属性,但它是一个定义了各种<border>
的数据类型。
<line-width> = <length> | thin | medium | thick
<'border-width'> = <line-width>{1,4}
连接符
上面讲了 CSS 属性值有四种成分类型,这些成分通过连接符进行组合形成丰富的表达能力,下面是这些连接符,类似正则表达式中的某些元字符。
并列
成分值之间使用空格符连接,表示所有的成分值都必须出现,且按给定顺序。
例如下面的属性的取值组合只有一种。
<'property'> = A B
.example { property: A B;}
&&
成分值之间使用两个与符号连接,表示所有成分值都必须出现,顺序任意。
例如下面的属性的取值组合有下面几种。
<'property'> = A && B
.example { property: A B;}
.example { property: B A;}
||
成分值之间使用两个竖线连接,表示至少有一个成分值出现,顺序任意。
例如下面的属性的取值组合有下面几种。
<'property'> = A || B
.example { property: A B;}
.example { property: B A;}
.example { property: A;}
.example { property: B;}
|
成分值之间使用单个竖线连接,表示有且仅有一个成分值必须出现。
例如下面的属性的取值组合有下面几种。
<'property'> = A | B | C
.example { property: A;}
.example { property: B;}
.example { property: C;}
[ ]
中括号用于分组,括号内的成分值和连接器组成一个独立的成分值,可以和上面介绍的连接器一起使用。
例如下面的属性的取值组合有下面几种。
<'property'> = [A | B] && C
.example { property: A C;}
.example { property: B C;}
.example { property: C A;}
.example { property: C B;}
连接符优先级
连接符优先级由高到低为:[] > 并列 > && > || > |
。所以,下面两行是等价的。
a b | c || d && e f
[ a b ] | [ c || [ d && [ e f ]]]
累乘器
下面介绍的 8 种修饰符可以接在类型、关键字或分组([]
)后面,表示其所修饰的类型出现的次数。
**星号 ***
星号(*
)表明其之前的类型、关键字或组出现零次或多次。
在下面的例子中,第二个成分值与一个逗号一起放在了中括号里。放置其后的星号意味着,A 必须出现,但我们也能随我们想地使用 B 任意次,每个成分值以逗号分隔。
<'property'> = A [, <B> ]*
.example { property: A;}
.example { property: A, B;}
.example { property: A, B, B;}
加号 +
加号(+
)表明其之前的类型、关键字或组出现一次或多次。
在下面的例子中,放置于成分值之后的加号意味着该值必须被使用超过一次,无需逗号分割。
<'property'> = A+
.example { property: A;}
.example { property: A A;}
.example { property: A A A;}
问号 ?
表明其之前的类型、关键字或组出现零次或一次。
在下面的例子中,第二个成分值与一个逗号一起放在了中括号里。放置其后的问号意味着,A 必须出现,但我们也可使用 A 和 B,以逗号分隔。
<'property'> = A [, <B> ]+
.example { property: A;}
.example { property: A, B;}
{A}
大括号({A}
)中包含一个数字表明其之前的类型、关键字或组出现 A 次。
下面例子中,B 必须出现两次才合法。
<'property'> = B{2}
.example { property: B B;}
{A, B} 及 {A,}
大括号({A, B}
)中包含两个数字表明其之前的类型、关键字或组出现至少 A 次,至多 B 次。其中 B 可以省略,省略后的大括号({A,}
)表示至少出现 A 次。
下面例子中,B 出现 2 次或 3 次都是合法的,次数更多或更少都是不合法的。
<'property'> = B{2, 3}
.example { property: B B;}
.example { property: B B B;}
下面例子中,B 至少出现 2 次才是合法的。
<'property'> = B{2,}
.example { property: B B}
.example { property: B B B}
.example { property: B B B B}
哈希标识 #
哈希标识(#)表明其之前的类型、关键字或组出现一次或多次,且以逗号分割。
下面例子中,B 可以出现一次或多次,出现多次时以逗号分割。
<'property'> = B#
.example { property: B;}
.example { property: B, B;}
.example { property: B, B, B;}
哈希标识后面可以接上面的大括号来指定出现的次数。
例如下面例子,B 必须出现 2 次或 3 次才合法。
<'property'> = B#{2,3}
.example { property: B, B;}
.example { property: B, B, B;}
感叹号 !
感叹号(!
)表明其之前的分组必须出现,且分组要产生至少一个值。即使分组内语法允许所有值都省略,分组也必须产生至少一个值。
下面例子中,分组内的 B 和 C 虽然是通过|
连接,可以同时省略,但是由于分组后面有感叹号,所以 B 和 C 必须出现至少一个。
<'property'> = A [B | C]!
.example { property: A B;}
.example { property: A C;}
.example { property: A B C;}
举个栗子
让我们<'text-shadow'>
当作例子观察一番。这是它在规范里的定义:
<'text-shadow'> = none | [ <length>{2,3} && <color>? ]#
我们可以拆分这些符号:我们可以拆分这些符号:
|
表明我们可以使用关键字none
或者一个组#
表明我们可以使用这个组一次或多次,以逗号分割&&
意味着我们必须包括组中所有值,但顺序可以任意<length>{2,3}
表明我们可以使用 2 或 3 个长度值<color>
后有一个?
,这意味着其可能出现零次或一次。
用简单的一句话描述:指明了 none 或 一个或多个由逗号分离的组,其中包含了二到三个长度值与一个可选的颜色值。长度值与可选的颜色值可以以任意顺序编写。
这意味着我们能够以很多不同的方式来写 text-shadow 属性的值。
// 设置为 none
.example { text-shadow: none; }
// 只写两个长度值,这意味着我们将设置阴影水平与竖直方向的偏移,但不会有模糊半径或者颜色值。
.example { text-shadow: 10px 10px; }
// 如果我们使用了三个长度值,我们将会同时定义阴影的水平与竖直方向的偏移和模糊半径。
.example { text-shadow: 10px 10px 10px; }
// 加入颜色,且颜色可以出现在 2 或 3 个长度值的前面或后面
.example { text-shadow: 10px 10px 10px red; }
.example { text-shadow: red 10px 10px 10px; }
// 也能包含多个文本阴影,写作以逗号分隔的组。
.example {
text-shadow: 10px 10px red,
-20px -20px 5px lime;
}
如果你以写 CSS 为生,了解如何正确地写合法的 CSS 属性值很重要。一旦你了解了不同的值是如何被组合或累乘的,CSS 属性值语法就变得非常容易理解了。然后看 CSS 的规范或书写合法的 CSS 都会变得更容易了。
参考资料
- CSS Values and Units Module Level 3 | W3C
- 读懂 CSS 属性值语法
- 《CSS权威指南(第三版)》的前言部分