图解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-size
min-width
和min-height
映射的是min-block-size
和min-inline-size
max-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逻辑属性会有一定能困惑,因为它并不是固定的,会随着书写模式,阅读方式等变化,这也会造成一定的使用成本和理解成本。但使用多了,就会熟悉这方面的属性。如果你在这方面有更多的经验和建议,欢迎在下面的评论中与我们一起共享。