网格、逻辑值和书写模式

CSS 网格布局最重要的特性之一,是在规范中内置了对不同书写模式的支持。在本指南中,我们将了解 CSS 网格布局的这一特性以及其他现代布局方法,并在此过程中学习一些关于书写模式以及逻辑与物理属性的知识。

逻辑与物理属性和值

CSS 中充满了物理定位属性和关键字——leftrighttopbottom。在下面的代码片段中,我们使用绝对定位来定位一个项目,并使用物理 inset 属性作为偏移值来推动该项目。该项目被放置在距离容器顶部 20 像素、距离左侧 30 像素的位置。

css
.container {
  position: relative;
}
.item {
  position: absolute;
  top: 20px;
  left: 30px;
}
html
<div class="container">
  <div class="item">Item</div>
</div>

这个例子使用了 leftright 属性;这只是 CSS 中众多物理属性中的两个。我们还可以使用物理属性添加外边距、内边距和边框,例如 margin-leftpadding-left。你可能还会看到物理关键字的使用,例如使用 text-align: right 将文本向右对齐。

我们称这些关键字和属性为物理的,因为它们与你正在看的屏幕有关。无论你的文本方向如何,左边永远是左边。

物理属性的问题

在开发一个需要支持多种语言(包括文本从右向左或从上到下流动的语言)的网站时,物理属性可能会导致问题。浏览器被设计为能够正确显示内容,而无论语言如何。一些 CSS 特性可能会覆盖浏览器的默认设置,导致内容显示效果不佳。

在这个例子中,direction 属性被设置为 rtl,这将英语文档的默认书写模式从 ltr 切换了。我们有两个段落。由于在祖先元素(<body>)上设置了 direction 值,这两个段落都应该从右向左流动。第一个段落将 text-align 设置为 left,因此它与其容器的左侧对齐。第二个段落与右侧对齐,并从右向左流动。

css
body {
  direction: rtl;
}
.left {
  text-align: left;
}

这是一个基本演示,说明了在 CSS 中使用物理值和属性可能出现的问题。如果我们使用物理属性和关键字来编写 CSS,我们就告诉了浏览器我们对文本流动方式的假设,并阻止了它处理其他书写模式。

逻辑属性和值

逻辑属性和值不假定文本方向。这就是为什么我们在 CSS 网格布局中使用关键字 start 来将某个东西与容器的起始位置对齐。在处理英语内容时,start 将在左侧,然而,它不一定非得如此。start 这个词没有暗示任何物理位置,这使得网站在使用从右到左的语言(如阿拉伯语)时,可以将内容从右侧开始。

块级与行内

当使用逻辑属性而不是物理属性时,我们不再将世界看作是从左到右、从上到下的。我们有一个不同的参考点。这就是理解在网格对齐指南中介绍的块级行内轴变得非常有用的地方。如果你从块级和行内的角度来思考布局,CSS 网格布局的工作方式就非常有道理了。

An image showing the default direction of the block and inline axes.

CSS 书写模式

CSS 书写模式模块规定了书写模式在 CSS 中的工作方式。这些功能不仅用于支持与英语书写模式不同的语言,还可以用于创意目的。本节中的示例利用 writing-mode 属性来更改应用于我们网格的书写模式,并在此过程中演示了逻辑值的工作原理。

writing-mode

书写模式不仅仅是从左到右和从右到左的文本,writing-mode 属性可以帮助我们显示以其他方向排列的文本。writing-mode 属性可以有以下值:

  • horizontal-tb
  • vertical-rl
  • vertical-lr
  • sideways-rl
  • sideways-lr

horizontal-tb,代表“水平,从上到下”,是 Web 文本的默认值。它就是你正在阅读本指南的方向。其他值会改变文本在文档中的流动方式,以匹配世界各地不同的书写模式。

举个例子,下面我们有两个段落。第一个使用默认的 horizontal-tb 值,第二个使用 vertical-rl。在第二种书写模式中,文本仍然是从左到右书写,但文本的方向是垂直的——行内文本现在是沿着页面向下,从上到下排列。

html
<div class="wrapper">
  <p class="horizontal-tb">
    I have writing mode set to the default <code>horizontal-tb</code>
  </p>
  <p class="vertical-rl">I have writing mode set to <code>vertical-rl</code></p>
</div>
css
.horizontal-tb {
  writing-mode: horizontal-tb;
}
.vertical-rl {
  writing-mode: vertical-rl;
}

网格布局中的书写模式

将这一点应用到网格布局示例中,我们可以看到改变书写模式意味着改变我们对块级轴和行内轴位置的看法。

默认书写模式

在这个例子中,网格有三列和两行轨道。这意味着有三个轨道沿着块级轴向下延伸。在默认书写模式下,网格会自动从左上角开始放置项目,向右移动,填满行内轴上的三个单元格。然后它会移动到下一行,创建一个新的行轨道,并填入更多的项目。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(2, 100px);
  gap: 10px;
}
html
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
  <div class="item5">Item 5</div>
</div>

设置书写模式

如果在上一个例子中,我们给网格容器添加 writing-mode: vertical-lr,我们可以看到块级轴和行内轴现在运行在不同的方向。块级轴或轴现在从左到右横跨页面,而行内轴则沿着页面向下,从上到下创建行。

css
.wrapper {
  writing-mode: vertical-lr;
}

对齐的逻辑值

随着块级轴和行内轴能够改变方向,对齐属性的逻辑值开始变得更有意义了。

在这个例子中,我们使用对齐(align-selfjustify-self 属性)来对齐一个设置为 writing-mode: vertical-lr 的网格内部的项目。startend 属性的工作方式与它们在默认书写模式中的工作方式完全相同,并且保持了逻辑性,这是使用 left 和 right、top 和 bottom 来对齐项目所做不到的。一旦我们像这样将网格翻转到侧面,就会发生这种情况。

css
.wrapper {
  writing-mode: vertical-lr;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 100px);
  gap: 10px;
}

.item1 {
  grid-column: 1 / 4;
  align-self: start;
}

.item2 {
  grid-column: 1 / 3;
  grid-row: 2 / 4;
  align-self: start;
}

.item3 {
  grid-column: 3;
  grid-row: 2 / 4;
  align-self: end;
  justify-self: end;
}
html
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
</div>

如果你想看看这些是如何工作的,包括从右到左以及从上到下的书写模式,可以将 vertical-lr 切换为 vertical-rl,这是一种从右到左运行的垂直书写模式。

自动放置与书写模式

正如我们在前面的例子中所看到的,书写模式可以改变项目在网格上放置的视觉方向。默认情况下,项目会沿着行内轴放置自己,并在块级方向上添加新的行。我们现在已经看到,行内轴并不总是从左到右,块级轴也不总是从上到下。

基于网格线的放置与书写模式

当按行号放置项目时,要记住的关键是,无论你处于哪种书写模式,第 1 行都是起始线,第 -1 行都是结束线。

从左到右文本的基于网格线的放置

在这个例子中,我们有一个按默认 ltr 方向布局的网格,其中有三个项目使用基于网格线的放置方式进行定位。

  • 项目 1 从列网格线 1 开始,跨越一个轨道。
  • 项目 2 从列网格线 -1 开始,跨越到 -3。
  • 项目 3 从列网格线 1 开始,跨越到列网格线 3。
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 100px);
  gap: 10px;
}
.item1 {
  grid-column: 1;
}
.item2 {
  grid-column: -1 / -3;
}
.item3 {
  grid-column: 1 / 3;
  grid-row: 2;
}
html
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
</div>

从右到左文本的基于网格线的放置

如果我们在上一个例子的网格容器中添加 direction 属性,值为 rtl,那么第 1 行就会被放置在网格的右侧,而第 -1 行则在左侧。

css
.wrapper {
  direction: rtl;
}

如果你正在切换文本的方向,无论是整个页面还是部分页面,并且正在使用网格线,你可能需要为你的网格线命名,以避免布局完全改变方向。对于某些情况,例如网格包含文本内容时,这种切换可能正是你想要的。但对于其他用途,可能并非如此。

grid-area 属性中奇怪的值顺序

你可以使用 grid-area 属性将网格区域的所有四条线指定为一个值。当人们第一次遇到这个属性时,他们常常惊讶于这些值的顺序与 margin 的简写顺序不同——后者是顺时针的:上、右、下、左。

grid-area 值的顺序是:

  • grid-row-start
  • grid-column-start
  • grid-row-end
  • grid-column-end

对于英语,从左到右意味着顺序是:

  • top
  • left
  • bottom
  • right

这是逆时针的!这与我们对外边距和内边距的做法相反。如果我们记住 grid-area 将世界看作是“块级和行内”,你会注意到我们正在设置两个起始边,然后是两个结束边,一旦你知道了这一点,这就变得更加合乎逻辑了!

混合书写模式与网格布局

除了为文档使用正确的语言书写模式外,书写模式还可以在其他方面为 ltr 文档带来创意。在这个例子中,我们有一个网格布局,一侧有一组链接。我们使用书写模式(writing-mode: vertical-lr)将它们在列轨道中侧向旋转。

css
.wrapper {
  display: grid;
  grid-gap: 20px;
  grid-template-columns: 1fr auto;
  font:
    1em "Helvetica",
    "Arial",
    sans-serif;
}
nav {
  writing-mode: vertical-lr;
}
nav ul {
  list-style: none;
  margin: 0;
  padding: 1em;
  display: flex;
  justify-content: space-between;
}
nav a {
  text-decoration: none;
}
html
<div class="wrapper">
  <div class="content">
    <p>
      Turnip greens yarrow ricebean rutabaga endive cauliflower sea lettuce
      kohlrabi amaranth water spinach avocado daikon napa cabbage asparagus
      winter purslane kale. Celery potato scallion desert raisin horseradish
      spinach carrot soko. Lotus root water spinach fennel kombu maize bamboo
      shoot green bean swiss chard seakale pumpkin onion chickpea gram corn pea.
      Brussels sprout coriander water chestnut gourd swiss chard wakame kohlrabi
      beetroot carrot watercress. Corn amaranth salsify bunya nuts nori azuki
      bean chickweed potato bell pepper artichoke.
    </p>
    <p>
      Nori grape silver beet broccoli kombu beet greens fava bean potato
      quandong celery. Bunya nuts black-eyed pea prairie turnip leek lentil
      turnip greens parsnip. Sea lettuce water chestnut eggplant winter purslane
      fennel azuki bean earthnut pea sierra leone bologi leek soko chicory
      celtuce parsley jícama salsify.
    </p>
  </div>
  <nav>
    <ul>
      <li><a href="">Link 1</a></li>
      <li><a href="">Link 2</a></li>
      <li><a href="">Link 3</a></li>
    </ul>
  </nav>
</div>

物理值和逻辑属性

如果将逻辑网格属性与物理属性结合使用,请记住物理属性不会根据书写模式而改变。在我们的在 CSS 网格布局中对齐项目指南中,我们使用自动外边距将一个项目推离其他项目;这使用了物理属性。大多数物理属性都有逻辑属性的等价物,它们以与网格放置和对齐属性及值相同的方式遵循书写模式。

同样,当在网格区域内使用绝对定位时,你可以使用逻辑 inset 属性将项目放置在网格区域内。当混合使用逻辑和物理属性或值时,要注意它们之间的冲突。例如,你可能需要更改 CSS 以应对从 ltrrtl 的切换。你通过网格对块级和行内的理解将帮助你理解 CSS 逻辑属性和值