使用 CSS Grid 创建会议日程

Avatar of Mark Root-Wiley
Mark Root-Wiley 发表

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

找到一项新技术的完美应用,感觉真是妙不可言。您可以阅读各种 便捷的入门指南,并欣赏 炫酷的演示,但当您第一次在自己的项目中使用它时……才是真正理解它的时刻。

在为会议日程构建灵活布局时,我对 CSS Grid 有了新的认识。项目的需要与网格的优势完美契合:一个二维(垂直和水平)布局,以及子元素的复杂放置。在构建概念验证的过程中,我发现了一些技巧,使代码高度易读,并且工作起来非常有趣。

生成的演示包含了一些有趣的 CSS Grid 功能用法,并迫使我处理一些日常生活中不会遇到的网格细节。

在开始之前,最好打开另一个选项卡,并参考 CSS-Tricks 的 CSS Grid 指南,以便在整个文章中参考我们涵盖的概念。

定义我们的布局需求

由于 WordCamp(每年在世界各地举办的数百场以 WordPress 为中心的会议)的存在,我着手创建以下布局。这些不同的活动规模和格式各异,但都使用相同的日程安排布局工具。

A schedule with 4 tracks of presentations (columns) and 6 times slots. The length of sessions vary, and some talks span multiple tracks.
我们将构建的最终布局。

我曾帮助安排过几个 WordCamp 并设计过 WordCamp 网站,因此我知道现有 HTML 表格布局的缺点。如果您的日程安排不适合统一网格,那么……¯\_(ツ)_/¯

为了寻找更好的方法,我首先列出了布局需求

  • 时长可变的会议(限于设定时间增量)
    想象一下,三个房间里连续进行一个小时的演讲,另一个房间里进行一个两小时的研讨会。
  • 跨越一个或多个“赛道”的会议
    赛道通常与场地的特定房间相关联。就我在西雅图举办的本地 WordCamp 而言,场地可以拆除一堵墙来合并两个房间!
  • 日程安排可以包含空白空间
    最后一刻的取消或超短的会议会在日程安排中产生空缺。
  • 设计易于使用 CSS 自定义
    WordCamp 网站仅允许通过 CSS 进行主题设置。
  • 布局可以从 CMS 内容自动生成
    由于我们是在数千个网站上从结构化的会议数据构建布局,因此我们不能依赖任何过于聪明或定制的 HTML 或 CSS。

开始:稳固的 HTML

在编写任何 CSS 之前,我总是从可靠的 HTML 开始。

顶级 <div> 将具有 .schedule 类,并充当网格父元素。每个唯一的开始时间都有其自己的标题,后面跟着所有在该时间开始的会议。每个会议的标记并不重要,但请确保无需查看布局即可了解会议何时何地举行。(您很快就会明白为什么。)

<h2>Conference Schedule</h2>
<div class="schedule">

  <h3 class="time-slot">8:00am</h3>
  <div class="session session-1 track-1">
    <h4 class="session-title"><a href="#">Session Title</a></h4>
    <span class="session-time">8:00am - 9:00am</span>
    <span class="session-track">Track 1</span>
    <span class="session-presenter">Presenter Name</span>
  </div>
  <!-- Sessions 2, 3, 4 -->

  <h3 class="time-slot">9:00am</h3>
  <div class="session session-5 track-1">
    <h4 class="session-title"><a href="#">Session Title</a></h4>
    <span class="session-time">9:00am - 10:00am</span>
    <span class="session-track">Track 1</span>
    <span class="session-presenter">Presenter Name</span>
  </div>
  <!-- Sessions 6, 7, 8 -->

  <!-- etc... -->

</div> <!-- end .schedule -->

移动布局和网格回退完成!

添加一些您自己的 CSS 来美化界面,我们的移动布局和不支持 CSS Grid 的浏览器的回退功能已经完成了!

以下是使用我所用颜色后的外观

手机用户、放大浏览器或甚至使用 Internet Explorer 的用户都可以轻松找到他们在本次会议上喜欢的会议!

添加网格布局

现在是真正的 CSS Grid 部分了!

我在构建这个过程中获得的顿悟来自阅读 Robin 在 CSS-Tricks 上发表的文章 “使用 CSS Grid 创建条形图。” TL;DR:一行网格代表图表高度的 1%,因此条形跨越的行数与它所代表的百分比相同。

.chart {
  display: grid;
  grid-template-rows: repeat(100, 1fr); /* 1 row = 1%! */
}

.fifty-percent-bar {
  grid-row: 51 / 101; /* 101 - 51 = 50 => 50% */
}

这让我意识到,网格非常适合任何与某种规则增量单位绑定的布局。在日程安排的情况下,该单位是时间!在本演示中,我们将使用 30 分钟的增量,但您可以根据自己的需要进行设置。(只需注意 限制 Grid 布局为 1000 行的 Chrome 浏览器错误。)

我尝试的第一个版本使用了与 Robin 的条形图类似的语法,以及一些基本数学运算来放置会议。我们使用 8 行,因为在上午 8 点到中午 12 点之间有 8 个 30 分钟的增量。请记住,隐式网格线编号从 1 开始(而不是 0),因此网格行编号为 1 到 9。

A 5 row grid with numbered lines, 1-6.
带有编号网格线的单列日程安排的初步版本。(查看演示:编号网格线与命名网格线
.schedule {
  display: grid;
  grid-template-rows: repeat(8, 1fr);
}

.session-1 {
  grid-row: 1 / 3; /* 8am - 9am, 3 - 1 = 2 30-minute increment */
}

.session-2 {
  grid-row: 3 / 6; /* 9am - 10:30am, 6-3 = 3 30-minute increments */
}

此技术的难题在于,在具有大量行的网格上放置项目非常抽象且令人困惑。(此问题也为 Robin 的条形图演示增加了大量复杂性。)

此时,命名网格线 应运而生!我们可以根据相应的时间给每行一个可预测的名称,而不是依赖网格线编号。

A 5 row grid with rows named for the time. Example: time-0800
通过对日程安排使用命名网格线,放置会议变得容易得多。(查看演示:编号网格线与命名网格线
.schedule {
  display: grid;
  grid-template-rows:
    [time-0800] 1fr
    [time-0830] 1fr
    [time-0900] 1fr
    [time-0930] 1fr;
    /* etc...
        Note: Use 24-hour time for line names */
}

.session-1 {
  grid-row: time-0800 / time-0900;
}

.session-2 {
  grid-row: time-0900 / time-1030;
}

这真是妙不可言。无需进行复杂的数学计算来确定会议开始或结束前后的行数。更棒的是,我们可以使用存储在 WordPress 中的信息生成网格线名称和会议布局样式。将开始时间和结束时间传递给网格,一切就绪!

由于日程安排有多个赛道,因此我们需要为每个赛道创建一个列。赛道的工作方式类似于时间,为每条网格列线使用命名的赛道线。此外,还有一个额外的第一列用于开始时间标题。

.schedule { /* continued */
  grid-template-columns:
    [times] 4em
    [track-1-start] 1fr
    [track-1-end track-2-start] 1fr
    [track-2-end track-3-start] 1fr
    [track-3-end track-4-start] 1fr
    [track-4-end];
}

但是,在这里,我们将命名网格线更进一步。每行获得两个名称:一个表示它开始的赛道,一个表示它结束的赛道。这并非严格必要,但它使代码更清晰,尤其是在会议跨越多个列时。

定义了基于时间和赛道的网格线后,我们现在只需知道会议的时间和赛道即可放置任何会议!

.session-8 {
  grid-row: time-1030 / time-1100;
  grid-column: track-2-start / track-3-end; /* spanning two tracks! */
}

将所有这些结合在一起,我们得到了一些冗长但非常易读的代码,使用起来非常愉快。

@media screen and (min-width: 700px) {
  .schedule {
    display: grid;
    grid-gap: 1em;
    grid-template-rows:
      [tracks] auto /* Foreshadowing! */
      [time-0800] 1fr
      [time-0830] 1fr
      [time-0900] 1fr
      [time-0930] 1fr
      [time-1000] 1fr
      [time-1030] 1fr
      [time-1100] 1fr
      [time-1130] 1fr
      [time-1200] 1fr;
    grid-template-columns:
      [times] 4em
      [track-1-start] 1fr
      [track-1-end track-2-start] 1fr
      [track-2-end track-3-start] 1fr
      [track-3-end track-4-start] 1fr
      [track-4-end];
  }

  .time-slot {
    grid-column: times;
  }
}
<div class="session session-1 track-1" style="grid-column: track-1; grid-row: time-0800 / time-0900;">
  <!-- details -->
</div>
<div class="session session-2 track-2" style="grid-column: track-2; grid-row: time-0800 / time-0900">
  <!-- details -->
</div>
<!-- etc... -->

最终代码使用内联样式进行会议放置,我觉得这样很合适。如果您不喜欢这种方式,并且正在使用更新的浏览器,则可以通过 CSS 变量将 行名称传递给 CSS

快速提示:使用 fr 单位与 auto 值作为行高

值得注意的一个细节是使用 fr 单位定义行高。

当使用 1fr 确定行高时,所有行都具有相同的高度。高度由日程安排中最高行的内容决定。(我必须阅读 W3C 的 fr 规范才能弄清楚这一点!)这会生成一个美丽的日程安排,其中高度与时间成比例,但也可能导致非常高的布局。

A grid with 5 rows of equal height. The height matches the tallest row containing 3 lines of text.
使用 1fr 生成由网格中最高行决定的等高行。(查看演示:网格行高 1fr 与 auto

例如,如果您的日程安排网格以 15 分钟为增量,从早上 7 点到晚上 6 点,则总共有 48 行网格。在这种情况下,您可能希望使用 auto 作为行高,因为每个网格行的高度由其内容决定,因此日程安排更加紧凑。

A grid with 5 rows of unequal height. Each row contains its content.
使用 auto 使每行的高度与其内容相同。(查看演示:网格行高 1fr 与 auto

关于可访问性

某些 CSS Grid 技术存在真正的可访问性问题。具体来说,以不匹配源顺序的方式更改信息的可视顺序会给使用键盘导航的用户带来问题。

此布局利用此功能任意地在网格上放置项目,因此需要谨慎。但是,由于标题和源顺序与开始时间的可视化保持一致,因此在我看来,这似乎是一种安全的使用方式。

如果您受到启发想要做类似的事情,请仔细考虑可访问性。在这种情况下,按时间顺序排列信息是有意义的,但我可以想象一个合理的案例,即TAB顺序应该向下遍历列而不是横跨行。(修改此演示以执行此操作应该不难!)

无论您做什么,始终要考虑可访问性。

添加粘性轨道名称

最后,是时候添加看起来像每个列顶部的表格标题的轨道名称了。由于会话的轨道已经可见,因此我选择使用aria-hidden="true"将“标题”隐藏在辅助技术之外。

轨道名称位于第一行网格中,方便地命名为“tracks”。只要您没有遇到任何奇怪的溢出问题position: sticky就可以在您滚动时使这些名称保持可见。

<span class="track-slot" aria-hidden="true" style="grid-column: track-1;">Track 1</span>
<span class="track-slot" aria-hidden="true" style="grid-column: track-2;">Track 2</span>
<span class="track-slot" aria-hidden="true" style="grid-column: track-3;">Track 3</span>
<span class="track-slot" aria-hidden="true" style="grid-column: track-4;">Track 4</span>
.track-slot {
  display: none; /* only visible with Grid layout */
}

@supports( display:grid ) {
  @media screen and (min-width:700px) {    
    .track-slot {
      grid-row: tracks;
      display: block;
      position: sticky;
      top: 0;
      z-index: 1000;
      background-color: rgba(255,255,255,.9);
    }
  }
}

这是最终演示的一个巧妙的小收尾工作。✨

结果

以下是我们将所有内容组合在一起后的效果!

查看 CodePen 上的笔
使用 CSS Grid 创建会议日程
,作者是 Mark Root-Wiley (@mrwweb)
CodePen 上。

我们才刚刚开始

这个日程安排绝对是我用 CSS Grid 做出的最令人满意的作品。我喜欢行命名的“数据驱动”和语义化方式,可访问性和CMS 需求也完美地融入其中,没有任何不便。

我唯一剩下的问题是还有哪些其他类型的“数据驱动”网格可以构建?我见过一些很棒的日历布局,这里还有一个大富翁棋盘布局。足球场、时间线、餐厅餐桌或剧院座位呢?还有什么?