09Tự đổi màu dark mode
Dùng light-dark() để màu tự đổi khi ở dark mode.
Dùng choMọi trang có dark mode
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 Tự đổi màu dark mode — Diện tích đo được 15.289% · khung hình chuyển động 9/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 hiệu ứng css skeleton loading copy-paste
Bài viết chứa demo này
9 hiệu ứng css skeleton loading copy-pasteCss skeleton loading là kỹ thuật hiện khối xám giữ đúng hình dạng của nội dung thật trong lúc chờ dữ liệu, thay cho màn Xem tất cả demo trong bài này


