构建多方向布局

Avatar of Ahmad El-Alfy
Ahmad El-Alfy

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

CSS 中有一些新功能可以帮助我们轻松地为不同方向和语言构建布局。本文介绍的是 CSS 逻辑属性和值(例如 margin-inline-start)。 它们是 W3C 工作草案,目前仍在进行大量编辑,但已在许多浏览器中 发布。我想谈谈这个话题,因为我使用这些属性已经有一段时间了,并且在切换到这些属性后,我的工作流程得到了显著提升。

我将讨论规范以及如何在您当前的工作中使用它。我住在埃及,我们使用阿拉伯语作为主要语言。阿拉伯语是从右到左书写的,这意味着阿拉伯语网站看起来像是英语版本的镜像。我们创建的大多数网站都是双语的,这意味着我们为每个方向提供一个特定的样式表。我们通过翻转几乎所有内容的值和属性来做到这一点!我不会详细讨论这部分内容,但您可以快速了解一下我之前关于该主题撰写的一篇 文章

它从在 HTML 标签上声明 dir 属性开始。

 <html dir="rtl">

此属性接受两个值之一:ltr(如果未指定任何值,则为默认值)和 rtl。根据其值,浏览器开始按照特定算法绘制元素。文本将根据方向书写,标点符号将放置在其正确的位置。某些元素(如表格)的方向将被切换(例如,在 rtl 中,<td> 从右侧开始)。值得庆幸的是,一些新的规范(如 CSS Grid 和 flexbox)采用了与表格类似的方法。这意味着我们不必更改任何内容的顺序,因为浏览器会处理它!

HTML5 为 dir 属性引入了一个新的 auto 值。它将检查元素中的第一个字符,如果它属于从左到右书写的语言(如拉丁字符),则该元素将具有 ltr 方向,反之亦然。W3C 敦促作者避免依赖此值来确定文本方向,而是使用服务器端解决方案。

auto 值的一个有趣的用例是,当您不确定内容的方向时,例如用户生成的内容,例如评论线程。我看到很多人在阿拉伯语网站上用英语参与讨论。除了 Internet Explorer 和 Edge 之外,对 auto 的支持都很好。

介绍 :dir() 伪类

:dir() 伪类是一个新的选择器修饰符,它根据元素的方向值选择元素。它的工作原理如下

/* Select all paragraphs that have their direction value set to rtl */
p:dir(rtl) {
  font-size: 16px; /* Sometimes Arabic glyphs need a size boost to feel right. */
}


/* Select all paragraphs that have their direction value set to ltr */
p:dir(ltr) {
  font-size: 14px;
}

如果元素直接设置了方向或使用 auto 自动评估设置的值,则此选择器将选择该元素,它仍然会根据其内容正确选择元素!

<style>
  p:dir(ltr) {
    background: green;
  }
  p:dir(rtl) {
    background: red;
  }
</style>


<!-- The following paragraph will have a green background -->
<p dir="auto">This is a paragraph that starts with a latin character</p>
<!-- The following paragraph will have a red background -->
<p dir="auto">هذا النص يستخدم حروف عربية</p>

遗憾的是,:dir() 的支持情况并不理想,仅限于 Firefox。

此浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示浏览器在该版本及更高版本中支持该功能。

桌面

ChromeFirefoxIEEdgeSafari
12017*12016.4

移动/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712716.4

即使浏览器支持非常好,选择器也只允许您定位元素并手动向其应用某些样式。这意味着我们仍然应该翻转所有内容的值(如边距、填充、浮动、位置等),这实际上并不能提高我们的工作流程或减少生成多方向布局的工作量。

介绍 CSS 逻辑属性和值

如 W3C 所定义,逻辑属性和值使我们能够通过逻辑而不是物理方向和维度映射来控制布局。让我们跳过技术术语,直接进入细节。这些为我们提供了新的属性和值,这些属性和值将根据某些条件以不同的方式进行评估。

逻辑值

假设我们有一个段落,我们希望将其对齐到与语言自然方向相反的方向。假设这是遵循 ltr 方向的英语。我们会这样做

<article>
  <p class="opposite">
    Lorem ipsum dolor sit amis ..
  </p>
</article>

CSS 文件如下所示

.opposite {
  text-align: right;
}

为了为 rtl 版本提供相反的效果,我们将通过使用 dir 属性定位 <html> 标签来覆盖选择器,或者简单地为 rtl 版本提供一个不同的文件,如下所示

html[dir="rtl"] .opposite {
  text-align : left;
}

创建逻辑属性和值是为了解决这个问题。为什么我们不使用评估为正确上下文的值,而是使用左和右?在 ltr 元素中,值 left 表示元素的开头或开始,而在 rtl 元素中,值 right 表示开始!很简单,对吧?

因此,我们可以使用以下代码代替之前编写的代码

.opposite {
  text-align: end;
}

就是这样!如果元素的计算方向是 ltr,则文本将右对齐;如果计算方向是 rtl,则文本将左对齐。因此,我们可以简单地用 startend 替换 text-alignleftright 值。这比我们之前的选项容易得多,也更方便。

逻辑属性

我们刚才看到的的是逻辑,现在让我们转向逻辑属性。逻辑属性具有相同的想法;它们根据元素的方向以不同的方式进行评估。以边距为例。以前,我们想在段落的开头添加一些空间。我们可以在 ltr 文档中使用以下代码来实现

article img {
  margin-left: 15px;
}

现在,在 rtl 版本的情况下,我们需要将边距添加到相反的方向,并重置左值

html[dir="rtl"] article img {
  margin-left: 0;
  margin-right: 15px;
}

我们可以使用逻辑属性做得更好。请考虑以下内容

article img {
  margin-inline-start: 15px;
}

-inline-start 部分评估为图像水平轴的开头。在 ltr 的情况下,这意味着左侧,在 rtl 的情况下,这意味着右侧。

start 和 end 现在可能很明显了,但 inline 这个词是什么,为什么我们需要它?要理解它,我们需要谈论一个叫做CSS 书写模式的东西。Jen Simmons 撰写了一篇关于该主题的 优秀文章。我不会重复那里解释的所有内容,但最重要的是,我们可以使用书写模式来定义书写方向。一些语言,如中文、韩文和日文,可以从上到下垂直书写。CSS 书写模式允许我们控制流程。请查看以下段落

您可以清楚地识别块的顶部、底部、左侧和右侧边缘。如果我们使用 CSS 书写模式更改段落的方向使其垂直流动会发生什么情况?

当我们谈论此旋转段落的“顶部”时,我们的意思是内容的开头,还是在未旋转时右侧边缘的位置?识别四个方向变得令人困惑。让我们从不同的角度来看待它们。“正常”书写条件下,垂直轴将后缀为 -block,水平轴将后缀为 -inline,两者都后跟 startend

如果我们旋转它,它应该是这样的

由于我们正在讨论正常的水平布局,因此我们将使用 -inline-start-inline-end,但了解其他属性也很不错。我们还可以使用逻辑关键字编写逻辑简写值。请考虑以下内容

article img {
  margin: logical 10px 20px 30px 40px;
}

图像边距的计算值将如下所示

article img {
  margin-block-start: 10px;
  margin-inline-start: 20px;
  margin-block-end: 30px;
  margin-inline-end: 40px;
}

logical 关键字目前是实验性功能,这意味着在规范正式发布之前,它可能会发生变化或被替换。您可以同时关注关于该主题的 公开讨论

逻辑属性还允许我们将值应用于特定轴,这意味着我们分别对水平轴和垂直轴有 margin-inlinemargin-block

属性逻辑属性
margin-topmargin-block-start
margin-leftmargin-inline-start
margin-rightmargin-inline-end
margin-bottommargin-block-end

就像逻辑外边距属性一样,我们也有逻辑填充属性,它们遵循与外边距相同的规则。

属性逻辑属性
padding-toppadding-block-start
padding-leftpadding-inline-start
padding-rightpadding-inline-end
padding-bottompadding-block-end

逻辑定位属性

在前面的例子中,我们能够通过添加后缀来修改属性的含义,但是位置呢?属性名称完全改变了,从我们现在所知的toprightbottomleft变成了其他名称。

.element {
  position: absolute;
  inset-block-start: 0;  /* evaluates to top */
  inset-block-end: 0;    /* evaluates to bottom */
  inset-inline-start: 0; /* evaluates to left in ltr and right in rtl */
  inset-inline-end: 0;   /* evaluates to right in ltr and left in rtl */
}

学习新的属性和值可能很困难,但是,嘿,我们得到一个名为inset的简写属性,让它稍微容易一些。

/* Shorthand FTW! */
.element {
  position: absolute;
  inset: logical 10px 20px 30px 40px;
}


/* It evaluates to this */
.element {
  position: absolute;
  inset-block-start: 10px;
  inset-inline-start: 20px;
  inset-block-end: 30px;
  inset-inline-end: 40px;
}

inset支持inset-blockinset-inline,就像margin和padding一样。

属性逻辑属性
topinset-block-start
leftinset-inline-start
rightinset-inline-end
bottominset-block-end

逻辑边框属性

通过添加-inline-start-block-start,边框属性也可以变成逻辑属性。

属性逻辑属性
border-top{-size|style|color}border-block-start{-size|style|color}
border-left{-size|style|color}border-inline-start{-size|style|color}
border-right{-size|style|color}border-inline-end{-size|style|color}
border-bottom{-size|style|color}border-block-end{-size|style|color}

在Rachel Andrew关于逻辑属性和值的深入探讨中,她提供了一个优秀的演示,展示了逻辑边框属性以及它们如何响应不同的书写模式。

我们今天如何开始使用这一切?

感谢PostCSS的强大功能,我们今天就可以开始使用逻辑属性和值的全部魔力!Jonathan Neal编写了这个很棒的PostCSS插件,它使我们能够以逻辑方式编写代码,并将代码编译成当今浏览器能够理解的内容。该插件分三个阶段工作。

  • 它将新语法转换为不受支持的浏览器可以识别的现有标准,使用:dir伪类为ltr和rtl创建输出。
  • 它使用Neal的另一个插件:dir转换为属性选择器,如下所示。
 .element:dir(ltr) {
   ...
 }
 [dir="ltr"] .element {
   ...
 }
  • 它使用postcss-nested插件将嵌套选择器转换为单行选择器,与其他CSS预处理器的方式相同。

PostCSS适用于任何工作流程。您可以使用Grunt、Gulp和webpack尝试它。


最后我想说,自从转向逻辑属性和值以来,我看到了很多好处。当然,构建多方向布局需要时间。有学习曲线、需要编写更多属性,当然还有测试。我们之前创建多方向布局的方法要么是在开发中同时处理两个方向,要么一次处理一个方向——这两种方法都不适合大型项目。使用逻辑属性和值,您只需编写一次代码,它就可以在两个方向上工作,无需任何考虑。

参考文献