メディアクエリに依存した要素の表示切り替えを `display: contents` でユーティリティコンポーネント化
Posted: Updated:
画面幅、メディアクエリに依存した要素の表示切り替え
- React + CSS Modules を使っている前提イメージ
- JSX 上で表示・非表示の分岐が明示されてほしい
- CSS を掘らないと分からないのは見通しが悪く感じる
matchMedia()
ベースの Hooks にすると SSR で難儀する- 一貫性のためにサーバーサイドコードで頑張るのも億劫である
- 表示・非表示だけなら純粋な CSS で実現したい
display: contents
を使ってみた
メディアクエリで display: none と block を切り替えれば良いという単純な話ではなく、親要素が Flexbox や Grid だった場合を想定する必要があるので、表示されている状態ではボックスモデル的に虚無になってほしい。
contents
これらの要素は自身のために特定のボックスを生成しません。擬似ボックスやその子ボックスで置き換えられます。なお、 CSS >Display Level 3 仕様書では、 contents の値が「普通ではない要素」 — 置換要素のように、 CSS ボックスの純粋な概念に従って表示されない要素に影響する方法を定義しています。 display - CSS: カスケーディングスタイルシート | MDN
ということで思いだしたのが表題の通り display: contents
であり、いつの間にか使っても良さそうなフェーズになっていたので採用。
サンプルコード
JSX
import styles from './WhenVisible.module.css';
import type { PropsWithChildren } from 'react';
type WhenVisibleProps = {
mobile?: boolean;
tablet?: boolean;
desktop?: boolean;
};
export const WhenVisible = ({
mobile = false,
tablet = false,
desktop = false,
children,
}: PropsWithChildren<WhenVisibleProps>) => {
const classNames = [
styles.container,
mobile ? styles.mobile : '',
tablet ? styles.tablet : '',
desktop ? styles.desktop : '',
].join(' ');
return <div className={classNames}>{children}</div>;
};
CSS
.container {
display: none;
}
/* @custom-media --desktop-viewport */
@media screen and (min-width: 1040px) {
.desktop {
display: contents;
}
}
/* @custom-media --tablet-viewport */
@media screen and (min-width: 601px) and (max-width: 1039px) {
.tablet {
display: contents;
}
}
/* @custom-media --mobile-viewport */
@media screen and (max-width: 600px) {
.mobile {
display: contents;
}
}
Usage
export const Header = () => {
return (
<header>
<WhenVisible desktop>
<HeaderDesktop />
</WhenVisible>
<WhenVisible mobile tablet>
<HeaderMobile />
</WhenVisible>
</header>
);
};
a11y Issues?
display: contents 大部分のブラウザーの現在の実装では、アクセシビリティツリーから display の値が contents であるすべての要素を削除します (ただし子孫は残ります)。これにより、その要素自身は読み上げソフトでは読み上げられなくなります。これは CSS 仕様書によれば正しくありません。 display - CSS: カスケーディングスタイルシート | MDN
とありますが子孫 (children
) が残るなら、今回のユースケースであれば実用上の問題はないと判断。
リハビリ記事でした
久々に自分でまとまった量の CSS を書いてまして。 以前も CSS 〜デザイン得意パーソンにお任せ気味だったので、ハンズオンで触れると隔世の感がある... c⌒っ.ω.)っ