CSS
flexbox
gridlayout

サーバーサイドエンジニアも知っておくべきCSS

SC(非公式)Advent Calendar 2017 の12日目です。
普段サーバーサイドでC#を書いたり、SQLを書いたり、AzureのPOCとかをしています。

はじめに

サーバーサイドエンジニアってデザイナさんが作ったHTML/CSSにロジックを足していくパターンが多いと思います。
.Netとかだと.Net MVCの場合はRazorなんかを使ってViewの表示を切り替えたりすると思います。
が、CSS3(CSS)を使用していると初見殺しのシンタックスが結構あるので、知っとくと便利なCSSを紹介したいと思います。

レイアウト

下手にいじっちゃうと大惨事になるレイアウト周り。
サーバーサイドの人からすると、レイアウト関連のdivタグの使い方って神ですよね。
けど、ひと昔前までは職人(神)技だったけど、今はそれに近いことが、割と簡単にできるようになってきた(かも。。。)

レイアウトサンプル

業務アプリでよく見るこんな感じのレイアウト
image.png

Flex Box display: flex

Can I Use
主要ブラウザだと、IE11だけベンダープレフィックスを付ける必要があります。それ以外の主要ブラウザではサポートされています。
きっと普段みなさんが日常的にみているサイトのほとんどはFlexBoxを使用していると思います。
Flex BoxはFlexコンテナーという親要素とFlexアイテムで構成されます。
Flexコンテナーで囲った領域内のアイテムを縦に並べるのか、横に並べるのか、右からか、左からか、といったことを決めていきます。

さっきのサンプルをFlex Boxを使うとこんな感じ。
image.png

css
.container {
  display: flex;
  flex-direction: column;
}
.item {
  flex: 0 1 auto;
}

flex-direction: column;はアイテムを縦に
flex-direction: row;はアイテムを横(デフォルト左から右)に並べます。
flex: 0 1 auto;flex-grow:0flex-shrink:1flex-basis:autoのショートハンド
さらに、Flexアイテム自体をFlexコンテナとして指定ができる。
image.png

css
.container {
  display: flex;
  flex-direction: column;
}
.item {
  display: flex;
  flex-direction: row;
  flex: 0 1 auto;
}
.side-menu {
  width: 30%;
  flex: 0 1 auto;
}
.main {
  width: 70%;
  flex: 0 1 auto;
}

image.png

css
.container {
  display: flex;
  flex-direction: column;
}
.item {
  display: flex;
  flex-direction: row;
  flex: 0 1 auto;
}
.side-menu {
  width: 30%;
  flex: 0 1 auto;
}
.main {
  display: flex;
  width: 70%;
  flex: 0 1 auto;
}
.search-area {
  height: 150px;
  flex: 0 1 auto;
}
.grid {
  height: 350px;
  flex: 0 1 auto;
}

こんな感じでFlexコンテナ(display:flex)Flexアイテム(flex:~)をネストしてレイアウトを作っていきます。float: leftみたいに、下に滑ることもないし、今は中身のコンテンツがないので、便宜上widthheightを指定していますが、コンテンツがあれば、widthheight指定なしでレイアウトを組むことができます。カッコいい:heart_eyes:

Grid Layout

個人的にイチ押しのCSS Grid Layoutです。
Can I Use
Edgeもベンダープレフィックスなしで使えるようになりました。IE11は相変わらずです。

float: leftでレイアウトを作っていく場合もdisplay: flexで作っていく場合も、並べたいdiv要素に対して、親要素でくくる必要がありましたが、GridLayoutは必要ありません。直観的(というかTableレイアウト的?)です。
とは言えContainerは必要です。
display:gridでContainerを定義して、grid-template-columnsgrid-template-rowsで列と行の境界を指定していきます。
まずはdisplay:gridを指定してContainerを作ります。

image.png

html
<body>
  <div class="container">
    <div class="header"></div>
    <div class="side-menu"></div>
    <div class="search"></div>
    <div class="grid"></div>
  </div>
</body>

grid-template-columns:~で列の数・幅を
grid-template-rows:~で行の数・幅を決めていきます。

css
.container {
  display: grid;
  grid-template-columns: 180px 1fr;
  grid-template-rows: 70px 70px 400px;
  background-color: #ddd
}
.header {
  grid-column: 1/3;
  grid-row: 1;
  border: 1px solid blue;
}
.side-menu {
  grid-column: 1;
  grid-row: 2/4;
  border: 1px solid orange;
}
.search {
  grid-column: 2;
  grid-row: 2/3;
  border: 1px solid red;
}
.grid {
  grid-column: 2;
  grid-row: 3/4;
  border: 1px solid red;
}

はい、これだけ。
HTMLを見ていただけるとわかりますが、余計なネストがないのがわかると思います。
なれるまでは、Gridテンプレートの指定の方法がいろいろあるので、苦戦するかもしれませんが、IE10以下の要件がなければ、かなり楽ができそうです。
React.jsやVue.jsのようなコンポーネントを配置する考え方と非常に相性がいいです。
ただ、Gridテンプレートの境界を超えるようなアニメーションとかは難しいです。

Tips編

よくあるレイアウト修正のTipsの紹介

line-height

よく見るけど、正確に仕様を把握している人が少なそうなline-heightです。
line-heightは行の高さを決めるプロパティですね。
基本的にはfont-sizeプロパティの大きさに対する比率で行の高さが決まります。
line-height: 1
line-height: 16em
line-height: 100%
のように色々な単位で指定できますが、子、孫のフォントサイズに対して、親要素のline-heightが継承されてしまうため、単位をつけない方がいいとされています。

個人的には、超重要な属性だと思っていて、例えばよくある検索条件レイアウトで、ラベル、テキストボックス、ドロップダウンが並んでいる場合。

chrome

image.png

IE11

image.png

FireFox

image.png

html
  <div class="container">
    <div class="item">
      <label for="name">名前:</label>  
    </div>
    <div class="item">
      <input type="text"></input>
    </div>
    <div class="item">
      <select name="aaa" id="">
        <option value="1">月曜</option>
        <option value="2">火曜</option>
        ・・・
      </select>
    </div>
  </div>
css
.container {
  display: flex;
}
.item {
  flex: 0 1 auto;
}

すごく細かいですが、テキストボックスとドロップダウンの高さとか、Topの位置が違うのがわかると思います。
FlexBoxにはFlexアイテムを縦方向で中央寄せにするalign-items: centerプロパティがあるので、指定してみます。

chrome

image.png

IE11

image.png

FireFox

image.png

css
.container {
  display: flex;
  align-items: center;
}
.item {
  flex: 0 1 auto;
}

FireFoxはいい感じになりました。
が、ブラウザ間で差異が出るので、marrgin-top:○pxとかやりたくないですよね。
ここで、line-heightの登場です。

css
.container {
  display: flex;
  align-items: center;
  line-height: 1;
}
.item {
  flex: 0 1 auto;
}

親要素である、.containerクラスにline-height: 1を追加してみます。

chrome

image.png

IE11

image.png

FireFox

image.png

『ダイブいい感じー』になりました。
これは、ブラウザのuser-agent間の差異が原因で、reset.cssとか使ってればいいんですが、reset.cssでもほとんどのタグはvertical-align:baseline;に設定されていることが多いので、縦方向がうまくそろわないことがあります。
vertical-alignmarginpaddingあたりをいじりたくなる気持ちもわかりますが、line-heightを試してみてください。

text-overflow

text-overflowは指定した幅を超えてテキストが表示されるときに「・・・(3点リーダー)」に変換してくれるプロパティです。
image.png
設計段階ではわからなかったけど、データをバインドしたら、予想以上にデータが長い。そういう時にgridの列幅を可変にする仕様追加や、ロジックで何文字以下とするのは愚かな選択です。その分テストの工数が増えるし、CSSで解決できることはCSSに任せちゃいましょう。(まぁデータの種類にもよりますが・・・)

image.png

css
.text-overflow {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

最後に

SPA(シングルページアプリケーション)の時代ですね。
サーバーサイドの役割も変わってきています。
MVCやMVVMが古いわけじゃないけど、ユーザーがもとめているものをより早くデリバリできる柔軟さが求められてきていると思います。
CSSやjavascriptで解決できるものはそちらに任せた方がいいものもあります。
ブラウザからDBまで、処理の重さや頻度、通信の速度、保守性、拡張性を加味して、最適なソリューションを実践していきたいです。
個人的には、プロジェクトでケースバイケースでClient/Server間の役割分担を決めていけばいいと思っていますが、最近は選択肢が多いのが逆に悩みのタネです。