09Auto dark mode
light-dark() picks the right colors in dark mode automatically.
Best forAny site with a dark theme
light-dark()대비 높게회색 대신 파랑

<main class="stage">
<div class="wrap">
<div class="card light"><div class="bar"></div><div class="bar short"></div></div>
<div class="card dark"><div class="bar"></div><div class="bar short"></div></div>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-loop; // 밝은 색 ↔ 어두운 색 한 바퀴
$easing: $ease-spring;
$color: $subject-blue; // light-dark() 로 못 바꾸는 강조 색(파랑은 두 배경 다 위에서 산다)
@include stage($stage-ink);
// 실제로는 light-dark() 함수 하나가 이 크로스페이드가 하는 일을 대신한다 (본문 코드 참고).
// 렌더는 색을 실시간으로 못 바꾸므로, 여기서는 밝은 카드·어두운 카드 두 장을 겹쳐 페이드로 보여준다.
.wrap {
position: relative;
width: clamp(200px, 72%, 300px);
display: grid;
}
.card {
grid-area: 1 / 1;
padding: clamp(14px, 4vw, 18px);
border-radius: $r-card;
display: grid;
gap: clamp(8px, 2.6vw, 10px);
box-shadow: $shadow-raised;
}
.bar { height: clamp(11px, 3.2vw, 14px); border-radius: $r-control; width: 85%; }
.bar.short { width: 55%; }
.card.light {
background: $stage-paper;
animation: swap-out $duration $easing infinite;
.bar { background: rgba($subject-ink, .16); }
}
.card.dark {
background: $stage-ink;
box-shadow: $shadow-raised, inset 0 0 0 1px rgba($subject-cream, .14);
animation: swap-in $duration $easing infinite;
.bar { background: rgba($subject-cream, .22); }
}
@keyframes swap-out {
0% { opacity: 1; }
50% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes swap-in {
0% { opacity: 0; }
50% { opacity: 1; }
100% { opacity: 0; }
}
// ── 접근성: 색 전환 반복을 멈추고 어두운 배색 한 장으로 고정 ──────
@media (prefers-reduced-motion: reduce) {
.card.light { animation: none !important; opacity: 0; }
.card.dark { animation: none !important; opacity: 1; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #17141a;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.wrap {
position: relative;
width: clamp(200px, 72%, 300px);
display: grid;
}
.card {
grid-area: 1/1;
padding: clamp(14px, 4vw, 18px);
border-radius: 16px;
display: grid;
gap: clamp(8px, 2.6vw, 10px);
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
}
.bar {
height: clamp(11px, 3.2vw, 14px);
border-radius: 12px;
width: 85%;
}
.bar.short {
width: 55%;
}
.card.light {
background: #ffffff;
animation: swap-out 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.card.light .bar {
background: rgba(23, 20, 26, 0.16);
}
.card.dark {
background: #17141a;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32), inset 0 0 0 1px rgba(255, 247, 230, 0.14);
animation: swap-in 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.card.dark .bar {
background: rgba(255, 247, 230, 0.22);
}
@keyframes swap-out {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes swap-in {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.card.light {
animation: none !important;
opacity: 0;
}
.card.dark {
animation: none !important;
opacity: 1;
}
}import type { CSSProperties } from "react";
import styles from "./AutoThemeSkeleton.module.scss";
export type AutoThemeSkeletonProps = {
className?: string;
};
// light-dark() 가 색을 정하므로 이 컴포넌트에는 상태·prop 이 필요 없다.
// 시스템(또는 :root 의 color-scheme)이 바뀌면 배경·막대 색이 스스로 바뀐다.
export default function AutoThemeSkeleton({ className = "" }: AutoThemeSkeletonProps) {
return (
<div className={`${styles.card} ${className}`}>
<div className={styles.bar} style={{ width: "85%" } as CSSProperties} />
<div className={styles.bar} style={{ width: "55%" } as CSSProperties} />
</div>
);
}09 Auto dark mode — Measured area 15.289% · moving frames 9/23Original by GODRICH · SCSS and React ship in the zip · 9 CSS Skeleton Loading Screens, Copy-Paste Ready
The post this demo lives in
9 CSS Skeleton Loading Screens, Copy-Paste ReadyA css skeleton loading screen swaps a blank page for gray shapes that mirror the layout while data loads. Nine SCSS buil See all demos in this post
If you like this effect - similar demos
Loaders02 Skeleton row append — a list computing its preview row count from the viewport heightInfinite Scroll Loading: 9 Essential UI Patterns
Loaders01 Bar Chart Loading Skeleton Whose Delay Is Computed Per BarChart Loading Animation — 9 Computed Patterns
Loaders08 A Handoff Where the Logo Flies to the Header SlotSplash Screen Animation: 9 Canvas Opening Scenes