かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

CSS 100%幅の子要素にネガティブマージンを使うと右側だけはみ出して横スクロールが表示されてしまう

所謂Bootstrapの.row.col-で使われているような親要素が左右のネガティブマージンを持ち子要素がfloatを使ったカラムレイアウトを100%の画面に入れた時に右側だけが100%のブラウザサイズからはみ出して横スクロールが表示される現象に遭遇しました。

横スクロールが発生する例

HTML

<div class="container">
  <div class="row">
    <div class="col col-half">
      <div class="content">content</div>
    </div>
    <div class="col col-half">
      <div class="content">content</div>
    </div>
  </div>
</div>

CSS (stylus表記)

*
  box-sizing: border-box
.container
  width: 100%
.row
  margin-left: -15px
  margin-right: -15px
  .col
    float: left
    padding-left: 15px
    padding-right: 15px
  .col-half
    width: 50%

f:id:kikiki-kiki:20170325212944p:plain
こんな風に横スクロールが発生してしまいます。

解決方法1 ネガディブマージンを持つ要素の親にパディングを持たせる

Bootstrapをよくよく観察していると、ネガティブマージンを持つ.rowの親要素には必ずネガティブマージン分のパディングを持つ要素がある事に気が付きました。親要素にパティングを持たせておくことでウィンドウサイズからはみ出さないようにしているようです。

HTML

<div class="container">
  <div class="wrap">
    <div class="row">
      <div class="col col-half">
        <div class="content">content</div>
      </div>
      <div class="col col-half">
        <div class="content">content</div>
      </div>
    </div>
  </div>
</div>

CSS

.wrap
  padding-left: 15px
  padding-right: 15px

ネガティブマージンを持つ要素を囲み、囲った要素にネガティブマージン分のパディングをもたせればOKです。
f:id:kikiki-kiki:20170325223024p:plain
横スクロールは発生しませんが、.content.colの持つパディング分左右が小さくなります。
border-boxを使っているこの状態なら下記のような.rowに初めからネガティブマージンを持たせないものと同じ結果です。

*
  box-sizing: border-box
.container
  width: 100%
.row
  .col
    float: left
    padding-left: 15px
    padding-right: 15px
  .col-half
    width: 50%

解決方法2 ネガティブマージンを持つ要素の親にoverflow: hiddenを設定する

.colの持つパディング分を画面外に出し、.contentを画面の端一杯にしたいときは、ネガティブマージンをもつ.rowの親に幅指定と共にoverflow: hiddenを設定すればOKです。
HTML

<div class="container">
  <div class="row">
    <div class="col col-half">
      <div class="content">content</div>
    </div>
    <div class="col col-half">
      <div class="content">content</div>
    </div>
  </div>
</div>

CSS

*
  box-sizing: border-box
.container
  width: 100%
    overflow: hidden // <- 追加
.row
  margin-left: -15px
  margin-right: -15px
  .col
    float: left
    padding-left: 15px
    padding-right: 15px
  .col-half
    width: 50%

f:id:kikiki-kiki:20170325223042p:plain
図のように画面端から.contentのエリアになります。
overflow: hiddenを使用するのであまり上のレイヤーの要素にこの設定をしてしまうと他のデザインで苦労する自体が発生しかねないので注意が必要です。
 
しばらくCSSとか書いてなかったので予期しないレベルダウンを感じています…
今までこんな所に躓いたこと無かった気が…

ITがメインではない仕事に就いている(正確には仕事がない)とドンドンレベルダウンしていっているのを身にしみて感じていて、今迄できていたような事がカンタンに出来なくなるってのが凄まじいストレスと自己嫌悪になるので、都会に引越ししてエンジニア的なお仕事に再就職したい今日この頃なのでした…


[参考]

はじめてのCSS設計 フロントエンドエンジニアが教えるメンテナブルなCSS設計手法 (WEB Engineer’s Books)

はじめてのCSS設計 フロントエンドエンジニアが教えるメンテナブルなCSS設計手法 (WEB Engineer’s Books)

Google スプレッドシート nヵ月前の日付をチェックしたい

例えば、下記のような期限日が入力されているシートで期限まで3ヵ月切っているセルをハイライトしたいような時のメモ。

A B
1 案内日 期限(6ヵ月後月末)
2 2016/07/07 2017/01/31
3 2016/08/07 2017/02/28
4 2016/09/10 2017/03/31
5 2016/10/06 2017/04/30
6 2016/11/04 2017/05/31
7 2016/12/05 2017/06/30
8 2017/01/05 2017/07/31
9 2017/02/03 2017/08/31
10 2017/03/07 2017/09/30

判定条件

条件としては「3ヶ月前の月末 < 本日」で判定すれば良さそうです。
3ヶ月前の月末はEOMONTHを使うと取得することができます。

EOMONTH(開始日, 月数)
起算日から指定した月数だけ前または後ろの月の最終日の日付を返します。
EOMONTH - Docs Editors Help

EX

B5セルの「2017/04/30」の3ヵ月前の月末(2017/01/31)は次の式で求めることができます。

=EOMONTH(B5, -3)

この式を利用した条件式付き書式をせっていすれば良さそうです。

シートに条件式付き書式を設定する

  1. B部分をクリックしてB列全体を選択
  2. メニューの「表示形式」から「条件付き書式...」を選択。
  3. 条件式付き書式設定ルール のメニューが表示されるので
    セルの書式設定の条件を「カスタム数式」を選択。
  4. 値または数式入力欄に下記の数式を入力
    =EOMONTH($B:$B,-3)<TODAY()
  5. いい感じに書式設定のスタイルを指定して、完了ボタンをクリック

期限日が3ヶ月切っているセルが設定した書式の通りに表示されていればOKです。
f:id:kikiki-kiki:20170324165053p:plain

月数を変えたいときはEOMONTHの第二引数の値を変えればOK。
他にもEDATE関数などを使用すれば条件の日付を自由に計算することができます。
公式のドキュメントにいろんな関数が載っているので興味があればそちらを参照ください。


[参考]

ポケット百科 Googleサービス 知りたいことがズバッとわかる本

ポケット百科 Googleサービス 知りたいことがズバッとわかる本

CSS3 マークアップ別Flexboxで要素を左端・センター・右端揃えのレイアウトを作る

IE8以下のサポートも終わり自動アップデートになってきていますし、CSS3のFlexboxもそろそろ実制作で活用できる用になってきていると思っています。
f:id:kikiki-kiki:20170323200844p:plain
flex サポート状況

なので、下記画像のようなWEBサイトのグローバルナビゲーションとかでよくある例えばロゴ・ナビゲーション+ボタンの様な構成で、ロゴを左端、ナビゲーションをセンターにしてボタンを右端に置くデザインをFlexboxで作ってみようと思います。
f:id:kikiki-kiki:20170323201153p:plain ※いつもの事ながらIEではチェックしていません。

並べたい各要素が同じ階層のマークアップの場合

HTML

<header class="header">
  <div class="nav-wrapper">
    <p class="logo">LOGO</p>
    <nav class="navigation">
      <ul class="nav-list">
        <li class="nav-link"><a href="">LINK</a></li>
        ...
      </ul>
    </nav>
    <div class="nav-btn">
      <a class="btn">BUTTON</a>
    </div>
  </div>
</header>

Flexboxの揃え方 justify-content: space-between を使う方法

CSS

/* ▼ 共通部分 ▼ */
.header,
.logo,
.navigation
.nav-list,
.nav-btn
  box-sizing: border-box
.header
  width: 100%
.nav-list
  padding: 0 1em // 最小の左右のマージンを持たせておく
/* ▲ 共通部分 ▲ */
// Flex
.nav-wrapper
  display: flex
  flex-flow: row nowrap
  justify-content: space-between
  align-items: baseline

flexboxにjustify-content: space-between と指定すれば、ロゴ・ナビゲーション・ボタンの間隔が均等になるのでデザインが実現できます。

margin auto を利用する方法

CSS

/* 共通部分省略 */
// Flex
.nav-wrapper
  display: flex
  flex-flow: row nowrap
  // justify-content: space-start <- default値
  align-items: baseline
.navigation
  margin: 0 auto

親要素がflexの子要素でmargin autoを使用すると親要素の幅内でいい感じに配置してくれる様です。
ですので、センター揃えにしたいナビゲーションにmargin: 0 autoを与えると右マージンに押されてボタンが右端に配置されデザインを実現することができます。
このautoで付けたマージンもjustify-content: space-betweenの間隔と同じように可変なのでレスポンシブやリキッドレイアウトにも対応可能かと思います。

おまけ margin autoを利用するとロゴだけ左揃えにもできる

ナビゲーションとボタンがflexを親に持つ同じ子要素なので、ロゴを左揃え・ナビゲーション・ボタンを右揃えにしたい時にはjustify-content: space-betweenは使えませんので、margin: autoがいい感じに幅を取ってくれることを利用すると実現が可能となります。

/* 共通部分省略 */
// Flex
.nav-wrapper
  display: flex
  flex-flow: row nowrap
  justify-content: space-end // 右を基点にする (全体右寄せ)
  align-items: baseline
.logo
  margin-right: auto

ロゴにmargin-right: autoを与えるとロゴの右側に取れるだけのマージンが付くので、ロゴのみ左揃えになります。
f:id:kikiki-kiki:20170323203950p:plain
 

ナビゲーションと内にボタンがあるマークアップの場合

レスポンシブデザインでナビゲーションとボタンを同時に変化させたい時とかこの様なマークアップもありそうです。
HTML

<header class="header">
  <div class="nav-wrapper">
    <p class="logo">LOGO</p>
    <nav class="navigation">
      <ul class="nav-list">
        <li class="nav-link"><a href="">LINK</a></li>
        ...
      </ul>
      <div class="nav-btn">
        <a class="btn">BUTTON</a>
      </div>
    </nav>
  </div>
</header>

2段重ねのFlexboxで実現する

‘.navigation'内の子要素も横並びにするので、ここもdisplay: flexにし、センター揃えにしたいナビゲーション(.nav-list)で調整する事で、キレイに左端揃え・センター揃え・右端揃えのレイアウトを実現することができます。

/* 共通部分省略 */
// Flex1 .logo と .navigation の並び
.nav-wrapper
  display: flex
  flex-flow: row nowrap
  // justify-content: space-start <- default値
  align-items: baseline
// Flex2 .nav-list と .nav-btn の並び
.navigation
  display: flex
  flex-flow: row nowrap
  justify-content: center // 全体中揃え
  width: 100% // 残りの幅全体にする
  box-sizing: border-box // 必須
.nav4 .nav-list
  margin: 0 auto
  1. .logo.navigationflexにする
    f:id:kikiki-kiki:20170323210413p:plain
  2. widtth: 100%.navigationを残りの部分一杯の領域にする
    f:id:kikiki-kiki:20170323210420p:plain
  3. ナビゲーション(.nav-list)の左右のマージンをautoにして全体のセンターにする
    f:id:kikiki-kiki:20170323210430p:plain

 

ボタンがナビゲーションのリスト内にある場合

<header class="header">
  <div class="nav-wrapper">
    <p class="logo">LOGO</p>
    <nav class="navigation">
      <ul class="nav-list">
        <li class="nav-link"><a href="">LINK</a></li>
        ...
        <li class="nav-btn">
          <a class="btn">BUTTON</a>
        </li>
      </ul>
    </nav>
  </div>
</header>

このマークアップだと完璧にナビゲーションのリンク部分だけをheaderのセンターに揃える方法を見つけることができませんでした。

ボタンにマージンを%で設定して調整するパターン

/* 共通部分省略 */
.nav-wrapper
  display: flex
  flex-flow: row nowrap
  justify-content: space-betwee
  align-items: baseline
.navigation
  width: 100%
  box-sizing: border-box // 必須
.nav-list
  display: flex
  flex-flow: row wrap
  justify-content: flex-end // 右端を基点にする
  padding-right: 0
.nav-btn
  margin-left: 25% // 適当な値に設定

.logo.navigationjustify-content: space-between で等間隔にし、.nav-listflexにして子要素を並べ、リストの最後になるボタン(.nav-btn)の左マージンを適当な%で指定してそれっぽくすることができました。ボタンのマージンは親要素のn%なので、ブラウザの幅が狭いとロゴとの間隔が狭くなり、逆にブラウザの幅が広いとボタンとの間隔に対してロゴとの間隔が広くなります。
f:id:kikiki-kiki:20170323215701p:plain

ボタンをposition: absoluteで右端に固定するパターン

/* 共通部分省略 */
.nav-wrapper
  display: flex
  flex-flow: row nowrap
  justify-content: space-betwee
  align-items: baseline
  position: relative
.navigation
  display: flex
  flex-flow: row nowrap
  justify-content: space-around  // .logoとの間隔を作る
  width: 100%
  box-sizing: border-box // 必須
.nav-list
  display: flex
  flex-flow: row wrap
  // justify-content: space-start <- default値
  padding-right: 0
.nav-btn
  position: absolute
  right: 0

ナビゲーション内のリスト左揃えで始めボタンだけposition: absoluteで右端に固定しflex配置から外します。そして.navigationをFlexboxにしてjustify-content: space-aroundを指定するとロゴとの間隔もいい感じになります。しかし、absolute配置しているボタンの幅は無いものして計算されるのでブラウザの幅が狭くなるとナビゲーションのリストとボタンが重なってしまいますので、メディアクエリなどで小さくなった時のCSSを別途作成する必要がありそうです。
f:id:kikiki-kiki:20170323221203p:plain
 

サンプル

See the Pen Flex nav sample by KIKIKI (@chaika-design) on CodePen.

 

今までどうしてもIE対応があったりで、Bootstrapで流行ったfloatdisplay: tableを使ったCSSを書くことが多くFlexboxをちゃんと使いこなせていなかったので今回この記事をかいて少しFlexboxにも慣れてきた感じがしてきました。
コーディングしない期間が続くと書き方を忘れていたり、新しいやり方のキャッチアップが疎かになってしまうので気をつけなきゃな―っとしみじみ感じています。


[参考]