CSS tips - Flexbox でネイティブっぽいリストアイテムを作る方法

phi phi on css, css3, Flexbox

最近, 仕事で CSS を使ってスマホの UI を再現してくれーという要望が多いので, その中でも中心となる リストアイテムのスタイリングについて備忘録がてらまとめました.

Flexbox を上手く使うことでシンプルなものからアイコン付きや, 2カラム, 3カラムになってもキレイに配置されるようにスタイリングすることができます.

デモはすべて Runstant で作ってあるので, fork して色を変えたり要素を増やしたりして遊んでみてください.

※ CSS には Less を使っています. 変換後の状態が見たい場合は下記のツールをお使いください.

リアルタイムで LESS から CSS に変換する Web ツール作りました! | phiary

シンプルなリストアイテム

border-top は全ての要素に, border-bottom は最後の要素のみ表示するようにすることで区切り線を表現しています. flex 系の指定は以降への布石です.

Edit

HTML

<div class='list'>  
  <a class='item', href=''>
    <div class='content'>
      <div>Item</div>
    </div>
  </a>
  <a class='item', href=''>
    <div class='content'>
      <div>Item</div>
    </div>
  </a>
  <a class='item pad', href=''>
    <div class='content'>
      <div>Item</div>
    </div>
  </a>
  <a class='item pad', href=''>
    <div class='content'>
      <div>Item</div>
    </div>
  </a>
</div>  

CSS(LESS)

.item {
  text-decoration: none;
  color: #444;

  padding: 0.5rem;
  background-color: white;

  display: flex;
  align-items: flex-start;
  justify-content: space-between;

  border-top: 1px solid rgba(0, 0, 0, 0.1);
  &:last-child {
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  }
  &:hover {
    background-color: #fafafa;
  }

  &.pad {
    padding: 1rem;
  }
}

複数行に対応したリストアイテム

やっていることは, タイトルを強調しているぐらいですね.

Edit

HTML

<div class='list'>  
  <a class='item', href=''>
    <div class='content'>
      <div class='header'>
        <div class='title'>Daniel</div>
      </div>
      <div>Hi, how are you? Glad to hear you are interested in our project.</div>
    </div>
  </a>
  <a class='item', href=''>
    <div class='content'>
      <div class='header'>
        <div class='title'>John</div>
      </div>
      <div>I am a teacher at a elementary school.</div>
    </div>
  </a>
</div>  

CSS(LESS)

.item {
  text-decoration: none;
  color: #444;

  padding: 0.5rem;
  background-color: white;

  display: flex;
  align-items: flex-start;
  justify-content: space-between;

  border-top: 1px solid rgba(0, 0, 0, 0.1);
  &:last-child {
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  }
  &:hover {
    background-color: #fafafa;
  }

  &.pad {
    padding: 1rem;
  }

  .content {
    flex: 1;
    .header {
      display: flex;
      align-items: center;
      margin-bottom: 0.1rem;
      .title {
        flex: auto;
        color: hsl(220, 80%, 50%);
        font-weight: bold;
      }
    }
  }
}

時間表示にも対応したリストアイテム

align-items: center を指定することでタイトルと時間の縦軸がキレイにならぶよう調整しています. また, title のみ flex: auto を指定することで画面サイズに応じて幅が広がるようになっています.

Edit

HTML

<div class='list'>  
  <a class='item', href=''>
    <div class='content'>
      <div class='header'>
        <div class='title'>Daniel</div>
        <div class='time'><time>2016/05/18</time></div>
      </div>
      <div>Hi, how are you? Glad to hear you are interested in our project.</div>
    </div>
  </a>
  <a class='item', href=''>
    <div class='content'>
      <div class='header'>
        <div class='title'>John</div>
        <div class='time'><time>2016/05/18</time></div>
      </div>
      <div>I am a teacher at a elementary school.</div>
    </div>
  </a>
</div>  

CSS(LESS)

.item {
  text-decoration: none;
  color: #444;

  padding: 0.5rem;
  background-color: white;

  display: flex;
  align-items: flex-start;
  justify-content: space-between;

  border-top: 1px solid rgba(0, 0, 0, 0.1);
  &:last-child {
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  }
  &:hover {
    background-color: #fafafa;
  }

  &.pad {
    padding: 1rem;
  }

  .content {
    flex: 1;
    .header {
      display: flex;
      align-items: center;
      margin-bottom: 0.1rem;
      .title {
        flex: auto;
        color: hsl(220, 80%, 50%);
        font-weight: bold;
      }
      .time {
        text-align: right;
        color: #888;
        font-size: 0.6rem;
      }
    }
  }
}

アイコン画像付きリストアイテム

最初に指定した display:felx が活きてきます. この指定による横並びになり, .content のほうには flex: 1, .icon のほうは width を固定サイズにそれぞれ指定することで, 画面サイズが変わっても .content の方のみ伸縮するようにしています.

またアイコンを円形にするスタイリングを .circle クラスとして外出しすることで HTML 側で 使い分けることができるようになっています.

Edit

HTML

<div class='list'>  
  <a class='item', href=''>
    <div class='icon'>
      <img src='http://phi-jp.github.io/phiary-workspace/assets/images/kenkyo.jpg' />
    </div>
    <div class='content'>
      <div class='header'>
        <div class='title'>Daniel</div>
        <div class='time'><time>2016/05/18</time></div>
      </div>
      <div>Hi, how are you? Glad to hear you are interested in our project.</div>
    </div>
  </a>
  <a class='item', href=''>
    <div class='icon circle'>
      <img src='http://phi-jp.github.io/phiary-workspace/assets/images/kenkyo.jpg' />
    </div>
    <div class='content'>
      <div class='header'>
        <div class='title'>John</div>
        <div class='time'><time>2016/05/18</time></div>
      </div>
      <div>I am a teacher at a elementary school.</div>
    </div>
  </a>
</div>  

CSS(LESS)

.item {
  text-decoration: none;
  color: #444;

  padding: 0.5rem;
  background-color: white;

  display: flex;
  align-items: flex-start;
  justify-content: space-between;

  border-top: 1px solid rgba(0, 0, 0, 0.1);
  &:last-child {
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  }
  &:hover {
    background-color: #fafafa;
  }

  &.pad {
    padding: 1rem;
  }

  .content {
    flex: 1;
    .header {
      display: flex;
      align-items: center;
      margin-bottom: 0.1rem;
      .title {
        flex: auto;
        color: hsl(220, 80%, 50%);
        font-weight: bold;
      }
      .time {
        text-align: right;
        color: #888;
        font-size: 0.6rem;
      }
    }
  }

  .icon {
    width: 30px;
    height: 30px;
    margin-right: 0.5rem;
    border: 1px solid rgba(0, 0, 0, 0.1);

    &.circle {
      border-radius: 50%;
      overflow: hidden;
    }

    img {
      width: 100%;
      height: 100%;
    }
  }
}

カードタイプのリストアイテム

margin を指定して, borderbox-sizing を調整してあげるだけで
Material Design の Cards のような表現ができます.

これも .card というクラスとして外出ししておくことで HTML 側で切り替えることができるようにしています.

Edit

HTML

<div class='list'>  
  <a class='item card', href=''>
    <div class='icon'>
      <img src='http://phi-jp.github.io/phiary-workspace/assets/images/kenkyo.jpg' />
    </div>
    <div class='content'>
      <div class='header'>
        <div class='title'>Daniel</div>
        <div class='time'><time>2016/05/18</time></div>
      </div>
      <div>Hi, how are you? Glad to hear you are interested in our project.</div>
    </div>
  </a>
  <a class='item card', href=''>
    <div class='icon circle'>
      <img src='http://phi-jp.github.io/phiary-workspace/assets/images/kenkyo.jpg' />
    </div>
    <div class='content'>
      <div class='header'>
        <div class='title'>John</div>
        <div class='time'><time>2016/05/18</time></div>
      </div>
      <div>I am a teacher at a elementary school.</div>
    </div>
  </a>
</div>  

CSS(LESS)

.item {
  text-decoration: none;
  color: #444;

  padding: 0.5rem;
  background-color: white;

  display: flex;
  align-items: flex-start;
  justify-content: space-between;

  border-top: 1px solid rgba(0, 0, 0, 0.1);
  &:last-child {
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  }
  &:hover {
    background-color: #fafafa;
  }

  &.pad {
    padding: 1rem;
  }

  &.card {
    margin: 0.5rem;
    border: none;
    border-radius: 4px;
    box-shadow: 1px 1px 2px 0px #aaa;
  }

  .content {
    flex: 1;
    .header {
      display: flex;
      align-items: center;
      margin-bottom: 0.1rem;
      .title {
        flex: auto;
        color: hsl(220, 80%, 50%);
        font-weight: bold;
      }
      .time {
        text-align: right;
        color: #888;
        font-size: 0.6rem;
      }
    }
  }

  .icon {
    width: 30px;
    height: 30px;
    margin-right: 0.5rem;
    border: 1px solid rgba(0, 0, 0, 0.1);

    &.circle {
      border-radius: 50%;
      overflow: hidden;
    }

    img {
      width: 100%;
      height: 100%;
    }
  }
}

様々なタイプのリストアイテム

全部のせです. CSS は, カードタイプのリストアイテム と同じものを使っています.

一部をクラス指定にすることによってオプション化してあるので, 今まで紹介してきたリストアイテムを HTML 側で使い分けることができます.

Edit

HTML

<div class='list'>  
  <a class='item', href=''>
    <div class='content'>
      <div>Item</div>
    </div>
  </a>
  <a class='item pad', href=''>
    <div class='content'>
      <div>Item</div>
    </div>
  </a>
  <a class='item', href=''>
    <div class='content'>
      <div class='header'>
        <div class='title'>Daniel</div>
      </div>
      <div class='desc'>Hi, how are you? Glad to hear you are interested in our project.</div>
    </div>
  </a>
  <a class='item', href=''>
    <div class='icon circle'>
      <img src='http://phi-jp.github.io/phiary-workspace/assets/images/kenkyo.jpg' />
    </div>
    <div class='content'>
      <div class='header'>
        <div class='title'>John</div>
      </div>
      <div class='desc'>I am a teacher at a elementary school.</div>
    </div>
  </a>
  <a class='item', href=''>
    <div class='icon circle'>
      <img src='http://phi-jp.github.io/phiary-workspace/assets/images/kenkyo.jpg' />
    </div>
    <div class='content'>
      <div class='header'>
        <div class='title'>Lucy</div>
        <div class='time'><time>2016/05/18</time></div>
      </div>
      <div class='desc'>Hello, nice meeting you.</div>
    </div>
  </a>
</div>

<div class='list'>  
  <a class='item card', href=''>
    <div class='icon circle'>
      <img src='http://phi-jp.github.io/phiary-workspace/assets/images/kenkyo.jpg' />
    </div>
    <div class='content'>
      <div class='header'>
        <div class='title'>Elizabeth</div>
      </div>
      <div class='desc'>Please call me when you're ready.</div>
    </div>
  </a>
</div>