[譯] 使用 CSS 柵格和 Flexbox 打造 Trello 佈局

經過本教程,我將帶你完成 Trello 看板 (查看示例)的基本佈局。這是一個響應式的、純 CSS 的解決方案,而且咱們將只開發佈局的結構特性。css

這是一個 CodePen demo,可預覽一下最終結果。html

除了柵格佈局Flexbox,這個方案還採用了 calc視圖單位。咱們也將利用 Sass 變量,讓代碼更可讀和高效。前端

不提供向下兼容,因此請確保在支持的瀏覽器上運行。一切就緒,就讓咱們開始一步一步開發看板組件吧。react

屏幕布局

一個 Trello 看板由一個 app 欄、一個 board 欄和一個包含卡片列表的部分組成。我使用如下標籤骨架搭建出這一結構:android

<div class="ui">
  <nav class="navbar app">...</nav>
  <nav class="navbar board">...</nav>
  <div class="lists">
    <div class="list">
      <header>...</header>
      <ul>
        <li>...</li>
        ...
        <li>...</li>
      </ul>
      <footer>...</footer>
    </div>
  </div>
</div>複製代碼

這個佈局將經過 CSS 柵格實現。確切地說是 3×1 柵格(就是指一列三行)。第一行用於 app 欄,第二行用於 board 欄,第三行用於 .lists 元素。ios

前兩行各自有一個固定的高度,而第三行將撐起可變窗口高度的其他部分:css3

.ui {
  height: 100vh;
  display: grid;
  grid-template-rows: $appbar-height $navbar-height 1fr;
}複製代碼

視圖單位能夠確保 .ui 容器老是和瀏覽器的窗口高度一致。git

一個柵格化的上下文被分配給容器,而且指定了上文說的行和列。確切地說,是隻指定了行,由於聲明單獨的列是沒有必要的。一對 Sass 變量指定了兩個欄目的高度,使用 fr 單位指定 .lists 元素高度使其撐起可變窗口高度的其他部分,這樣每行的大小就設定完成了。github

卡片列表部分

如上所述,屏幕柵格的第三行託管着卡片列表的容器。這是標籤的輪廓:後端

<div class="lists">
  <div class="list">
    ...
  </div>
  ...
  <div class="list">
    ...
  </div>
</div>複製代碼

我用一個滿屏寬的 Flexbox 單行行容器來格式化列表:

.lists {
  display: flex;
  overflow-x: auto;
  > * {
    flex: 0 0 auto; // 'rigid' lists
    margin-left: $gap;
  }
  &::after {
    content: '';
    flex: 0 0 $gap;
  }
}複製代碼

overflow-x 指定 auto 值,當列表不適合視口提供的寬度時,瀏覽器會在屏幕底部顯示一個水平滾動條。

flex 簡寫屬性用於 flex item 使列表更嚴格。flex-basis (簡寫的方式使用)的 auto 值指示佈局引擎從 .list 元素的寬度屬性取值,flex-growflex-shrink 的 0 值能夠防止寬度的改變。

接下來我將在列表之間添加一個水平分隔。若是給列表設置右間距,當水平溢出時看板上最後一個列表以後的間距不會被渲染。爲了解決這個問題,列表被一個左間距分隔而且最後一個列表和窗口右邊緣的間距經過給每一個 .lists 元素添加一個僞元素 ::after 來實現。默認值 flex-shrink: 1 必定要被重寫,不然這個僞元素會」吸取「全部的負空間,而後消失。

注意在 Firefox < 54 的版本上要給 .lists 指定 width: 100% 以確保正確的佈局渲染。

卡片列表

每一個卡片列表由一個 header 欄、一個卡片序列和一個 footer 欄目組成。如下 HTML 代碼段實現了這一結構:

<div class="list">
  <header>List header</header>
  <ul>
    <li>...</li>
    ...
    <li>...</li>
  </ul>
  <footer>Add a card...</footer>
</div>複製代碼

這裏的關鍵任務是如何管理列表的高度。header 和 footer 有固定的高度(未必相等)。而後有一些不定數量的卡片,每一個卡片都有不定量的內容。所以隨着卡片的添加和移除,這個列表也會增大和縮小。

可是高度不能無限增大,它須要有一個取決於 .lists 元素高度的上限。一旦突破上線,我想有一個垂直滾動條出現來容許訪問溢出列表的卡片。

這聽起來是 max-heightoverflow 屬性能作的。但若是根容器 .list 提供了這些屬性,一旦列表達到了它的最大高度,全部的 .list 元素包括 header 和 footer 在內都會出現滾動條。下圖左右兩邊分別顯示錯誤的和正確的側邊條:

所以,讓咱們把 max-height 約束給內部的 <ul>。應該提供什麼值呢?header 和 footer 的高度必須從列表父容器(.lists)的高度之中扣除:

ul {
  max-height: calc(100% - #{$list-header-height} - #{$list-footer-height});
}複製代碼

但還有一個問題。百分比數值並不參照 .lists 而是參照 <ul> 元素的父元素 .list,而且這個元素沒有定義高度,所以這個百分比不能肯定。這個問題能夠經過設置 .list.lists 一樣高度來解決:

.list {
  height: 100%;
}複製代碼

這樣,既然 .list.lists 老是同樣高,它的 background-color 屬性不能用於列表背景色,但可使用它的子元素(header, footer 和卡片)來實現這一目的。

最後一個 list 高度的調整頗有必要,可用來計算列表底部和窗口底部的一點空間($gap)。

.list {
  height: calc(100% - #{$gap} - #{$scrollbar-thickness});
}複製代碼

還有一個 $scrollbar-thickness 須要被減去,防止列表觸及 .list 元素的水平滾動條。 事實上這個滾動條」增加「在 .lists 盒子內部。也就是說,100% 這個值是指包括滾動條在內的 .lists 的高度。

而在火狐中,這個滾動條被」附加「給 .lists 高度的外部,就是說 .lists 高度的 100% 並不包含滾動條。因此這個減法就沒什麼必要了。結果是當滾動條可見時,在火狐中已經觸及最大高度的底部邊框和滾動條的頂部之間的可視空間會稍大一些。

這是這個組件相應的 CSS 規則:

.list {
  width: $list-width;
  height: calc(100% - #{$gap} - #{$scrollbar-thickness});

  > * {
    background-color: $list-bg-color;
    color: #333;
    padding: 0 $gap;
  }

  header {
    line-height: $list-header-height;
    font-size: 16px;
    font-weight: bold;
    border-top-left-radius: $list-border-radius;
    border-top-right-radius: $list-border-radius;
  }

  footer {
    line-height: $list-footer-height;
    border-bottom-left-radius: $list-border-radius;
    border-bottom-right-radius: $list-border-radius;
    color: #888;
  }

  ul {
    list-style: none;
    margin: 0;
    max-height: calc(100% - #{$list-header-height} - #{$list-footer-height});
    overflow-y: auto;
  }
}複製代碼

如上所述,列表背景色經過給每個 .list 元素的子元素的 background-color 屬性指定 $list-bg-color 值而被渲染。overflow-y 使得卡片滾動條只有按需顯示。最後,給 header 和 footer 添加一些簡單的樣式。

完成收尾

單個卡片包含的一個列表元素 HTML:

<li>Lorem ipsum dolor sit amet, consectetur adipiscing elit</li>複製代碼

卡片也有可能包含一個封面圖片:

<li>
  <img src="..." alt="...">
  Lorem ipsum dolor sit amet
</li>複製代碼

這是相應的樣式:

li {
  background-color: #fff;
  padding: $gap;

  &:not(:last-child) {
    margin-bottom: $gap;
  }

  border-radius: $card-border-radius;
  box-shadow: 0 1px 1px rgba(0,0,0, 0.1);

  img {
    display: block;
    width: calc(100% + 2 * #{$gap});
    margin: -$gap 0 $gap (-$gap);
    border-top-left-radius: $card-border-radius;
    border-top-right-radius: $card-border-radius;
  }
}複製代碼

設置完一個背景、填充、和底部間距就差背景圖片的佈局了。這個圖片寬度必定是跨越整個卡片的,從左填充的邊緣到右填充的邊緣:

width: calc(100% + 2 * #{$gap});複製代碼

而後,指定負邊距以使圖片水平和垂直對齊:

margin: -$gap 0 $gap (-$gap);複製代碼

第三個正邊距的值用於指定封面圖片和文字之間的空間。

最後我給佔據屏幕布局第一行的兩條添加了一個 flex 格式化上下文,但它們只是草圖。經過擴展 demo 自由構建你本身的實現吧。

總結

這只是實現這種設計的一種可行方法,若是能看見其餘方式那必定頗有趣。此外,若是能完成整個佈局那就更好了,好比完成最後的兩個欄目。

另外一個潛在的改進是可以爲卡片列表實現自定義的滾動條。

因此,fork 這個 demo 盡情發揮吧,記得在下面的討論區留下你的連接哦。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索