DEMO
@starting-style
display:none からでもフェードインします。JavaScript は使っていません。
CODE
<div class="demo-stage ssf-stage">
<input type="checkbox" id="ssf-toggle" class="ssf-check">
<label for="ssf-toggle" class="ssf-btn">TOGGLE PANEL</label>
<div class="ssf-panel">
<p class="ssf-title">@starting-style</p>
<p class="ssf-text">display:none からでもフェードインします。JavaScript は使っていません。</p>
</div>
</div>
.ssf-stage { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:1.4rem; padding:3rem; background:#0F0F12; min-height:280px; }
.ssf-check { position:absolute; opacity:0; width:0; height:0; }
.ssf-btn {
padding:.85rem 2rem; border:1px solid rgba(233,27,137,.6); border-radius:999px;
background:#15151A; color:#fff; cursor:pointer;
font-family:'Oswald',sans-serif; font-size:.78rem; letter-spacing:.22em;
transition: background .25s ease, border-color .25s ease;
}
.ssf-btn:hover { background:#1F1F26; border-color:#E91B89; }
.ssf-check:focus-visible + .ssf-btn { outline:2px solid #E91B89; outline-offset:3px; }
.ssf-panel {
display:none; opacity:0; transform:translateY(14px) scale(.96);
max-width:340px; padding:1.6rem 1.8rem; border-radius:12px;
background:#17171C; border:1px solid rgba(255,255,255,.1);
transition: opacity .35s ease, transform .35s cubic-bezier(.23,1,.32,1), display .35s allow-discrete;
}
.ssf-check:checked ~ .ssf-panel { display:block; opacity:1; transform:translateY(0) scale(1); }
@starting-style {
.ssf-check:checked ~ .ssf-panel { opacity:0; transform:translateY(14px) scale(.96); }
}
.ssf-title { font-family:'JetBrains Mono',monospace; font-size:.9rem; color:#E91B89; margin-bottom:.6rem; }
.ssf-text { font-size:.85rem; line-height:1.8; color:#B9B9C2; }
@media (prefers-reduced-motion: reduce) {
.ssf-panel { transition-duration:.01ms; }
}
AI PROMPT
display:none の要素を表示に切り替えるときにフェードインさせたい。JavaScriptは使わない。要点は3つ。1) 表示側のセレクタ(例 :checked ~ .panel や .is-open)に display:block と最終状態(opacity:1, transform:none)を書く。2) 元のセレクタの transition に display .35s allow-discrete を含める。allow-discrete を付けると display のような離散プロパティもトランジションの対象になり、消えるときは終了まで display が保持され、現れるときは即座に切り替わる。3) @starting-style { 表示側のセレクタ { opacity:0; transform: translateY(14px) scale(.96) } } を書く。これが「表示された瞬間の初期値」になり、ここからのトランジションが走る。@starting-style がないと display:none からの変化は開始値を持てず、フェードが起きずに突然現れる。非対応ブラウザでは即時表示になるだけでレイアウトは壊れない。
Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift
AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する
BOOKS · PR · AMAZON
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
display:none から表示に切り替わる要素をアニメーションさせることは、長らくCSSだけでは不可能で、JavaScriptで一旦 display を変えてから次のフレームでクラスを付ける、という回りくどい手順が定番でした。@starting-style と transition-behavior: allow-discrete の組み合わせは、この定番ハックをCSSの標準機能で置き換えるものです。
当サイトの Page Fade Transition はページ遷移そのものの明転・暗転、Modal Fade はモーダルの開閉演出という別テーマです。本ページは「display が切り替わる要素すべてに効く土台の仕組み」を扱っており、モーダル・ドロワー・トースト・アコーディオンなど表示切り替えを伴う全ての実装で共通して使えます。
ポイントは @starting-style を「表示状態のセレクタ」に対して書くことです。元のセレクタに書いても効かないため、ここを間違えると無反応になります。
なぜ @starting-style が必要なのですか?
display:none の要素はレンダリングツリーに存在しないため、ブラウザは「変化前の値」を持てません。開始値が無いとトランジションは起動できず、突然表示されます。@starting-style は「その要素が初めて描画される瞬間に使う値」を宣言する仕組みで、これがあることで display:none → block の変化にもフェードを付けられます。
transition-behavior: allow-discrete は何のためにありますか?
display や content-visibility のように中間値を持たない離散プロパティは、既定ではトランジションの対象外です。allow-discrete を指定すると、表示に変わるときは即座に、非表示に変わるときはトランジション終了まで待ってから切り替わるようになります。これがないと閉じるときに要素が一瞬で消え、フェードアウトが見えません。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。