2.2 学会看懂CSS属性值定义语法

CSS属性值有专门的定义语法,用来表示CSS属性值的合法组成。例如,线性渐变的语法为:

linear-gradient( [ <angle> | to <side-or-corner> ,]? <color-stop-list> )

如果你看得懂上面这一串字符是什么意思,就可以跳过本节内容;如果你看不懂这一串字符,那么一定要静下心来阅读接下来的内容。

在开始介绍具体的语法规则之前,我想先说说我们为什么要学会看懂CSS属性值定义语法(CSS value definition syntax)。就拿线性渐变的语法举例,根据我的观察,几乎无一例外,CSS开发者会写出类似下面这样的CSS线性渐变代码:

background: linear-gradient(to bottom, deepskyblue, deeppink);

上面这句CSS声明有问题吗?从语法和功能上讲是没有任何问题的,线性渐变可以正常渲染,但是在写法上却有瑕疵,“to bottom,”这几个字符是多余的。我们直接使用下面这样的书写方式就可以了:

background: linear-gradient(deepskyblue, deeppink);

如果你看得懂CSS的语法,那么在学习CSS的时候,只要稍微看一看线性渐变的语法,就能很轻松地知道“线性渐变的方向设置是可以省略的”这样一个细节知识,也就能写出更简洁的CSS代码。

这就是看懂CSS属性值定义语法的好处:不仅有助于快速了解CSS新属性,还有助于发现别人注意不到的细节知识,而这些细节知识就是你的竞争力所在,也是从众多CSS开发者中脱颖而出的重要因素之一。

CSS属性值定义语法是专门用来限定CSS属性合法取值的语法,这种语法包含以下3种基本组成元素:

● 关键字;

● 数据类型;

● 符号。

线性渐变的语法就包含上面这3种基本组成元素:

linear-gradient( [ <angle> | to <side-or-corner> ,]? <color-stop-list> )

先讲一下这一语法中的几个关键点:

● to是关键字;

● <angle>、<side-or-corner>和<color-stop-list>是数据类型,如果对这几个数据类型不了解,可以参考我的一篇文章《CSS值类型文档大全》(https://www. zhangxinxu.com/wordpress/2019/11/css-value-type/);

● “[]”“?”“,”是符号。

下面稍微展开介绍一下这3种基本组成元素。

1.关键字

关键字分为通用关键字和全局关键字:

● auto、none、ease等关键字是通用关键字,或者可以称为普通关键字,这些关键字均只被部分CSS属性支持;

● inherit、initial、unset和revert是全局关键字,属于被所有CSS属性支持的特殊关键字,全局关键字在2.3节会介绍,这里不展开。

2.数据类型

数据类型外面有一对尖括号(“<”和“>”)。有些数据类型是CSS规范中专门定义的,它们被称为基本类型,其他数据类型就被称为其他类型。

数据类型相关内容在2.1.2节已经有过专门介绍,这里也不展开。

3.符号

符号是CSS语法中的重点和难点。

CSS语法中的符号分为字面符号、组合符号和数量符号这3类,下面就介绍一下它们对应的含义。

(1)字面符号指的是CSS属性值中原本就支持的合法符号,这些符号在CSS语法中会按照其原本的字面意义呈现。目前字面符号就两个,一个是逗号(,),另一个是斜杠(/)。具体描述如表2-1所示。

表2-1 字面符号描述信息表

(2)组合符号用来表示数个基本元素之间的组合关系。目前共有5个组合符号,其中大多数组合符号的含义一目了然,除了“|”这个组合符号。因为“|”表示互斥,这在编程语言中比较少见,大家可以特别关注一下。具体描述如表2-2所示(表中从上往下组合符号的优先级越来越高)。

表2-2 组合符号描述信息表

(3)数量符号用来描述一个元素可以出现多少次,数量符号不能叠加出现,并且优先级高于组合符号。目前共有6个数量符号,大多数的数量符号的含义和在正则表达式中的含义是一样的,具体描述如表2-3所示。

表2-3 数量符号描述信息表

有了表2-1~表2-3,理解CSS属性值定义语法就变得很容易了。

例如,线性渐变的语法解析如图2-1的标注说明所示。

图2-1 线性渐变语法中的符号标注

下面先讲一下语法中的元素。

● 逗号(,)是字面符号,没有什么特殊的含义,只表示这里需要有一个逗号字符。要注意下面这种写法是不合法的:

/* 不合法,缺少逗号 */
background: linear-gradient(0deg blue, pink);

● 空格( )是组合符号,表示并列,同时保证各部分按顺序出现。因此,如果把<color-stop-list>数据类型放在前面,那就不合法了:

/* 不合法,顺序不对 */
background: linear-gradient(blue, pink, 0deg);

● “互斥”组合符(|)表示角度和方位只能出现一个,两者不能同时出现。因此,下面的写法是不合法的:

/* 不合法,角度和方位不能同时出现 */
background: linear-gradient(0deg to top, blue, pink);

● 方括号([])用来分组,方便指定数量。

● 问号(?)是数量符号,表示方括号框起来的元素可以出现零次或者一次,零次的意思就是这个元素可以不出现。因此,下面的写法是不合法的:

/* 不合法,角度或方位最多出现一次 */
background: linear-gradient(0deg, 0deg, blue, pink);

综上分析,下面这些线性渐变语法都是合法的:

linear-gradient( <color-stop-list> )
linear-gradient( <angle>, <color-stop-list> )
linear-gradient( to <side-or-corner>, <color-stop-list> )

接下来只要弄清楚<angle>、<side-or-corner>和<color-stop-list>这几个数据类型的含义和语法,我们就可以理解线性渐变的语法了。相关内容在后面对应的章节会有非常详细且深入的介绍,这里不做展开。

最后再通过一些案例介绍一下线性渐变语法之外的符号。

4.字面符号斜杠(/)的详细介绍

在CSS这门语言中,凡是出现斜杠(/)的地方,斜杠前后的数据类型一定是相同或者部分相同的,否则整个语句就是非法的。很多开发者总是记不住包含斜杠的CSS缩写语法,那么只要记住这个规则就可以了。

例如,background属性值中需要使用斜杠分隔的两个属性一定是background- position和background-size,因为只有这两个属性的值的类型相似,且都可以使用百分比值表示。这样就会出现很有趣的现象,像下面这样的CSS语句是合法的:

/* 合法 */
background: 0 / 0;

但是下面这个看上去合法的缩写却是非法的:

/* 不合法 */
background: #eee url(../Images/1.png) no-repeat / contain;

因为斜杠前面的值no-repeat属于background-repeat属性,斜杠后面的值contain属于background-size属性,而background-repeat的属性值的类型绝不可能和background- size的属性值的类型一致,这不符合斜杠前后数据类型至少部分相同的要求,所以这条语句是非法的。

记住,background缩写语法中斜杠前面只能是background-position的属性值,上面的CSS语句要想合法,可以把background-position属性的初始值0 0写上:

/* 合法 */
background: #eee url(../Images/1.png) no-repeat 0 0 / contain;

又如,font属性的斜杠前后一定是font-size的属性值和line-height的属性值,因为两者都可以使用px长度单位值。例如:

.example {
    font: 16px / 1.5 sans-serif;
}

斜杠这个符号除了出现在部分CSS的缩写语法中,还会出现在一些CSS函数中用来表示分隔,例如rgba()函数的语法:

<rgba()> = rgba( <percentage>{3} [ / <alpha-value> ]? ) | rgba( <number>{3} [ / 
<alpha-value> ]? ) | rgba( <percentage>#{3} , <alpha-value>? ) | rgba( <number>#{3} , 
<alpha-value>? )

从上面的语法我们可以看出rgba()函数也是支持斜杠的,因此,下面的属性值都是合法的:

/* 合法 */
rgba(100% 0% 0% / .5);
rgba(255 0 0 / .5);

类似“rgba()函数支持斜杠语法”这样的细节知识,如果不看语法是绝对不知道的。还是那句话,要想CSS学得好,CSS属性值定义语法必须要学好,其重要性不亚于JavaScript中的正则表达式。

5.其他符号介绍

下面介绍其他符号。

(1)“或”组合符(||)。“或”组合符(||)在CSS语法中很常见,例如border属性的语法:

border: <line-width> || <line-style> || <color>

这一语法表示border属性的3个值的顺序是随机的,组合也是随机的。

(2)叹号(!)。叹号(!)在image()函数中出现过:

<image()> = image( <image-tags>? [ <image-src>? , <color>? ]! )

这一语法表示<image-src>数据类型和<color>数据类型至少出现一个,当然,两者也可以同时出现。

(3)其他数量符号和“与”组合符(&&)。到目前为止,还有井号(#)、星号(*)、花括号({A,B})等数量符号和“与”组合符(&&)没有介绍,这里用box-shadow属性的语法加以说明,如下:

box-shadow: none | <shadow>#

等同于:

box-shadow: none | [ <shadow>, ] *

或可写成:

box-shadow: none | [ inset? && <length>{2,4} && <color>? ]#

其中出现的“与”组合符(&&),表明inset关键字、<length>数据类型和<color>数据类型的顺序是可以随意排列的,所以下面这几种写法都是合法的:

box-shadow: 2px 2px inset #000;
box-shadow: inset #000 2px 2px;
box-shadow: #000 2px 2px inset;

其中<length>{2,4}表示可以使用2~4个<length>数据类型,很好理解。

下面要抛出一个很有意思的问题了:既然[ <shadow>, ]*等同于<shadow>#,那这个#符号是不是一个多余的设计呢?

这不是多余的设计,虽然多了一个#符号就多了一点学习成本,但是语法更简洁了,更重要的是#符号有一个和其他数量符号不一样的特性,那就是#符号可以在后面指定数量范围。例如rgba()函数的语法中有下面这一段内容:

rgba( <number>#{3} , <alpha-value>? )