CSSでチェックボックスやラジオボタンをカスタマイズする 2024年版 ― 2024年05月24日 09時05分
HTMLのチェックボックス(<input type="checkbox">
)やラジオボタン(<input type="radio">
)をCSSで装飾したいというのはよく聞く話です。2024年現在は、HTMLの記述は簡単なまま、CSSで自由度の高い装飾も実現できるようになっています。
結論
単に色調を整えられればよいという場合は、accent-color
プロパティを使います。
input[type="checkbox"], input[type="radio"] {
accent-color: #d31;
}
accent-color
プロパティは継承するので、body
要素などに指定してその子孫のフォームコントロールに一律に適用することもできます。
自由に装飾したい場合は、input
要素にappearance: none
を指定したうえで、未チェック状態とチェック済み状態のスタイルを指定していきます。::before
、::after
疑似要素も使えます。
input[type="checkbox"] {
-webkit-appearance: none; /* Safari 15.3以下のため */
appearance: none;
border: thin solid;
}
input[type="checkbox"]::before {
content: '✔';
visibility: hidden;
}
input[type="checkbox"]:checked::before {
visibility: visible;
}
従来の手法
これまでチェックボックスやラジオボタンをCSSで装飾したい場合は、input
要素自身ではなくその直後に置いたspan
要素やlabel
要素を装飾するのが一般的でした。
<label>
<input type="checkbox">
<span>...</span>
</label>
<input id="the-radio-1" type="radio">
<label for="the-radio-1">...</label>
/* 組み込みのチェックボックスとラジオボタンを見えなくする */
input[type="checkbox"], input[type="radio"] {
position: absolute;
clip: rect(0, 0, 0, 0);
}
input[type="checkbox"] + span { /* 未チェック状態のスタイル */ }
input[type="checkbox"]:checked + span { /* チェック済み状態のスタイル */ }
input[type="radio"] + label { /* 未チェック状態のスタイル */ }
input[type="radio"]:checked + label { /* チェック済み状態のスタイル */ }
この手法は、スタイル指定のためだけのspan
要素が必要となる、あるいはinput
要素にid
属性を指定して明示的にlabel
要素と紐づける必要があるという点で、ひと手間かかります。また、アクセシビリティを確保するため、input
要素を見えなくするのにはdisplay: none
以外の手段を使う必要があります(display: none
を使うと、チェックボックスやラジオボタンをキーボードで操作できなくなります)。
appearance
プロパティを使う手法
appearance
プロパティを使う場合、HTML側ではlabel
要素とinput
要素の暗黙的な紐づけを使えます(明示的な紐づけを使っても構いません)。
<label>
<input type="checkbox">
...
</label>
CSS側ではappearance: none
を指定します。これにより、input
要素が組み込みのチェックボックスやラジオボタンではなく、単なる「内容が空の要素」として描画されることになります。<span></span>
と同じ扱いができるので、背景、ボーダー、影、アニメーション、それに::before
、::after
疑似要素といったCSSの各種機能をふんだんに使って装飾できます。
外枠の配置
appearance: none
を指定しただけでは内容が空なので何も表示されません。まずは幅と高さを指定してチェックボックスまたはラジオボタンの領域を確保しましょう。幅と高さの指定が確実に効くようにするため、display
プロパティにinline-block
、inline-grid
、inline-flex
などの値を指定する必要があります。
input[type="checkbox"] {
-webkit-appearance: none; /* Safari 15.3以下のため */
appearance: none;
display: inline-block;
width: 1.4em;
height: 1.4em;
}
そのままだとブラウザ組み込み(ユーザーエージェントスタイルシート)のスタイルが残ることがあるので、適宜初期化していきます。ブラウザごとの差異を吸収するため、マージンと背景色は常に指定したほうがよいでしょう。em
単位を使うのならfont-size
プロパティを、親要素と同じフォントや文字色(あるいはcurrentcolor
キーワード)を使うのならfont-family
プロパティやcolor
プロパティを、それぞれ継承させます。
input[type="checkbox"] {
...
margin: 0;
background-color: transparent;
color: inherit;
font: inherit;
}
また、気をつけておきたいのが行内での縦位置です。個人的には、チェックボックスおよびラジオボタンの縦中央と、ラベル文字列の縦中央とがそろったほうが収まりがよく感じられます(vertical-align: middle
は英小文字の縦中央とそろえるので、日本語文字の縦中央とは少しずれますが……)。
input[type="checkbox"] {
...
vertical-align: middle;
}
外枠の仕上げとしてボーダーを指定します。ラジオボタンの場合はborder-radius
プロパティで円形にするのがわかりやすいでしょう。ボーダーではなく背景画像やグラデーションで外枠を表現することもできます。後述する強制カラーモードへの対応のため、背景画像を使わないときはボーダーを指定することをお勧めします(border: thin solid transparent
といった透明のボーダーでも構いません)。
input[type="checkbox"], input[type="radio"] {
...
border: thin solid;
}
input[type="checkbox"], input[type="radio"] {
...
border-radius: 0.2em;
}
input[type="radio"] {
...
border-radius: 50%;
}
ここまでの指定を適用したのが以下の例です。
未チェックとチェック済みの切り替え
外枠ができたら次は内側、チェック状態の切り替えを表現していきましょう。::before
疑似要素でチェック済みを表す内容を指定しつつ最初は隠しておき、チェック済み状態(:checked
疑似クラス)になったらその内容を表示するというパターンが多いです。
以下の例では、ボーダーを使ってチェックボックスのチェックマークとラジオボタンの点を描画しています。ボーダーを使っているのは、後述する強制カラーモードでもチェックマークや点が表示されるようにするためです。また、内容を上下左右とも中央ぞろえするためにグリッドレイアウトを使っています。
input[type="checkbox"],
input[type="radio"] {
...
display: inline-grid;
place-content: center;
}
input[type="checkbox"]::before {
content: '';
width: 0.7em;
height: 0.3em;
border-left: 0.3em solid;
border-bottom: 0.2em solid;
transform: translateY(-0.1em) rotate(-40deg);
visibility: hidden;
}
input[type="checkbox"]:checked::before {
visibility: visible;
}
input[type="radio"]::before {
content: '';
border: 0.4em solid;
border-radius: 50%;
visibility: hidden;
}
input[type="radio"]:checked::before {
visibility: visible;
}
clip-path
プロパティを使って好きな形に切り抜いたり、背景画像やボーダー画像にグラデーションを使ったりと、自由度の高い表現が可能です。
input[type="checkbox"]::before {
content: '';
border: 0.5em solid;
clip-path: polygon(0% 60%, 0% 45%, 35% 65%, 100% 0%, 100% 15%, 45% 100%, 30% 100%);
visibility: hidden;
}
input[type="radio"]::before {
content: '';
width: 1em;
height: 1em;
border: 0.2em solid transparent;
border-radius: 50%;
padding: 0.2em;
background:
repeating-conic-gradient(from 45deg, #f33 0deg 90deg, #fc6 90deg 180deg) content-box,
repeating-conic-gradient(from 45deg, #fc6 0deg 90deg, #f33 90deg 180deg) padding-box,
repeating-conic-gradient(from 45deg, #f33 0deg 90deg, #fc6 90deg 180deg) border-box,
CanvasText;
visibility: hidden;
}
チェック状態の切り替え時に、フェードイン・フェードアウトや拡大・縮小といったアニメーション効果をつけるのもよいでしょう。
input[type="checkbox"]::before {
...
opacity: 0;
transition: opacity 0.5s ease-out;
}
input[type="checkbox"]:checked::before {
opacity: 1;
}
input[type="radio"]::before {
...
scale: 0;
transition: scale 0.5s ease-out;
}
input[type="radio"]:checked::before {
...
scale: 1;
}
強制カラーモードへの対応
強制カラーモードとは、テキストを読みやすくするために、背景色や文字色をコントラストのはっきりした色にする機能のことです。OSのアクセシビリティ機能の一環として提供されることが多く、Windowsなら「ハイコントラスト(Windows 10)」「コントラストテーマ(Windows 11)」という設定を有効にすることで、ブラウザで表示中の文書でも強制カラーモードが有効化されます。
強制カラーモードの主な影響は以下の通りです。詳しくはforced-colors
メディア特性を参照してください。
color
、background-color
、border-color
、outline-color
、accent-color
などは、OSやブラウザ側で決められた色に変更されます。text-shadow
、box-shadow
は無視されます。background-image: linear-gradient(...)
などのグラデーションの指定は無視されます。
一般的なテキストと画像に関しては、強制カラーモードを意識せずにスタイルを指定していても、最低限テキストが読める状態になることが多いです。しかし、カスタマイズしたチェックボックスやラジオボタンに関しては、気をつけないと全く表示されず、ユーザーがそれらフォームコントールの存在に気づかないという事態が起こりかねません。
チェック状態に関しても、チェックマークや点を単純にbackground-color: #d51
のような背景色だけで表現してしまうと、強制カラーモードではチェック済みかどうか視認できなくなってしまいます。前述のボーダーを使った表現や、または以下に挙げるような表現手法をとる必要があります。
透明なボーダーやアウトライン
背景部分に透明なボーダーやアウトラインを重ねる手法です。強制カラーモードが無効なときは背景がそのまま表示されますし、有効なときはボーダーやアウトラインが塗りつぶされて視認できます。背景部分にアウトラインを重ねるためには、outline-offset
プロパティの値に負数を指定してボックスの内側にアウトラインを配置します。
input[type="checkbox"]::before {
content: '';
border: 0.4em solid transparent;
background-color: #d31;
visibility: hidden;
}
input[type="radio"]::before {
content: '';
width: 0.8em;
height: 0.8em;
border-radius: 50%;
background-color: #d31;
outline: 0.4em solid transparent;
outline-offset: -0.4em;
visibility: hidden;
}
内向きの影や背景グラデーション
CanvasText
(文書の文字色を示す)といったシステム色は、強制カラーモードでも視認できる背景色として使えます。内向きの影(box-shadow: inset ...
)は背景の前面に描画されるため、強制カラーモードが無効なら影の色が表示されますが、有効だと背景色が表示されることになります。背景画像としてグラデーションを使った場合も、そのグラデーションは背景色の前面に描画されます。
input[type="checkbox"]::before {
content: '';
width: 0.8em;
height: 0.8em;
background-color: CanvasText;
box-shadow: inset 0 0 0 0.4em #d31;
visibility: hidden;
}
input[type="radio"]::before {
content: '';
width: 0.8em;
height: 0.8em;
border-radius: 50%;
background: linear-gradient(#d31 0% 100%), CanvasText;
visibility: hidden;
}
画像やテキスト
background-image: url(...)
やcontent: url(...)
で読み込んだ画像、およびcontent
プロパティで指定したテキストは、強制カラーモードでもそのまま表示されます。
input[type="checkbox"]::before {
content: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2010%2010%22%3E%3Cpath%20d%3D%22M%205%2C1%20L%209%2C5%205%2C9%201%2C5%20Z%22%20fill%3D%22%23fda%22%20stroke%3D%22%23f81%22%2F%3E%3C%2Fsvg%3E");
width: 1.2em;
height: 1.2em;
visibility: hidden;
}
input[type="radio"] {
...
display: inline-block;
text-align: center;
}
input[type="radio"]::before {
content: '';
display: block;
margin-top: -0.2em;
}
input[type="radio"]:checked::before {
content: '👉';
}
ブラウザ組み込みの外観
強制カラーモードではカスタマイズせずに、ブラウザ組み込みの外観を使うのもひとつの手です。forced-colors
メディア特性で強制カラーモードが有効かどうかを判別でき、all
プロパティとrevert
キーワードの組み合わせでスタイル指定を撤回できます。
@media (forced-colors: active) {
input[type="checkbox"],
input[type="checkbox"]::before,
input[type="radio"],
input[type="radio"]::before {
/* 疑似クラスに指定されたスタイル(後述)も撤回したいので!importを指定する。 */
all: revert !important;
}
}
ここまで書いておいてなんですが、筆者は強制カラーモードを常用しておらず、強制カラーモードでチェックボックスやラジオボタンをカスタマイズしてもよいのかしないほうがよいのかのベストプラクティスを持ち合わせていません。ご存じの方は教えていただけると幸いです。
状態に応じたスタイルの指定
以上で最低限の表示切替ができましたが、ほかにも状態に応じたスタイルを指定できます。
よく使うのはマウスホバー時を表す:hover
疑似クラスでしょう。フォームコントロールに対する:hover
疑似クラスは、そのフォームコントロールに紐づけられたlabel
要素にマウスホバーしているときにも適用されます。
次の例では:hover
疑似クラスと:enabled
疑似クラスを組み合わせています。:hover
疑似クラスを使うのは「今まさにユーザーが操作できる対象を強調したい」という意図であり、無効状態の(操作できない)フォームコントロールに適用してしまうとその意図から外れてしまうからです。
input[type="checkbox"]:enabled:hover,
input[type="radio"]:enabled:hover {
box-shadow: 0 0 0.3em 0.1em #d31;
}
多くのブラウザでは、チェックボックスやラジオボタンにキーボード(Tabキー)でフォーカスしたときに、フォーカスリングが表示されます。このフォーカスリングの表示を変更したいときには:focus-visible
疑似クラスを使います。その際には「アクセシビリティの考慮」の項やWCAG 2.1の達成基準1.4.11「非テキストのコントラスト」の解説も参考に、「今まさにユーザーが操作できる対象」が伝わるようにしてください。
以下の例では、影を使ってフォーカスリングを表現しつつ、強制カラーモードに備えて透明のアウトラインを設定しています。単にoutline-color: transparent
を指定するだけ(outline-style: auto
のまま)だとブラウザによってはアウトラインが見えてしまうことがあるので、outline-style: solid
にしています。
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible {
box-shadow: 0 0 0.3em 0.1em #d31;
outline: medium solid transparent;
}
無効状態の(disabled
属性が付与された)チェックボックスやラジオボタンを提供するのなら、:disabled
疑似クラスを使います。次の例ではfilter
プロパティを使って無彩色にしたうえで、opacity
プロパティで色を薄くしています。強制カラーモードで背景が明色になろうと暗色になろうと、半透明なら色が薄くなることに変わりありません。
input[type="checkbox"]:disabled,
input[type="radio"]:disabled {
border-style: dashed;
filter: grayscale(1);
opacity: 0.6;
}
チェックボックスには未チェックともチェック済みとも言えない未決定状態が存在します。ただし、未決定状態になるのはJavaScriptでinput
要素オブジェクトのindeterminate
プロパティを設定したときだけです。そのようなJavaScriptの記述がなければ、未決定状態に対するスタイルを指定する必要はありません。そのような記述があるのなら、:indeterminate
疑似クラスを使ってスタイルを指定します。
以下の例では「くだもの」グループに属すチェックボックスの一部のみがチェックされているときに、「くだもの」チェックボックスを未決定状態にしています。
input[type="checkbox"]:indeterminate::before {
width: 0.5em;
height: 0.5em;
border: 0.2em dotted;
transform: none;
visibility: visible;
}
参考文献
チェックボックスとラジボタンのカスタマイズについて:
-
Xユーザーのしゃろさん: 「<input type="radio"> や <input type="radio"> を独自にスタイリングするために、次の3つを使用していた。 - いわゆる visually hidden なスタイル - 隣接する label/div/span 要素、または、それらの疑似要素 ::before/::after - 隣接セレクタ + と擬似クラス :checked」 / X
この記事を書くきっかけとなったツイート。返信中に記載された例では、行内での縦位置を揃えるためlabel
要素にdisplay: inline-flex
を指定している。 -
Pure CSS Custom Styled Radio Buttons | Modern CSS Solutions
ラジオボタンの装飾方法について。強制カラーモードへの対応や:focus-within
疑似クラスを用いたラベルのスタイル指定など。 -
Pure CSS Custom Checkbox Style | Modern CSS Solutions
チェックボックスの装飾方法について。clip-path
プロパティを用いたチェックマークの作成など。 -
One last time: custom styling radio buttons and checkboxes | scottohara.me
チェックボックスとラジオボタンの装飾方法について。双方向書字やダークモードなどでの確認を勧める。 -
Custom Styling Form Inputs With Modern CSS Features | CSS-Tricks - CSS-Tricks
チェックボックスとラジオボタンの装飾方法について。トグルスイッチの例など。
強制カラーモードについて:
-
アクセシビリティを高めるための、コントラストテーマの基本対応 | 株式会社ヌーラボ(Nulab inc.)
WindowsのコントラストテーマとBacklogでの対応事例。
CSSのconic-gradientで直線的な模様を作る ― 2022年12月19日 11時11分
この記事はCSS Advent Calendar 2022の19日目の分です。
CSSのconic-gradient
(扇形グラデーション)関数を使うと、円グラフや集中線のような表現ができます。ここであえて扇形の中心角の部分に注目し、直線的な模様を作ってみることはできないでしょうか? いくつか試してみました。
例
例1. 二重のボーダー
「二重のボーダー」のCSSコード
p {
--outer-border-width: 1em;
--inner-border-width: 1em;
--outer-border-top-left-color: #1bf;
--inner-border-top-left-color: #35b;
--inner-border-bottom-right-color: #dc6;
--outer-border-bottom-right-color: #3a8;
margin: 0;
padding: 1em;
border: calc(var(--outer-border-width) + var(--inner-border-width)) solid transparent;
background:
conic-gradient(from 180deg at var(--outer-border-width) var(--outer-border-width), var(--outer-border-top-left-color) 270deg, transparent 270deg) no-repeat border-box 0 0 / calc(100% - var(--outer-border-width)) 100%,
conic-gradient(from 180deg at var(--inner-border-width) var(--inner-border-width), var(--inner-border-top-left-color) 270deg, transparent 270deg) no-repeat border-box var(--outer-border-width) var(--outer-border-width) / calc(100% - 2 * var(--outer-border-width) - var(--inner-border-width)) calc(100% - 2 * var(--outer-border-width)),
conic-gradient(from 0deg at calc(100% - var(--inner-border-width)) calc(100% - var(--inner-border-width)), var(--inner-border-bottom-right-color) 270deg, transparent 270deg) no-repeat border-box calc(var(--outer-border-width) + var(--inner-border-width)) var(--outer-border-width) / calc(100% - 2 * var(--outer-border-width) - var(--inner-border-width)) calc(100% - 2 * var(--outer-border-width)),
conic-gradient(from 0deg at calc(100% - var(--outer-border-width)) calc(100% - var(--outer-border-width)), var(--outer-border-bottom-right-color) 270deg, transparent 270deg) no-repeat border-box var(--outer-border-width) 0 / calc(100% - var(--outer-border-width)) 100%;
}
例2. 矢印
「矢印」のCSSコード
p {
--arrow-bg-color: #aaa;
--arrow-color: #ff5;
--arrow-angle: 120deg;
--arrow-from-angle: calc(180deg + var(--arrow-angle) / 2);
--arrow-outer-angle: calc(360deg - var(--arrow-angle));
--arrow-margin-left: 1em;
--arrow-width: 2em;
--arrow-center: calc(var(--arrow-width) / 2);
--arrow-offset: 1em;
margin: 0;
padding: 1em;
border: solid transparent;
border-width: 0 0 0 calc(var(--arrow-margin-left) + var(--arrow-width));
border-radius: 0.5em;
background: #ddd no-repeat border-box var(--arrow-margin-left) 0 / var(--arrow-width) 100%;
background-image:
conic-gradient(from var(--arrow-from-angle) at var(--arrow-center) calc(1 * var(--arrow-offset)), var(--arrow-bg-color) var(--arrow-outer-angle), transparent var(--arrow-outer-angle)),
conic-gradient(from var(--arrow-from-angle) at var(--arrow-center) calc(2 * var(--arrow-offset)), var(--arrow-color) var(--arrow-outer-angle), transparent var(--arrow-outer-angle)),
conic-gradient(from var(--arrow-from-angle) at var(--arrow-center) calc(3 * var(--arrow-offset)), var(--arrow-bg-color) var(--arrow-outer-angle), transparent var(--arrow-outer-angle)),
conic-gradient(from var(--arrow-from-angle) at var(--arrow-center) calc(4 * var(--arrow-offset)), var(--arrow-color) var(--arrow-outer-angle), var(--arrow-bg-color) var(--arrow-outer-angle));
}
例3. 星形
「星形」のCSSコード
p {
--outer-block-angle: 6deg;
--outer-inline-angle: 22deg;
--cone-angle: calc(90deg - var(--outer-block-angle) - var(--outer-inline-angle));
--cone-top-left-color: #bde;
--cone-top-right-color: #cae;
--cone-bottom-right-color: #ecc;
--cone-bottom-left-color: #ffb;
margin: 0;
padding: 2em 3em;
background:
conic-gradient(from calc(90deg + var(--outer-block-angle)) at 0 0, var(--cone-top-left-color) var(--cone-angle), transparent var(--cone-angle)) no-repeat 0 0 / 50% 50%,
conic-gradient(from calc(180deg + var(--outer-inline-angle)) at 100% 0, var(--cone-top-right-color) var(--cone-angle), transparent var(--cone-angle)) no-repeat 100% 0 / 50% 50%,
conic-gradient(from calc(270deg + var(--outer-block-angle)) at 100% 100%, var(--cone-bottom-right-color) var(--cone-angle), transparent var(--cone-angle)) no-repeat 100% 100% / 50% 50%,
conic-gradient(from var(--outer-inline-angle) at 0 100%, var(--cone-bottom-left-color) var(--cone-angle), transparent var(--cone-angle)) no-repeat 0 100% / 50% 50%;
}
例4. 吹き出し
「吹き出し」のCSSコード
p {
--bg-color: #fed;
--padding: 1em;
--border-width: 0.5em;
--border-color: #f93;
--border-radius: 1em;
--balloon-tail-angle: 60deg;
--balloon-tail-from-angle: calc(90deg - var(--balloon-tail-angle) / 2);
--balloon-tail-width: 2em;
--balloon-tail-height: calc(1.15470054 * var(--balloon-tail-width)); /* 1.15470054 = 2 / sqrt(3) */
--balloon-tail-bottom-offset: 1em;
margin: 0 0 0 var(--balloon-tail-width);
padding: var(--padding);
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
background: var(--bg-color);
}
p::after {
content: '';
position: absolute;
display: block;
width: calc(var(--balloon-tail-width) + var(--border-width));
height: var(--balloon-tail-height);
background:
conic-gradient(from var(--balloon-tail-from-angle) at calc(2 * var(--border-width)) calc(var(--balloon-tail-height) / 2), var(--bg-color) var(--balloon-tail-angle), transparent var(--balloon-tail-angle)) no-repeat,
conic-gradient(from var(--balloon-tail-from-angle) at 0 calc(var(--balloon-tail-height) / 2), var(--border-color) var(--balloon-tail-angle), transparent var(--balloon-tail-angle)) no-repeat;
transform: translate(calc(0em - var(--padding) - var(--balloon-tail-width) - var(--border-width)), calc(var(--padding) + var(--border-width) - var(--border-radius) - var(--balloon-tail-height) - var(--balloon-tail-bottom-offset)));
}
感想
やってはみたもののどうも微妙です。直線的な模様ならだいたいはlinear-gradient
関数で表現できますし、要素を切り抜くのならclip-path
プロパティのほうが自由度が高いです。conic-gradient
関数を使えば記述量を減らせるかというと、そういうこともそんなになく、やはり目的外の利用はあまりうまくいかないのかもしれません。
今回ひとつ得られた教訓は、背景画像を使って疑似的にボーダーを表現する場合、疑似的なボーダーと同じ大きさの透明なボーダー(border: <border-wdith> solid transparent
)を指定したほうがよいということです。そうすることで、overflow: auto
で内容がはみ出したときなどにスクロール領域が疑似的なボーダーにかからず、より自然に見えます。
CSSの絶対配置の要素の静的位置矩形 ― 2022年12月11日 12時52分
この記事はCSS Advent Calendar 2022の11日目の分です。
CSSでpositoin: absolute
(絶対配置)の要素の位置を指定するときには、top
、left
、right
、bottom
プロパティ(またはこれらを一括指定するinset
プロパティ)がよく使われます。もし絶対配置の要素にそれらのプロパティが指定されていなかったら、その要素の位置はどこになるでしょうか?
その場合、絶対配置の要素は原則として「その要素がpositoin: static
(静的配置)だった場合の位置(静的位置矩形; static-position rectangle)」に置かれます。絶対配置の要素の親要素がインライン要素だった場合、絶対配置の要素自身がdisplay: inline
(インライン要素)かdisplay: block
(ブロック要素)かによって位置が異なってくることになります(インライン要素の子要素が絶対配置のときのデモ)。
この挙動をうまく利用すれば、絶対配置の要素の親要素にposition: relative
をつけて回らなくても、絶対配置の要素を期待する位置に置けることがあります。その場合、細かな位置の調整にinset
プロパティなどを使うことはできないので、transform: translate(...)
やmargin
プロパティを使うことになります。
フレックスアイテムが絶対配置のとき
「原則として」というからには例外もあります。絶対配置のフレックスアイテムでinset
プロパティなどが指定されていないものは、フレックスコンテナの位置に置かれます。
(「フレックスコンテナ」はdisplay: flex
またはdisplay: inline-flex
が指定された要素、「フレックスアイテム」はフレックスコンテナの子要素です。)
グリッドアイテムが絶対配置のとき
グリッドアイテムが絶対配置のときはちょっと複雑です。グリッドコンテナが静的配置なら、絶対配置のグリッドアイテムでinset
プロパティなどが指定されていないものは、グリッドコンテナの位置に置かれます。
グリッドコンテナが静的配置でないなら、絶対配置のグリッドアイテムでinset
プロパティが指定されていないものは、grid
プロパティなどで指定されたグリッド領域の位置に置かれます(グリッドアイテムが絶対配置のときのデモ)。
(「グリッドコンテナ」はdisplay: grid
またはdisplay: inline-grid
が指定された要素、「グリッドアイテム」はグリッドコンテナの子要素です。)
とはいえ、フレックスボックスやグリッドと絶対配置を組み合わせるとCSSのコードがだいぶ複雑になるので、普段は避けたほうがよいと思います。
CSSでスクロールバーの有無によるがたつきをなくす ― 2022年12月09日 09時42分
この記事はCSS Advent Calendar 2022の9日目の分です。
CSSのボックスモデルにおいては、ボーダーの内辺とパディングの外辺の間にスクロールバーが配置されます。最近はどのOSでもオーバーレイスクロールバー(スクロールバーが内容の前面に覆いかぶさるようなもの)が主流となり、スクロールバーが存在してもしなくても内容の幅が変わらないようになっています。一方、クラシックスクロールバー(スクロールバーが常に表示されるようなもの)が使われる環境では、overflow: auto
な要素において内容がはみ出すときとはみ出さないときで内容の幅が変わってきます。
クラシックスクロールバーが使われる環境でも内容の幅を一定に保ちたいという場合は、scrollbar-gutter
プロパティを使います。scrollbar-gutter: stable
を指定すれば、スクロールバーが表示されないときでもスクロールバーと同じだけの領域が確保され、内容の幅はその分狭くなります。左右中央に配置したいのにスクロールバーの領域の分だけずれて困るというときは、scrollbar-gutter: stable both-edges
を指定することで、左右どちらにもスクロールバーと同じだけの領域が確保されます。
scrollbar-gutter
プロパティはoverflow: hidden
な要素にも適用されますが、overflow: visible
な要素には適用されません。また、scrollbar-gutter
プロパティの効果は、縦スクロールバー(縦書きなら横スクロールバー)にのみ影響します。
オーバーレイスクロールバーが使われる環境では、scrollbar-gutter: stable
の効果はありません。
使用事例
使用事例としては、@about_hiroppyさんの紹介している「HTMLのdialog
要素によるモーダルダイアログが開いているときに、背景の文書をスクロールさせない」というのが考えられます(scrollbar-gutter
プロパティを指定した例と指定しなかった例)。
html {
scrollbar-gutter: stable;
}
html:has(dialog:modal) {
overflow: hidden;
}
クラシックスクロールバーが使われる環境では、文書をスクロールさせないためにoverflow: hidden
を指定すると、スクロールバーが消えて文書の内容ががたついてしまいます。scrollbar-gutter: stable
をあらかじめ指定しておけば、スクロールバーが消えてもその分の領域が確保され続けるので、文書の内容ががたつきません。
CSSでモーダルダイアログの背景をスクロールさせないようにできるかもしれない ― 2022年12月01日 01時41分
この記事はCSS Advent Calendar 2022の1日目の分です。
HTMLのdialog
要素を使うとモーダルダイアログを表現できます(使い方によってはモードレスダイアログも表現できます)。ただし、そのままだとモーダルダイアログを開いているときに、マウスホイールなどによってダイアログの背景(文書全体)までスクロールしてしまいます。
モーダルダイアログの背景をスクロールさせたくない場合、これを書いている現在のCSS仕様草案によれば、以下の記述で実現できるはずです(デモ)。
dialog {
overscroll-behavior: contain;
}
しかしながら、この方法はChrome canary 110では期待通り動作しますがマウスホイールによるスクロールは防げますが、矢印キーやPageUp/PageDownキーによるスクロールは防げず、Firefox nightly 109では動作しません。
このあたりの事情はちょっと複雑で、
- CSS仕様草案によれば、
overflow: auto
な要素においてはスクロール可能な領域がないときもoverscroll-behavior
プロパティが適用されるはず。- CSS Overscroll Behaviorモジュール草案によれば、スクロールコンテナに対して
overscroll-behavior
プロパティが適用される。 - CSS Overflowモジュール草案によれば、
overflow: auto
な要素(およびoverflow: hidden
な要素)はスクロール可能な領域がないときもスクロールコンテナである。
- CSS Overscroll Behaviorモジュール草案によれば、スクロールコンテナに対して
- 各ブラウザ(少なくともChromeの場合とFirefoxの場合)は、あえて仕様草案を無視し、スクロール可能な領域がないときに
overscroll-behavior
プロパティを適用しない。- しかし、Chromeはなぜか、
dialog
要素においてはスクロール可能な領域がないときもマウスホイールによるスクロールに関してはoverscroll-behavior
プロパティを適用する模様(そうなっている経緯は未調査)。
- しかし、Chromeはなぜか、
- CSS仕様草案に対して、スクロール可能な領域がないときは
overscroll-behavior
プロパティが適用されないようにしよう(仕様側を現状のブラウザ実装に合わせよう)という提案がなされている。
となっています。上述のCSSコードが将来的にも機能するかどうかは不透明です。
overflow: hidden
を使ってスクロールを防止する方法でも、:has
疑似クラスと組み合わせることで、CSSのみでモーダルダイアログの背景のスクロールを防止できるそうです。
最近のコメント