上のようなガントチャート風デザインを実装する方法を2種類紹介します。
実装方法1:単純にmargin-leftとwidthをそれぞれ調整して実装する
実装方法2:gridを使用する
実装方法2のgridを使用して実装するのがスマートな方法(ただの感覚)ですが、IEだとgridは対応していません。
その対策として一般的にはautoprefixer[user_help]対応していないブラウザに対応させるために対応させるための処理を自動的に行ってくれる機能。[/user_help]でプレフックスを自動で付与しますが実はgridにはautoprefixerが対応していない書き方があり、その書き方で書いてハマったことがあります。
時間を無駄にしないために、autoprefixerに対応し、IEでも使えるgridの書き方も紹介していきます。
フェーズラベルの作り方
まずは矢印タイプのフェーズをあらわすラベルを制作していきます。
ここではラベルの作り方は簡単に説明します。
作り方を詳しく知りたいかたはこちら(準備中)の記事をどうぞ
html
<div class="c-figure-label"> <div class="c-figure-label__item">期間1</div> <div class="c-figure-label__item">期間2</div> <div class="c-figure-label__item">期間3</div> <div class="c-figure-label__item">期間4</div> <div class="c-figure-label__item">期間5</div> <div class="c-figure-label__item">期間6</div> </div>
scss
.c-figure-label { display: flex; align-items: center; width: 960px; margin: 100px auto 0 auto; } .c-figure-label__item { position: relative; background-color: #333; color: #fff; display: flex; justify-content: center; align-items: center; font-weight: bold; font-size: 18px; width: calc( 100% / 6 ); height: 50px; &:not(:first-child) { &::before { content: ''; position: absolute; top: 0; left: -3px; border-top: solid 25px #fff; border-bottom: solid 25px #fff; border-left: solid 15px transparent; border-right: solid 3px #fff; } &:after { content: ''; position: absolute; top: 0; left: 0px; border-top: solid 25px #333; border-bottom: solid 25px #333; border-left: solid 15px transparent; } } }
矢印を作るポイントとしては疑似要素でこのような図形を2つ制作し、重ね合わせています。
この形の図形はborderを使って三角形を作る方法を応用して制作しています。
この形の図形の作り方もこちら(準備中)の記事で解説しています。
実装方法1:marginを使う
widthでバーの幅調整をし、場所の調整はmarginを使って単純に実装します。
ソースコードが以下になります。
html
<div class="c-figure"> <div class="c-figure__inner"> <div class="c-figure__item"> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> </div> <div class="c-figure__content"> <div class="c-figure__content-item"><a href="#">プロジェクトA</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトB</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトC</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトD</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトE</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトF</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトG</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトH</a></div> </div> </div> </div>
scss
.c-figure { width: 960px; margin: 16px auto; } .c-figure__inner { position: relative; } .c-figure__item { display: flex; align-items: center; justify-content: center; position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; } .c-figure__item-line { width: calc( 100% / 6 ); height: 100%; border-left: dotted 1px #333; &:last-child { border-right: dotted 1px #333; } } .c-figure__content { padding: 8px 0; } .c-figure__content-item { padding: 5px 10px; position: relative; font-size: 12px; & + & { margin-top: 10px; } &::before { content: ''; position: absolute; background-color: rgb(165, 164, 164); top:0; left: 5px; width: calc( 100% - 10px ); height: 100%; z-index: -1; border-radius: 2px; } & a { text-decoration: none; color: #333; font-weight: bold; &:hover { text-decoration: underline; } } } .c-figure__content-item { &:nth-child(1) { width: calc( ( 100% / 6 ) * 2 ); margin-left: calc( 100% / 6 ); } &:nth-child(2) { width: calc( ( 100% / 6 ) * 3 ); margin-left: calc( 100% / 6 ); } &:nth-child(3) { width: calc( ( 100% / 6 ) * 4 ); margin-left: calc( ( 100% / 6 ) * 2 ); } &:nth-child(4) { width: calc( ( 100% / 6 ) * 3 ); margin-left: calc( ( 100% / 6 ) * 2 ); } &:nth-child(5) { width: calc( ( 100% / 6 ) * 5 ); margin-left: calc( 100% / 6 ); } &:nth-child(6) { width: calc( ( 100% / 6 ) * 1 ); } &:nth-child(7) { width: calc( ( 100% / 6 ) * 1 ); margin-left: calc( ( 100% / 6 ) * 1 ); } &:nth-child(8) { width: calc( ( 100% / 6 ) * 3 ); margin-left: calc( ( 100% / 6 ) * 3 ); } }
順を追って解説します。
まず、背景部分の期間のエリアを分ける点線を制作しましょう。
背景の点線を作っているコードは以下です。
hrml
<div class="c-figure__item"> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> </div>
scss
.c-figure__item { display: flex; align-items: center; justify-content: center; position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; } .c-figure__item-line { width: calc( 100% / 6 ); height: 100%; border-left: dotted 1px #333; &:last-child { border-right: dotted 1px #333; } }
html構造は単純で、エリア分のdivを用意します。今回は6つのエリアがあるので6つのdivになります。そして、そのdivを囲む親を作ります。
cssは、まず、divの横幅6等分し、flexで横並びにし、敷き詰めます。
書く要素の左側にborderを点線で引き、最後の要素のみ右側にも点線のborderを引きます。
このままだと、バー部分の背景にすることはできず、縦積みになってしまうので、position:absoluteで浮かせてバー部分と重なりあうように配置します。
absoluteで浮かせるとレイヤーがバーの上に来てしまうのでz-index:-1でバーの背面に来るように調整し、完成です。
次にバー部分を制作していきます。
バーを制作しているコードは以下です
html
<div class="c-figure__content"> <div class="c-figure__content-item"><a href="#">プロジェクトA</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトB</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトC</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトD</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトE</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトF</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトG</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトH</a></div> </div>
scss
.c-figure__content { padding: 8px 0; } .c-figure__content-item { padding: 5px 10px; position: relative; font-size: 12px; & + & { margin-top: 10px; } &::before { content: ''; position: absolute; background-color: rgb(165, 164, 164); top:0; left: 5px; width: calc( 100% - 10px ); height: 100%; z-index: -1; border-radius: 2px; } & a { text-decoration: none; color: #333; font-weight: bold; &:hover { text-decoration: underline; } } } .c-figure__content-item { &:nth-child(1) { width: calc( ( 100% / 6 ) * 2 ); margin-left: calc( 100% / 6 ); } &:nth-child(2) { width: calc( ( 100% / 6 ) * 3 ); margin-left: calc( 100% / 6 ); } &:nth-child(3) { width: calc( ( 100% / 6 ) * 4 ); margin-left: calc( ( 100% / 6 ) * 2 ); } &:nth-child(4) { width: calc( ( 100% / 6 ) * 3 ); margin-left: calc( ( 100% / 6 ) * 2 ); } &:nth-child(5) { width: calc( ( 100% / 6 ) * 5 ); margin-left: calc( 100% / 6 ); } &:nth-child(6) { width: calc( ( 100% / 6 ) * 1 ); } &:nth-child(7) { width: calc( ( 100% / 6 ) * 1 ); margin-left: calc( ( 100% / 6 ) * 1 ); } &:nth-child(8) { width: calc( ( 100% / 6 ) * 3 ); margin-left: calc( ( 100% / 6 ) * 3 ); } }
こちらもhtmlの構造は単純ですね。
バーそれぞれをdivで制作し、文字をクリックすると詳細ページに飛ばしたい場合などはaタグを入れて、とても単純です。
一方cssは少し複雑になります。
まずは動きを見やすくするためにバー(c-figure__content-item)に背景色をつけます。
背景色をつけました。※見やすいようにマージンなどを調整しています。
そして、幅の計算を行ってみましょう。
プロジェクトAの幅を考えてみます。
プロジェクトAの幅6つのエリアの2つ分になりますので親要素の横幅の2 / 6という事になります。
この横幅が実装できるcalc式を考え、widthに設定すれば再現できそうです。
親要素の幅(100%)を6等分したうちの2つ分の幅ですので、calc計算式は以下のようになりますね。
width: calc( ( 100% / 6 ) * 2 );
うまくいきました。プロジェクトAのバーがエリア2つ分の横幅となっています。
次に位置の調整を行いましょう。プロジェクトAは2つ目のエリアから開始していますので、バーの左にエリア1つ分のマージンを開けなくてはいけません。
こちらも先ほどの横幅の計算をした時と同じ考えかたで親要素の横幅(100%)を6つに分けたうちの1つですので、cssは以下のようになります。
margin-left: calc( 100% / 6 );
うまくいきました。
式が少しわかりにくければmargin-left: calc( (100% / 6 ) * 1 );このようにして、横幅の式と書き方を揃えて書いてもいいかもしれません。
この要領ですべてのバーを調整すればほぼ完成です。あとは細かな調整のみです。
今までの画像は見やすくするために周りに余白が開いていましたが、バーのサイズと位置の調整のみを行った時点ではこちらの実装はされていないので、これから余白の実装方法を解説します。
まずは上の余白を開けましょう。
上の余白を開ける際は隣接セレクタを使用する方法が簡単です。
隣接セレクタとは、AとBが隣同士ならBにスタイルを適応させるといったようにある要素が隣に来た時のみに発動させることのできる書き方になります。
書き方は以下になります。
css
A + B {}
今回はバーの要素である.c-figure__content-itemの隣に.c-figure__content-itemがきたら、margin-topを10pxの余白を開けたいのでcssはこうです。
css
.c-figure__content-item + .c-figure__content-item { margin-top: 10px; }
scssで書く場合は以下になりますね。
scss
.c-figure__content-item { & + & { margin-top: 10px; } }
次は横の余白を開けます。
横の余白を左右5pxずつ開けたいですが、marginやpaddingを使って開ける事は無理です。
理由は場所をmarginを使って実装しているので、5pxのmarginを追加しても、横に少しずれるだけになるからです。
ですので、発想を『余白を開ける』から、『今のサイズから左右5pxずつ引いた大きさの背景を用意する』へ変えて実装しましょう。
赤い部分が本来のバーの範囲ですが、疑似要素を使って本来のバー(赤い部分)よりも左右が5pxずづ小さいバーを制作する事で再現したいバー(グレー)を実装します。
scssのコードはこちらの部分です。
&::before { content: ''; position: absolute; background-color: rgb(165, 164, 164); top:0; left: 5px; width: calc( 100% - 10px ); height: 100%; z-index: -1; border-radius: 2px; }
calcで元要素のサイズ(100%)から横の余白10px(右左5pxずつ)を引いてpositionのleftを5pxにすることで、左に5pxの余白を開けています。
position: absoluteになっており、バーの中の文字を覆い隠してしまうので、z-index: -1で文字の後ろに持っていきます。
すると以下ように実装が完了します。
実装方法2:gridを使う
背景の点線や、バーなどは基本的にcssは同じです。
違いはmargin-leftやcalcの計算式を使わずにgridで実装する部分ですので、実装方法1と同じ部分は省略します。
gridでの実装はautoprefixerを使ってIEに対応させることができる方法とautoprefixerを使ってもIEに対応させることができない方法があります。
ここでは一応、IEに対応できない書き方も一緒に紹介していきます。
autoprefixerに対応した書き方
.c-figure__grid-b { padding: 8px 0; display: grid; grid-template: '. item1 item1 . . .' 1fr '. item2 item2 item2 . .' 1fr '. . item3 item3 item3 item3' 1fr '. . item4 item4 item4 .' 1fr '. item5 item5 item5 item5 item5' 1fr 'item6 . . . . .' 1fr '. item7 . . . .' 1fr '. . . item8 item8 item8' 1fr / 1fr 1fr 1fr 1fr 1fr 1fr; gap: 5px 0px; } .c-figure__grid-b-item { &:nth-child(1) { grid-area: item1;; } &:nth-child(2) { grid-area: item2; } &:nth-child(3) { grid-area: item3; } &:nth-child(4) { grid-area: item4; } &:nth-child(5) { grid-area: item5; } &:nth-child(6) { grid-area: item6; } &:nth-child(7) { grid-area: item7; } &:nth-child(8) { grid-area: item8; } }
html構造は全く同じです。
異なるのはバー(.c-figure__grid-b-item)とそれを囲っている.c-figure__grid-bです。
.c-figure__grid-bにはgridを指定し、親がgrid areaを指定する方法で実装します。(gridにはもう1つ実装方法がある)
子要素になるバー(.c-figure__grid-b-item)はgrid areaのエリア名を指定するだけです。
gridの詳しい実装方法はこちらが参考になります。
直観的なのでコードをみてデザインを想像しやすいです。
autoprefixerに対応していない書き方
IE対応していないもう1つの実装方法を紹介します。
実はgridで検索すると一番ヒットする実装方法がこの方法で、こちらがおそらく一般的なのですが、一部のコードがautoprefixerで望んだ挙動にならないので、今回のような実装はIEでは崩れてしまいます。
scss
.c-figure__grid-a { padding: 8px 0; display: grid; grid-template-columns: repeat(6, 1fr); grid-template-rows: repeat(8, 1fr); gap: 5px 0px; } .c-figure__grid-a-item { &:nth-child(1) { grid-row: 1 / 2; grid-column: 2 / 4; } &:nth-child(2) { grid-row: 2 / 3; grid-column: 2 / 5; } &:nth-child(3) { grid-row: 3 / 4; grid-column: 3 / 7; } &:nth-child(4) { grid-row: 4 / 5; grid-column: 3 / 6; } &:nth-child(5) { grid-row: 5 / 6; grid-column: 2 / 7; } &:nth-child(6) { grid-row: 6 / 7; grid-column: 1 / 2; } &:nth-child(7) { grid-row: 7 / 8; grid-column: 2 / 3; } &:nth-child(8) { grid-row: 8 / 9; grid-column: 4 / 7; } }
gridをIE対応させるためにはプレフィックスをつけなければいけません。
ですが、プレフックスをコーダーが自分でつけるのは骨が折れる作業なので、基本的にはautoprefixerという自動でプレフックスを付けてくれるものを使用します。
上のコードにautoprefixerでプレフックスを以下のような結果が出力されます。
//上部略 .c-figure__grid-a-item:nth-child(1) { -ms-grid-row: 1; -ms-grid-row-span: 1; grid-row: 1 / 2; -ms-grid-column: 2; -ms-grid-column-span: 2; grid-column: 2 / 4; } //下部略
これでIE対応できると思いきや、実は
-ms-grid-row: 1;
-ms-grid-row-span: 1;
-ms-grid-column: 2;
-ms-grid-column-span: 2;
これらの1や2の数字は間違っています。だたしくはそれぞれの数字を2倍した数をセットしないと希望通りにはなりません。
さらにspanがついているプロパティがIE対応していないのか、数字を直しても位置がうまくいきません。
ですので、こちらの実装方法はやめて、grid-areaを使って実装する必要があります。
まとめ
実装方法は2種類ありますが、単純にmargin-leftを使用して実装する方法とgridで実装する方法、どちらがよいのでしょうか。
結論はどちらでもよいと思います。ただ、gridの方がシンプルでコードを見た時にわかりやすいので、どちらかというとgrid推しです。
autoprefixerを使っていない方(margin-left方法)
html
<div class="c-figure"> <div class="c-figure__inner"> <div class="c-figure__item"> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> </div> <div class="c-figure__content"> <div class="c-figure__content-item"><a href="#">プロジェクトA</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトB</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトC</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトD</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトE</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトF</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトG</a></div> <div class="c-figure__content-item"><a href="#">プロジェクトH</a></div> </div> </div> </div>
scss
.c-figure { width: 960px; margin: 16px auto; } .c-figure__inner { position: relative; } .c-figure__item { display: flex; align-items: center; justify-content: center; position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; } .c-figure__item-line { width: calc( 100% / 6 ); height: 100%; border-left: dotted 1px #333; &:last-child { border-right: dotted 1px #333; } } .c-figure__content { padding: 8px 0; } .c-figure__content-item { padding: 5px 10px; position: relative; font-size: 12px; & + & { margin-top: 10px; } &::before { content: ''; position: absolute; background-color: rgb(165, 164, 164); top:0; left: 5px; width: calc( 100% - 10px ); height: 100%; z-index: -1; border-radius: 2px; } & a { text-decoration: none; color: #333; font-weight: bold; &:hover { text-decoration: underline; } } } .c-figure__content-item { &:nth-child(1) { width: calc( ( 100% / 6 ) * 2 ); margin-left: calc( 100% / 6 ); } &:nth-child(2) { width: calc( ( 100% / 6 ) * 3 ); margin-left: calc( 100% / 6 ); } &:nth-child(3) { width: calc( ( 100% / 6 ) * 4 ); margin-left: calc( ( 100% / 6 ) * 2 ); } &:nth-child(4) { width: calc( ( 100% / 6 ) * 3 ); margin-left: calc( ( 100% / 6 ) * 2 ); } &:nth-child(5) { width: calc( ( 100% / 6 ) * 5 ); margin-left: calc( 100% / 6 ); } &:nth-child(6) { width: calc( ( 100% / 6 ) * 1 ); } &:nth-child(7) { width: calc( ( 100% / 6 ) * 1 ); margin-left: calc( ( 100% / 6 ) * 1 ); } &:nth-child(8) { width: calc( ( 100% / 6 ) * 3 ); margin-left: calc( ( 100% / 6 ) * 3 ); } }
autoprefixerを使っている方。gridで実装したい方
html
<div class="c-figure"> <div class="c-figure__inner"> <div class="c-figure__item"> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> <div class="c-figure__item-line"></div> </div> <div class="c-figure__grid-b"> <div class="c-figure__grid-b-item"><a href="#">プロジェクトA</a></div> <div class="c-figure__grid-b-item"><a href="#">プロジェクトB</a></div> <div class="c-figure__grid-b-item"><a href="#">プロジェクトC</a></div> <div class="c-figure__grid-b-item"><a href="#">プロジェクトD</a></div> <div class="c-figure__grid-b-item"><a href="#">プロジェクトE</a></div> <div class="c-figure__grid-b-item"><a href="#">プロジェクトF</a></div> <div class="c-figure__grid-b-item"><a href="#">プロジェクトG</a></div> <div class="c-figure__grid-b-item"><a href="#">プロジェクトH</a></div> </div> </div> </div>
scss
.c-figure { width: 960px; margin: 16px auto; } .c-figure__inner { position: relative; } .c-figure__item { display: flex; align-items: center; justify-content: center; position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; } .c-figure__item-line { width: calc( 100% / 6 ); height: 100%; border-left: dotted 1px #333; &:last-child { border-right: dotted 1px #333; } } .c-figure__grid-b { padding: 8px 0; display: grid; grid-template: '. item1 item1 . . .' 1fr '. item2 item2 item2 . .' 1fr '. . item3 item3 item3 item3' 1fr '. . item4 item4 item4 .' 1fr '. item5 item5 item5 item5 item5' 1fr 'item6 . . . . .' 1fr '. item7 . . . .' 1fr '. . . item8 item8 item8' 1fr / 1fr 1fr 1fr 1fr 1fr 1fr; gap: 5px 0px; } .c-figure__grid-b-item { padding: 0px 5px; font-size: 12px; & a { display: block; background-color: rgb(165, 164, 164); width: 100%; padding: 5px 10px; text-decoration: none; color: #333; font-weight: bold; border-right: 2px; &:hover { text-decoration: underline; } } } .c-figure__grid-b-item { &:nth-child(1) { grid-area: item1;; } &:nth-child(2) { grid-area: item2; } &:nth-child(3) { grid-area: item3; } &:nth-child(4) { grid-area: item4; } &:nth-child(5) { grid-area: item5; } &:nth-child(6) { grid-area: item6; } &:nth-child(7) { grid-area: item7; } &:nth-child(8) { grid-area: item8; } }