Wiz テックブログ

Wizは、最新のIoTやICTサービスをお客様に届ける「ITの総合商社」です。

知ってると得するかもしれないCSS(Sass)のニッチなテクニック集

f:id:wiz_sasaki:20210927144411j:plain

皆様こんにちは、フロントエンドエンジニアの佐々木です。

今回は私が実際に実務で使用しているCSS(Sass)のちょっとしたテクニック集を紹介しようと思います。

否定疑似クラス「:not()」を利用した余白のとり方

:not():first-childを指定することで、最初の要素以外という指定ができます。

<ul class="list">
  <li class="list-item"></li>
  <li class="list-item"></li>
  <li class="list-item"></li>
  <li class="list-item"></li>
</ul>
.list-item {
  &:not(:first-child) {
    margin-top: 16px;
  }
}

これの何が良いのかというと、リストのようなレイアウトは最初もしくは最後の余白を0にしたいことがほとんどだと思います(他の要素への影響を最小限にするため)。 .list-itemに直接margin-topをとってしまうと.list-item:first-childmargin-topを打ち消す記述をしないといけません。:not()を使用することで不要な記述を減らすことができます。

「&」を利用した連続する同じ要素間の余白

ネスト内で& + & とすることで次の要素が同じだったらという指定ができます(ここでは.l-section)。

<section class="l-section">
  <h2>セクション1</h2>
</section>

<section class="l-section">
  <h2>セクション2</h2>
</section>

<section class="l-section">
  <h2>セクション3</h2>
</section>
.l-section {
  & + & {
    margin-top: 50px;
  }
}

先程の&:not(:first-child)に似ていますが、要素の間に別の要素が入って来た時の挙動が微妙に違います。

注意点としてこの方法はネストの親でのみ使用可能です。

ちなみに以下と同じ意味です。

.l-section {
  + .l-section {
    margin-top: 50px;
  }
}

詳細度を無理やり上げる

CSSには詳細度というのもが存在します。上書きするにはその詳細度を上回る必要があります。 !importantを使えば簡単に上書き可能ですが、宗教上の理由で!importantを使えない方がほとんどだと思います。以下はその代替方法です。

CSS詳細度計算サイト : https://specificity.keegan.st/

<div class="content">
  <p class="text">この文字の色は赤です。</p>
</div>
//詳細度 : 1, 1, 0
.text {
  &:not(#_) {
    color: red;
  }
}

//詳細度 : 0, 1, 0
.text {
  color: blue;
}

//詳細度 : 0, 2, 0
.content {
  .text {
    color: blue;
  }
}

上記のように&:not(#_)とすることで、.text#_ではないという指定になります。こうすることでIDを指定した時と同等の詳細度を得ることができます。

この方法のメリットは!importantを使用せずに高い詳細度を得ることにあります。AMPのような!importantが使用するとエラーが出る環境だと使う可能性があります。

可読性はあまり良くないので、そもそもこれを使わなくて済むようなCSS設計を心がけたほうが良いです。

他に有名な詳細度を上げる方法として:not(:root)という方法も存在します。

画像の比率を固定する

様々なデバイスに対応しないといけない現在のWEB制作において画像の比率を維持するということは非常に重要なことです。以下はpaddingの仕様を利用した画像の比率維持方法です。

ここでの画像は1920px * 1080pxを想定しています。

<div class="image-wrapper">
    <img src="/path/to/image.jpg" width="1920" height="1080" alt="比率固定したい画像">
</div>
.image-wrapper {
  position: relative;
  &::before {
    content: '';
    display: block;
    padding-top: calc((1080 / 1920) * 100%);
  }
  img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
}

上記のCSSで1920px * 1080pxの比率は画面の幅が変わっても維持されます。

ここで重要なのは疑似要素::beforepadding-topです。padding-topは%で指定するとその値は親要素の幅に対しての割合となります。

以下の計算式に当てはめると幅に対する高さの比率を求めることができます

(高さ / 幅) * 100%

これを利用して疑似要素で高さをとり、imgをabsoluteで浮かせて配置することで画像の比率の維持が可能です。

しかし、非常に可読性が悪くなるのでこういった面倒な計算をするものはmixin化すると使い勝手がよくなります。

@mixin aspect-ratio($width, $height, $first: true) {
  position: relative;
 
  &::before {
    content: '';
    display: block;
    padding-top: ($height / $width) * 100%;
  }

  @if $first == true {
    & > :first-child {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }
  }
}
@include aspect-ratio(1920, 1080);

親要素を突き破って横幅100%

背景のみ横幅100%にして色をつけたいときなどに役に立ちます。

margin-right: calc(50% - 50vw);
margin-left: calc(50% - 50vw);

padding-right: calc(50vw - 50%);
padding-left: calc(50vw - 50%);

ポイントはpaddingで内要素の幅を保っていることです。ですのでpadding削除すると完全に横幅100%になります。

状況に応じて使い分けていくと良いでしょう。

否定形のメディアクエリ

not演算子を使うとメディアクエリの内容を反転することができます。

例えば、767px以下768以上のスタイルを書きたい場合があるとします。

.text { 
  @media screen and (min-width: 768px) { 
    color: red; 
  } 
}
.text { 
  @media screen and (max-width: 767px) { 
    color: blue; 
  } 
}

このように書きたくなりますが、これは特定の条件でスタイルが当たりません。

特定の条件とは768px〜767pxの間である画面幅が767.5pxのなどの場合はスタイルが当たりません。これはブラウザの拡大率や画面の解像度の組み合わせによって起こる可能があります。

.text { 
  @media screen and (min-width: 768px) { 
    color: red; 
  } 
}
.text { 
  @media not screen and (min-width: 768px) { 
    color: blue; 
  } 
}

上記のようにメディアクエリにnot演算子を組み合わせることによって画面幅が768pxより小さい場合という指定が可能です。

つまり画面幅が767.5pxの場合でもスタイルが当たります。

メディアクエリについての詳しい解説はコチラ

ユーザビリティを考慮したコンテンツの非表示方法

以下のように実装するとテキストは見えないけどスクリーンリーダーは読み上げ可能を実現できます。

<p class="visually-hidden">このテキストは見えないけどスクリーンリーダーは読み上げます。</p>
.visually-hidden {
  position: absolute;
  white-space: nowrap;
  width: 1px;
  height: 1px;
  overflow: hidden;
  border: 0;
  padding: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%); 
  margin: -1px;
}

詳しくは下記サイトのHiding content visuallyの箇所をご確認ください。

https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939

他のメリットとしてはdisplay: noneではないのでフォーカスが可能な点が挙げられます。

最後に

今回は「CSSのニッチなテクニック集」という内容でパッと思いついたものを紹介してみました。知っている人は「なんだそんなことか」と思われる内容だったかもしれません。

CSSはかなり自由に書けてしまうのでオレオレコーディングになりがちです。特に:not()を利用したものは便利な反面、知らない人は「ん?」ってなることが多いです。

他の人もCSSを触ることを考慮して複雑な内容ほどコメントを残しておくと良いでしょう。

それでは今回は以上となります。この記事が少しでも誰かの役の立てればいいなと思います。

また、

Wizではエンジニアを募集しております。

興味のある方、ぜひご覧下さい。

careers.012grp.co.jp