图解CSS:CSS逻辑属性
自Web诞生以来,我们一直习惯于物理CSS属性,比如我们都知道使用margin-top、margin-right、margin-bottom和margin-bottom来设置元素的外边距,但随着书写模式特性的出现,这些物理特性,比如上、右、下和左的概念已经失去了它们的意义。特别是随着越来越多的Web开发人员要处理国际化业务,那么以前的物理特性已经无法满足业务的需求。换句话说,为了具有不同编写模式的多种语言设计页面时,开发人员必须跨多个元素分别调整这些物理属性,这也成了Web开发者的噩梦。幸运的是,CSS的逻辑属性的出现,可以让开发者根据书写模式来维护布局的完整性。即,根据内容的语义顺序进行动态更新。今天这篇文章,我们将和大家一起来探讨CSS的逻辑属性。
什么是逻辑属性
2017年5月18日,W3C的 CSS工作组(CSS Working Group) 发布了 CSS逻辑属性和值(CSS Logical Properties and Values Level 1) 的首份工作草案(First Public Working Draft)。不同的书写模式(writing mode)中,可以抽取出共性的抽象概念(如开始位置,或行),这些逻辑抽象概念需要在不同书写模式下映射到左或右、上或下等物理的概念上。一些CSS布局可能依赖这些共性的逻辑概念。该 CSS 模块给出了用于通过逻辑方式(而不是基于物理坐标、书写方向和维映射等)控制布局的逻辑属性和取值(logical properties and values)。这个模块来源于CSS21中关于逻辑属性和值的特性。
在过去(CSS逻辑属性还未到来之前),在CSS中来描述元素盒模型、位置等特性都是采用的物理属性(比如元素的尺寸,方向等),比如我们熟悉的元素位置会映射到top、right、bottom和left。在盒模型中都以物理属性为中心,不管是margin、padding、border还是position、float等。
除了top、right、bottom和left之外的width和height都是物理属性之一,比如我们描述一个盒子的大小,就是用width和height为描述的。但是这些属性和书写模式有着很大的关系,因为书写模式可以直接改变其方向。假设你的网站上有一些从右到左(RTL)的内容,那么左边可能是物理上的右边,因此你要是设置了margin-left: 100px,你可能希望将其替换为margin-right: 100px。但是,如果同时使用从左到右(LTR)和从右到左(RTL)混合,则需要根据不同的CSS属性来设置左或右。如果你考虑垂直写作模式,也会遇到类似的问题,内容可能是物理上的顶部(top)或底部(bottom)。
但在逻辑属性中却不一样,在逻辑属性中没有方向性的概念,只有开始(start)和结束(end)、块(block)和内联(inline)的概念。比如说,在从左到右(LTR)中,start是left,但在从右到左(RTL),它是right。也就是说,逻辑属性更易于适应不同的书写模式。
书写模式
虽然说,在Web排版的时候,我们习惯了从左到右(LTR)的排版,但当你的业务要面对多语言的场景时,你会发现除了熟悉的从左到右的排版之外,你还需要处理从右到左(RTL),从上到下等排版。
在Web开发中,HTML的dir属性取值为ltr可以实现从左到右排版,rtl可以实现从右到左排版。在CSS中的direction属性和dir属性类似,可以定义内联内容在屏幕上的流动方式(排版方式),即ltr是从左到右排版,rtl是从右到左排版。除此之外,CSS中的writing-mode属性除了可以义定内联内容在屏幕上的排版方式之外,还可以定义块内容在屏幕上的排版方式。该属性可以取值:
horizontal-tb:定义了内容从左到右水平流动,从上到下垂直流动。下一条水平线位于上一条线下方vertical-rl:定义了内容从上到下垂直流动,从右到左水平流动。下一条垂直线位于上一行的左侧vertical-lr:定义了内容从上到下垂直流动,从左到右水平流动。下一条垂直线位于上一行的右侧sideways-rl:定义了内容从上到下垂直流动,所有字形,甚至是垂直脚本中的字形,都设置在右侧sideways-lr:内容从上到下垂直流动,所有字形,甚至是垂直脚本中的字形,都设置在左侧
就拿上面示例上的圆角来说,如果采用以前我们熟悉的物理属性来描述border-radius的值,那么在不同书写模式下,它的值将要根据不同的模式进行调整:
.card__heading {
border-radius: 6px 6px 0 0;
}
[data-lang="Japanese"] .card__heading{
border-radius: 0 6px 6px 0;
}
[data-lang="Mongolian"] .card__heading{
border-radius: 6px 0 0 6px
}
这就是物理属性在多语言布局中带来的局限性,幸运的是,逻辑属性将彻底解决这一切烦恼。
逻辑属性与物理属性及逻辑值与物理值
CSS中布满了物理位置的关键词,比如我们熟悉的top、right、bottom和left。在使用position非static值对元素盒子定位时,就要使用这些物理位置来描述元素盒子的偏移量。
另一个用到物理关键词的地方是使用text-align控制文本对齐方式,比如取值为right时文本会右对齐,这也是CSS中的物理属性。当我们为项目增加外边距(margin)、内边距(padding)和边框(border)时会使用像margin-left、padding-left这样的物理属性。
把这些关键词称为物理属性,是因为它们与你看到屏幕紧密相关,左永远是左,不管文本流动的方向如何。
在开发有多种语言的网站时,如果其中包含了从右侧而不是从左侧开始书写的文字,物理属性就会成为一个问题。浏览器很擅长处理文本方向,不需要真的在一种 rtl (从右到左)的语言下开发,我们也可以一窥究竟。下面的例子里有两个段落,一个段落没有设置 text-align 属性,另一个段落的 text-align 设置为 left。在 html 元素上添加 dir="rtl" 声明,就会把书写模式从默认的 ltr (从左到右)的英语切换为 rtl (从右到左)的语言。我们可以看到,第一段仍然是从左到右显示,因为 text-align 的值为 left,但第二段把文字的流动方向切换成了从右到左。

这只是在使用物理属性和值时引起问题的一个非常简单的例子,它们阻止浏览器切换书写模式,因为这些物理属性和值已经假设文字的流动方向一定是从左到右、从上到下的。
逻辑属性和值不会预设文字方向,这也是为什么在网格布局中要实现对齐到容器的开始位置时使用 start 关键字的原因。对我来说,因为我使用英语工作,所以 start 就是左侧,不过它并不总是代表左侧,并不能根据 start 这个词推断出物理位置。
为什么需要逻辑属性
传统CSS根据屏幕的物理尺寸来定义元素的尺寸、位置,这些值都是物理尺寸。因此,我们使用CSS将元素(盒子)描述为具有宽度(width)和高度(height),用top、right、bottom和left来描述元素的位置。而CSS逻辑属性和值定义了这些物理值到它们的逻辑或流相关的映射,例如用开始和结速来描述左和右,顶部和底部。
为什么需要这些映射呢?我们来看一个简单地示例:
<h2>City Lights in New York</h2>
<h2>أضواء المدينة في نيويورك</h2>
如果仅仅按物理属来给标题定义一个左边框以及内容距左边框内距,往往会这样来描述:
h2 {
border-left: 6px double currentColor;
padding-left: 5vh;
}

对阅读模式来说,上面的样式适合于英文排版,但对于阿拉伯语来说,上面的样式设置就不太适合,因为阿拉伯语阅读方式是从右向左,也就是左边框变成了右边框,左内距变成了右内距。在逻辑属性还未出现之前,往往我们需要针对阿拉伯语单独做样式处理:
h2:nth-child(2) {
border-right: 6px double currentColor;
border-left: none;
padding-right: 5vh;
padding-left: 0;
}

如果我们用逻辑属性来描述的话,事情就会变得容易地多:
h2 {
border-inline-start: 6px double currentColor;
padding-inline-start: 5vh;
}

可能上面的示例你可能会感到陌生,那我们来看另一个示例。
在现代Web的布局中,我想你应该已经使用过了Flexbox布局,或者说已经接触过了Grid的布局。就比如说一个设置了宽度(width)的网格容器(元素上显式设置display为grid或inline-grid),在让网格项目对齐时会用到align-self和justify-self属性,而且它们的值会是start或end。这些属性会根据流方式产生不同的效果,比如justify-self: start会让网格项目在内联维度的开始位置,而align-self: start会让网格项目在块维度的开始位置。
如果我们修改writing-mode的值,你可以看到效果如下:

如果将物理属性width换成inline-size,把height换成block-size,再改变书写模式时,比如writing-mode的值为vertical-rl(或vertical-lr)时,inline-size(内联维度)会按垂直方向运行,而block-size(块维度)会按水平方向运行。这个时候justify-self取值start(或end)时,位置也会根据维度相应变化:

具体的效果,你可以尝试调整下拉选择框的值:
逻辑属性中的重要概念
从上面的内容中我们可以了解到,CSS的书写模式对逻辑属性最终呈现给用户的效果有着决定必的作用。除此之外,还要深入的了解几个和逻辑属性有关系的概念,只有了解了这几个概念,才能更好的运用好CSS的逻辑属性。
流
流在CSS中是一个非常重要的概念,我们在《视觉格式化模型》和《聊聊CSS中的层叠相关概念》都有聊过流。回到Web中来,在Web中所说的流主要指的是文档流和文本流。比如说,在HTML中,我们的元素可以分为块元素(如div),行内元素(如span)和可替换元素(如img)。如果未使用CSS做任何样式上的处理,那么块元素会按从上往下排列:

在CSS中,我们可以通过display属性让block元素和inline元素之间转换:

对于文本同样有流的概念,比如说英文,一般是从左到右,阿拉伯文是从右到左,而日文(古代的中文)从上到下,从右到左:

不管是文档流还是文本流,它们都具有相应的物理特性,比如从左到右,从右到左,从上到下,从下到上。即,它们都没有离开top、right、bottom和left方向。
随着Flexbox的到来,具体的方向性没有那么重要了,比如在Flexbox中,不再关注方向,而是更关注主轴和侧轴:

特别是进入到Grid的时代,方向性更不重要了。因为在Grid的时代变成了:

你也可以发现了,在Flexbox和Grid中,不再有具体的方向性的概念,有的只是开始(start)和结束(end)。如果你用过了Flexbox布局和Grid布局,我想你也就用过了前面提到的justify-self、align-self等属性,他们的的值不再有left、right,而是start和end。
另外,这些都会随着书写模式(writing-mode)会有所变动,比如:

换句话说,在CSS中可以使用流相对值来替代相应的物理值。
块(block)和内联(inline)维度
处理流相关属性和值的一相关键概念是**块(block)和内联(inline)**两个维度。正如上面所看到的,像Flexbox和Grid布局方法在对齐项目时使用了块和内联维度的概念,而不是top、right、bottom和left。
内联(inline)
内联维度是在使用的书写模式中运行的文本行(文本流)所在的维度。即,对应于文本流(阅读方式)的轴线。例如,英文是从左到右的文本流(或阿拉伯文从右到左),因此内联轴是水平的;对于日文,它的阅读方式是自上而下,因此内联轴是垂直的。
块(block)
块维度是另一个维度,以及块(如段落)相继显示的方向。在英语和阿拉伯语中,这些是垂直的,而在任何垂直书写模式中,这些是水平的。

也就是说,如果用逻辑属性而不是物理属性来思考,就不能使用从左到右,从上到下的方式观察世界,我们需要一个新的参考点,也就这里所说的内联轴和块轴。理解它们是非常重要的,除了能帮助我们更好的理解CSS逻辑属性之外,对于理解CSSS中Flexbox,Grid布局中的术语以及对齐方式也特别的有用。
我们可以换过一种方式来理解:
- 块轴:主要定义网站文档(元素块)流,CSS的书写模式
writing-mode会影响块轴的方向 - 内联轴:主要定义网站的文本流方向,也就是文本的阅读方式,CSS的
direction或HTML的dir会影响内联轴的方向

开始(start)和结束(end)
在前面的内容中,我们多次提到了开始和结束。一但你知道了文本的方向,就能很好的理解开始和结束。
开始(start)
这对应于文本的方向,并反映了文本的侧边,你将从哪里开始阅读。对于英文,开始对应于左。对于阿位伯文来说,对应于右。
结束(end)
这也对应于文本的方向,并反映了文本的侧边,你将在哪里结束阅读。对于英文,结束对应于右。对于阿拉伯文来说,对应于左。
逻辑维度 vs. 物理维度
将前面的内联轴、块轴、开始和结束结合起来可以构建CSS逻辑属性中的流相对值。即 block-start、block-end、inline-start 和 inline-end。这几个属性也被称为逻辑维度,其实就是用来指定在对应轴上的开始和结束位置。对应的就是我们熟悉的top、right、bottom和left几个物理方向。
换句话说,在CSS逻辑中,使用流相对值来代替相应的物理值。正如前面所述,流相对值(逻辑维度)和CSS的书写模式writing-mode或阅读方式direction有关。
接下来,我们通过几种典型的语言为例,来向大家阐述逻辑维度和物理维度的映射关系。
首先来看英文,英文的阅读方式一般是从左往右(即dirction: ltr或writing-mode:horizontal-tb),这种模式常称为 LTR(Left-To-Right)。它的内联轴是水平的,块轴是垂直的,相应的逻辑维度和物理维度映射关系如下:
| 逻辑维度 | 物理维度 |
|---|---|
inline-start |
left |
inline-end |
right |
block-start |
top |
block-end |
bottom |

接着来看阿拉伯文,它的阅读方式是从右往左(即direction: rtl或writing-mode:horizontal-tb),这种模式常称为 RTL(Right-To-Left)。它的内联轴是水平的,块轴是垂直的,相应的逻辑维度和物理维度映射关系如下:
| 逻辑维度 | 物理维度 |
|---|---|
inline-start |
right |
inline-end |
left |
block-start |
top |
block-end |
bottom |

再来看日文,竖排(有点类似中国古代的汉字书写模式),对应的writing-mode: vertical-rl。它的内联轴是垂直的,块轴是水平的,相应的逻辑维度和物理维度映射关系如下:
| 逻辑维度 | 物理维度 |
|---|---|
inline-start |
top |
inline-end |
bottom |
block-start |
right |
block-end |
left |

最后再来看蒙文,也是竖排,和日文不同的是writing-mode: vertical-lr。它的内联轴是垂直的,块轴是水平的,相应的逻辑维度和物理维度映射关系如下:
| 逻辑维度 | 物理维度 |
|---|---|
inline-start |
top |
inline-end |
bottom |
block-start |
left |
block-end |
right |

逻辑属性带来的变化
了解了上面介绍的这些概念,就能更好的帮助我们理解CSS逻辑属性。
CSS的逻辑属性通常不使用位置命名。正如《Web中向左向右》一文中所提到的,当文本以另一种语言呈现时,位置可能变得毫无意义。例如,使用padding-left来设置文本距容器左侧边缘距离(增加空白空间)比较适合于英文文本,但却不适合阿拉伯文(看上去怪怪的)。这主要是因为阿拉伯文的阅读方式是从右往左(LTR),也就是说,换到阿拉伯文就需要设置padding-right来增加内距,才更符合视觉上的效果。如果我们将padding-left这样的物理属必换成padding-inline-start,我们就无需担心文本是英文还是阿拉伯文了。
正如上面示例所言,我们以前熟悉的CSS盒模型相关属性,比如margin、padding、border、width和height;元素定位属性,比如top、right、bottom和left;浮动方向、文本对齐方向等都可以映射到对应的CSS逻辑属性上。

更详细的映射表如下图所示:
接下来,我们就来看看CSS逻辑属性的基本使用和给我们带来的变化。
特别声明:CSS的逻辑属性和
direction、writing-mode、text-orientation以及HTML的dir属性有着直接关系,因为这几个属性取值不同会改变轴的方向(内联轴和块轴);从而会影响start和end。
流盒模型属性
这里所说的流盒模型属性是相对于物理盒模型属性来说的。比如下图所示:

物理盒模型中的属性都有匹配逻辑属性映射关系。而且也可以按组划分,比如padding-*、margin-*、border-*等。而且这些映射关系(或者说流盒模型属性)并不是固定不变的,它们的结果和书写模式writing-mode、direction和text-orientation取值有关。
另外,流相关属性的指定值与对应物理属性的指定值不同,但流相关属性和物理属性共享计算值。换句话说,如果同一个元素同时声明了流相关的属性和物理相关的属性,最终计算值将会由CSS级联规则来决定。我们来看一个简单的示例:
.box {
border-inline-start: 5px solid #f36;
border-left: 5px solid #f90;
border-inline-end: 5px solid #90f;
}
尝试着调整writing-mode和dir的值,其结果如下:
当writing-mode:horizontal-tb和direction:ltr,.box的左边框颜色为#f90,右边框颜色为#90f。在这种模式下,border-inline-start和border-left共享一个计算值,而且border-left的声明在border-inline-start之后,因此最终运用于.box的是border-left的值(即5px solid #f90):

当writing-mode:horizontal-tb和direction:rtl,.box的左边框颜色为#90f,右边框的颜色为#f36。在这种模式下,border-left和border-inline-end共享一个计算值,而且border-inline-end的声明在border-left之后,因此最终运用于.box的是border-inline-end的值(即5px solid #90f):

当writing-mode: vertical-lr(或vertical-rl)和direction: ltr,不会像前面两个场景,共享同一个值,这个时候呈现给大家的效果如下:

当writing-mode: vertical-lr(或vertical-rl)和direction: rtl,也不会共享同一个值,这个时候呈现给大家的效果如下:

接下来,我们来按照盒模型的属性划分来看看CSS逻辑属性给盒模型带来的变化。
逻辑尺寸
从《元素尺寸的设置》中可以得知,物理属性width、height、min-width、min-height、max-width和max-height可以用来设置元素盒子尺寸大小。它们在CSS逻辑属性中都有对应的映射关系。比如:
width和height映射的是block-size和inline-sizemin-width和min-height映射的是min-block-size和min-inline-sizemax-width和max-height映射的是max-block-size和max-inline-size
但具体的对应关系取决于writing-mode属性的值。我们来看一个简单的示例,比如:
.box {
inline-size: 50vh;
block-size: 30vh;
}
同样拿不同的语言为例,比如英文(writing-mode: horizontal-tb和direction: ltr)、阿拉伯文(writing-mode: horizontal-tb和direction: rtl)、日文(writing-mode: vertical-rl)和蒙文(writing-mode: vertical-lr)。具体的效果如下:

min-width、min-height、max-width和max-height在不同writing-mode和direction模式下表现行为和width、height相似。
| 物理属性 | 逻辑属性(horizontal-tb) |
逻辑属性(vertical-lr) |
逻辑属性(vertical-rl) |
|---|---|---|---|
width |
inline-size |
block-size |
block-size |
height |
block-size |
inline-size |
inline-size |
min-width |
min-inline-size |
min-block-size |
min-block-size |
min-height |
min-block-size |
min-inline-size |
min-inline-size |
max-width |
max-inline-size |
max-block-size |
max-block-size |
max-height |
max-block-size |
max-inline-size |
max-inline-size |
在CSS中,除了上面提到的属性可以显式设置元素盒子大小之外,还可以使用resize属性来调整元素盒子大小。其物理值中的horizontal和vertical也有相应的逻辑关键字值。
- 使用
resize:inline允许在内联维度调整元素大小 - 使用
resize:block允许在块维度调整元素大小
逻辑边框
CSS盒模型中的边框(border)属性在逻辑属性中也有相应的映射属性。这些逻辑边框属性和writing-mode,direction和text-orientation有关。
.box {
border-inline-start: 6px solid #9c27b0;
border-inline-end: 8px solid #ff9800;
border-block-start: 10px solid #2196f3;
border-block-end: 12px solid #8bc34a;
}
同样的,在不同的语言中来向大家演示:

物理边框属性和逻辑边框属性在writing-mode和direction下对应的映射关系如下所示:

CSS的物理属性border可以分拆为border-top-width、border-right-width、border-bottom-width、border-top-style、border-right-style、border-bottom-style、border-left-style、border-top-color、border-right-color、border-left-color等。同样的,它们分别有对应的逻辑属性border-block-start-width、border-block-end-width、border-inline-start-width、border-inline-end-width、border-block-start-style、border-block-end-style、border-inline-start-style、border-inline-end-style、bordere-block-start-color、border-block-end-color、border-inline-start-color和border-inline-end-color。
其中border-width、border-style和border-color对应的逻辑属性有border-block-width、border-inline-width、border-block-style、border-inline-style、border-block-color和border-inline-color。
熟悉CSS的同学都知道,CSS的border属性是border-top、border-right、border-bottom和border-left的简写,CSS逻辑属性是border-block-start、block-block-end、block-inline-start、block-inline-end可以简写为border-block和border-inline。其中border-block和border-inline对应的是:
border-block: border-block-start border-block-end
border-inline: border-inline-start border-inline-end
逻辑内距
CSS的逻辑内距和逻辑边框有点类似,和物理padding能找到相应映射的属性。同样的逻辑内距和writing-mode、direction和text-orientation有关。比如:
.box {
padding-inline-start: 10px;
padding-inline-end: 20px;
padding-block-start: 30px;
padding-block-end: 40px;
}
同样在不同的语言的渲染效果如下:

物理内距和逻辑内距对应的映射关系如下:

逻辑内距也可以简写:
padding-block: padding-block-start padding-block-end
padding-inline: padding-inline-start padding-inline-end
逻辑外距
逻辑外距和逻辑内距类似,它和物理外距对应的映射关系如下:

逻辑圆角
在物理属性中,border-radius可以用来设置元素盒子的圆角,其拆分出来由border-top-left-radius、border-top-right-radius、border-bottom-left-radius和border-bottom-right-radius四个属性。而CSS的逻辑圆角属性由border-start-start-radius、border-start-end-radius、border-end-start-radius、border-end-end-radius组成。同样的,CSS逻辑圆角属性也受writing-mode、direction、text-orientation属性的影响。
比如下面这个示例:
请用Firefox浏览器查看Demo。

物理圆角和逻辑圆角映射关系如下:

逻辑偏移
CSS的position取值为非static时,可以通过top、right、bottom和left让定位元素偏移,在CSS的逻辑属性中同样也有相应的映射属性:inset-block-start、inset-block-end、inset-inline-start和inset-inline-end。它们还可以简写为inset-block、inset-inline和inset。
同样的,逻辑偏移也受writing-mode、direction和text-orientation属性的影响。比如:
.item:nth-child(1) {
inset-inline-start: 20px;
inset-block-start: 30px;
}
.item:nth-child(2) {
inset-inline-end: 40px;
inset-block-end: 60px;
}

物理属性和逻辑属性对应的关系如下:

小结
如果在构建Web页面或应用时,面对的仅是单一语言,那么使用CSS物理属性并无大碍,而且也不会影响整个Web应用的布局。而且使用CSS逻辑属性来替代物理属性也可以实现同等效果。如果你构建的Web应用是要处理多语言,那么物理属性带来的局限性就非常的明显,而且也会造成阅读上的不便,这个时候CSS逻辑属性就能起到作用就非常的大。
CSS逻辑属性和物理属性都有着相应的映射关系,比如下图所示:

CSS逻辑属性和物理属性之间最大的差异是,CSS逻辑属性受着writing-mode、direction和text-orientation属性的影响,并且用start和end来替代原有的物理方向。
对于初学者而言,使用CSS逻辑属性会有一定能困惑,因为它并不是固定的,会随着书写模式,阅读方式等变化,这也会造成一定的使用成本和理解成本。但使用多了,就会熟悉这方面的属性。如果你在这方面有更多的经验和建议,欢迎在下面的评论中与我们一起共享。