Safari 27(執筆時点の最新メジャー)では、CSSの新機能がいくつも先行実装されました。中にはChromeやFirefoxではまだ使えないものもあり、「便利そうだけど本番で使っていいの?」と迷うところです。この記事はそんなSafari先行CSSのカタログです。合言葉は @supports で検出して、安全に取り込むこと。対応ブラウザでは新しい書き方、未対応ブラウザでは従来の書き方にフォールバックする二段構えにすれば、執筆時点でChrome未対応の機能も今日から使えます。ここでは実務で出番の多い4機能に絞って、書き方と落としどころをまとめます。
:heading — h1〜h6列挙からの解放
見出し全部に同じスタイルを付けたいとき、h1, h2, h3, h4, h5, h6 と6つ列挙するのが定番でした。:heading 疑似クラスはそのショートハンドです。記事本文やドキュメント系のベーススタイルが一段と短く書けます。執筆時点ではSafari 27のみの対応とされ、Chrome・Firefoxは未対応です(詳細は caniuse: :heading を参照)。使うときは @supports selector(:heading) で検出するのが確実です。
/* Before: 6つ列挙する従来の書き方(全ブラウザ対応) */
.prose h1,
.prose h2,
.prose h3,
.prose h4,
.prose h5,
.prose h6 {
line-height: 1.4;
letter-spacing: 0.02em;
text-wrap: balance;
}
/* After: Safari 27 では :heading に置き換え */
@supports selector(:heading) {
.prose :heading {
line-height: 1.4;
letter-spacing: 0.02em;
text-wrap: balance;
}
}/* 応用: 特定レベルだけ除外する例 */
@supports selector(:heading) {
/* h1 以外の見出しにだけマージンを付ける */
.prose :heading:not(h1) {
margin-top: 2em;
}
}
/* 未対応ブラウザ向けフォールバック */
@supports not selector(:heading) {
.prose h2,
.prose h3,
.prose h4,
.prose h5,
.prose h6 {
margin-top: 2em;
}
}
@supports selector(...)はセレクタ対応の検出専用構文です。@supports (:heading)のようにプロパティ判定と混同しないこと。否定形は@supports not selector(:heading)と書けます。従来セレクタを外側に残したまま内側だけ上書きすれば、未対応ブラウザでも見た目が崩れません。
下のデモを「実行」すると、読者のブラウザが :heading に対応しているか自動判定します。対応時は3つの見出しとも青バー(:heading 指定)、未対応時は灰バー(従来セレクタのフォールバック)になります。
<div id="hd1-wrap" style="font-family:sans-serif;">
<p id="hd1-status" style="font-size:13px;padding:8px 12px;border-radius:6px;background:#f0f3f5;">判定中…</p>
<div class="hd1-prose" style="margin-top:12px;">
<h1 style="font-size:20px;margin:8px 0;">大見出し</h1>
<h2 style="font-size:17px;margin:8px 0;">中見出し</h2>
<h3 style="font-size:15px;margin:8px 0;">小見出し</h3>
<p style="font-size:13px;color:#555;">対応時は青バー、未対応時は灰バーになります。</p>
</div>
</div>
<style>
.hd1-prose h1, .hd1-prose h2, .hd1-prose h3 {
border-left: 6px solid #9fb0c3;
padding-left: 8px;
}
@supports selector(:heading) {
.hd1-prose :heading {
border-left-color: #3b5ba9;
}
}
</style>
<script>
(() => {
let ok = false;
try {
ok = document.querySelector(':heading') !== null;
} catch (err) {
ok = false;
}
const st = document.getElementById('hd1-status');
st.textContent = ok
? ':heading に対応:青バーが :heading 指定の証拠'
: ':heading 未対応:灰バー(フォールバック表示)が正解';
st.style.background = ok ? '#e4eaf4' : '#fdf0e6';
})();
</script>color-mix()複数色 + alpha()相対カラー
Safari 27では color-mix() の複数色・比率指定と、相対カラー関数 alpha() の扱いが強化され、パレット生成をCSSだけで閉じられるようになりました(執筆時点のChromeの挙動とは差があるため、使う場合は手元での確認を推奨します)。ボタンのホバー色やグラデーションの中間色のためにSassの mix() / transparentize() を持ち出す必要がなくなり、デザイントークンとの相性も良くなります。
:root {
--brand: #3b5ba9;
--accent: #4fa3c4;
}
.card {
/* 3色を比率付きで混色: ベース7割・アクセント2割・白1割 */
--tint: color-mix(in oklab, var(--brand) 70%, var(--accent) 20%, white 10%);
background: var(--tint);
border: 1px solid color-mix(in oklab, var(--brand) 60%, black);
}
/* 不透明度だけ変える: Sass の transparentize() 相当が不要に */
.card-muted {
color: alpha(var(--brand), 60%);
}
.card-faint {
background: alpha(var(--tint, #3b5ba9), 12%);
}
@supports not (color: color-mix(in oklab, red 50%, blue)) {
.card { background: #5a76b8; }
.card-muted { color: #3b5ba9; }
.card-faint { background: #eef1f6; }
}ポイントは2つです。第一に色空間を in oklab のように明示すること。無指定(既定は srgb)より中間色の濁りが少なく、ブランドカラーの淡色バリエーションがきれいに出ます。第二に @supports not (color: color-mix(...)) でフォールバック色を用意すること。変数の解決に失敗するとプロパティ全体が無効になるため、未対応ブラウザではベタ色に倒すのが安全です。
下のデモを「実行」すると、3色混色パレットがその場で再現されます。未対応ブラウザではベタ色フォールバックに倒れることも合わせて確認できます。
<div id="mx1-wrap" style="font-family:sans-serif;">
<p id="mx1-status" style="font-size:13px;padding:8px 12px;border-radius:6px;background:#f0f3f5;">判定中…</p>
<div style="display:flex;gap:8px;margin-top:12px;">
<div class="mx1-a" style="flex:1;height:64px;border-radius:8px;"></div>
<div class="mx1-b" style="flex:1;height:64px;border-radius:8px;"></div>
<div class="mx1-c" style="flex:1;height:64px;border-radius:8px;"></div>
</div>
<p style="font-size:12px;color:#555;">左から base / tint / faint。未対応時はベタ色フォールバックになります。</p>
</div>
<style>
.mx1-a { background: #3b5ba9; }
.mx1-b { background: #5a76b8; }
.mx1-c { background: #eef1f6; }
@supports (background: color-mix(in oklab, red 50%, blue)) {
.mx1-a { background: #3b5ba9; }
.mx1-b { background: color-mix(in oklab, #3b5ba9 70%, #4fa3c4 20%, white 10%); }
.mx1-c { background: color-mix(in oklab, #3b5ba9 12%, white); }
}
</style>
<script>
(() => {
const ok = CSS.supports('background', 'color-mix(in oklab, red 50%, blue)');
const st = document.getElementById('mx1-status');
st.textContent = ok
? 'color-mix に対応:3色混色パレットで表示中'
: 'color-mix 未対応:ベタ色フォールバックが正解';
st.style.background = ok ? '#e4eaf4' : '#fdf0e6';
})();
</script>light-dark()に画像 + image(色)
ダークモード対応というと @media (prefers-color-scheme: dark) でブロックごと書き換える方法が定番ですが、light-dark() を使うとプロパティ値の位置でライト/ダークを切り替えられます。Safari 27ではさらに light-dark() の引数に画像が使え、image() 関数で色を画像レイヤーとして扱えるようになりました(執筆時点ではChrome側の対応状況に差があります)。ヒーロー背景やバナーの昼夜切り替えが、メディアクエリの重複なしで1行で書けます。
.hero {
color-scheme: light dark;
/* ライト用とダーク用の背景画像を1行で切り替え */
background-image: light-dark(
url("/images/hero-light.avif"),
url("/images/hero-dark.avif")
);
background-size: cover;
}
/* 色と画像の重ね合わせも宣言的に */
.banner {
color-scheme: light dark;
background-image: light-dark(
image(#f0f3f5),
image(#1c2733)
);
}
.banner-overlay {
/* 画像レイヤー + グラデーションの合成例 */
background-image:
linear-gradient(alpha(#1c2733, 30%), alpha(#1c2733, 30%)),
light-dark(
url("/images/banner-light.avif"),
url("/images/banner-dark.avif")
);
}前提として color-scheme: light dark の宣言を忘れないことです。これがないと light-dark() は常に第一引数(ライト側)に倒れます。フォールバックは外側にライト用の background-image を書いておき、対応ブラウザだけ @supports (background-image: light-dark(url(a), url(b))) で上書きする形が素直です。画像のプリロードや fetchpriority の都合でHTML側の切り替えが必要な場合は、従来の picture 要素と使い分けてください。
アンカー配置の実用化
ツールチップやポップオーバーを出すたびにPopper系ライブラリを持ち出すのは重い、という場面で注目なのがCSSアンカー配置です。Safari 27では position-anchor: normal / none の扱いや、追従時の transform の振る舞い、anchor-valid()・anchor-visible() といった条件関数まわりが整い、実用ラインに乗ってきました(執筆時点ではChromeの実装との差異が残るため、位置が critical なUIでは必ずフォールバック確認を)。方向性としては「簡易ツールチップはCSSだけで、複雑な衝突回避が必要なものだけJS」という使い分けです。
/* Popperなしツールチップの最小形(対応ブラウザ向け) */
.tip-anchor {
position-anchor: normal;
anchor-name: --tip-target;
}
.tip {
position: absolute;
position-anchor: --tip-target;
/* アンカー下中央に配置し、はみ出し時は反転寄せ */
inset-area: block-end span-inline-end;
transform: translateX(-50%);
}
/* アンカーが無効・不可視のときは出さない */
@supports (inset-area: block-end) {
.tip:not(:where(:anchor-valid, :anchor-visible)) {
display: none;
}
}
/* 未対応ブラウザ向け: 従来の絶対配置にフォールバック */
@supports not (position-anchor: --tip-target) {
.tip {
position: absolute;
top: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
}
}実務のコツは、position-anchor: normal で暗黙のアンカー(ポップオーバーの起動元など)を拾い、明示したい場合だけ anchor-name を振ることです。anchor-valid() は参照先が存在するかどうか、anchor-visible() は画面内に収まっているかどうかの判定に使い、無効な状態では非表示に倒すと事故が減ります。スクロール追従でずれる場合は transform で微調整するより inset-area の見直しを優先してください。
アンカー配置は「出る位置がズレても致命的でないUI」から試すのが安全です。補足ツールチップやバッジはCSSアンカー配置+フォールバックで十分ですが、フォームのバリデーションポップや決済導線のような必読UIは、執筆時点では従来のJS配置と併用し、取りこぼしのない方を正としましょう。
Chromeではどうなの?まとめ表
ここまで紹介した4機能について、執筆時点で確認されている対応状況の目安を表にまとめます。バージョン番号は執筆時点のもので、最新情報は各リンク先の対応表で確認してください。読み方のコツは「Safari先行=未来の標準の先取り」と捉えることです。@supports とフォールバックを併用すれば、未対応ブラウザを壊さずに対応ブラウザだけ新しい書き方の恩恵を受けられます。
機能 | Safari 27 | Chrome(執筆時点) | Firefox(執筆時点) |
|---|---|---|---|
| 対応 | 未対応 | 未対応 |
| 対応(強化) | 部分的(差異あり) | 部分的(差異あり) |
| 対応(強化) | 部分的(差異あり) | 部分的(差異あり) |
CSSアンカー配置( | 対応(実用化) | 先行実装あり(差異あり) | 未対応寄り |
表の「部分的(差異あり)」は、関数名は使えても引数の取り方や条件関数の有無が違うケースを指します。動けばOKではなく、比率指定や画像引数など今回使う書式がそのまま通るかを @supports で検出するのが確実です。検出条件は甘めにせず、実際に使う値の形で書くのがポイントです。
まとめ
Safari 27先行の4機能は、見出し短縮・パレット生成・昼夜切替・配置という「毎回書く定型処理」をCSSに寄せるものです。検出は @supports、表示はフォールバック付きの二段構えにすれば、執筆時点でChrome未対応の機能も安全に試せます。まずは影響の小さい装飾から取り込み、未来の標準を先取りしましょう。
運用面の注意として、新機能を使うときは検出条件とフォールバックをセットでコードレビューの観点に入れることをおすすめします。対応表の値は執筆時点のスナップショットなので、リリース前に手元の実機と各ブラウザの対応表で再確認してください。小さく試して表の「部分的」の中身を潰していく進め方が、結果的に一番早く標準に追いつきます。
参考リンク:






