01キャンディリング回転
パステル3色が混ざったconic-gradientのリングが回りながらわずかに膨らんだり縮んだりして、暗い画面でもキャンディのように目を引く最初のローディング表示になる。
使いどころサービスの最初の画面のローディング、ダークテーマダッシュボードへの遷移時
conic-gradientmask$duration

<main class="stage">
<div class="cr-wrap is-demo" aria-hidden="true">
<div class="cr-ring"></div>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: 1s; // 링 한 바퀴
$easing: $ease-out;
$color: $subject-pink;
@include stage($stage-ink);
// 실사용: 로딩 시작하면 JS 가 .is-loading 을 붙인다
.cr-wrap {
width: 148px;
height: 148px;
display: grid;
place-items: center;
}
.cr-ring {
width: 148px;
height: 148px;
border-radius: 50%;
background: conic-gradient(from 0deg, $color, $subject-lilac, $subject-sky, $color);
mask: radial-gradient(farthest-side, #0000 calc(100% - 22px), #000 calc(100% - 21px));
}
.cr-wrap.is-loading,
.cr-wrap.is-demo {
.cr-ring { animation: cr-spin $duration linear infinite; }
animation: cr-breathe 2s $easing infinite;
}
@keyframes cr-spin {
to { transform: rotate(360deg); }
}
@keyframes cr-breathe {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
.cr-wrap.is-loading,
.cr-wrap.is-demo {
animation: none !important;
.cr-ring { animation: none !important; }
}
}
// 320px 폭 스테이지에서 1.12배 breathe 가 패딩 여유를 거의 다 먹어 타이밍에 따라 세로 넘침이 났다
// (실측 2026-09-11: 148px 링 기준 여유 8.64px 뿐이라 렌더 타이밍마다 폰넘침 O/X 가 갈림) — 좁은 화면에서 링 자체를 줄여 여유를 키운다
@media (max-width: 340px), (max-height: 220px) {
.cr-wrap, .cr-ring { width: 116px; height: 116px; }
}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);
}
.cr-wrap {
width: 148px;
height: 148px;
display: grid;
place-items: center;
}
.cr-ring {
width: 148px;
height: 148px;
border-radius: 50%;
background: conic-gradient(from 0deg, #ff9ebb, #b9a0ff, #6ec8ff, #ff9ebb);
mask: radial-gradient(farthest-side, rgba(0, 0, 0, 0) calc(100% - 22px), #000 calc(100% - 21px));
}
.cr-wrap.is-loading .cr-ring,
.cr-wrap.is-demo .cr-ring {
animation: cr-spin 1s linear infinite;
}
.cr-wrap.is-loading,
.cr-wrap.is-demo {
animation: cr-breathe 2s ease-out infinite;
}
@keyframes cr-spin {
to {
transform: rotate(360deg);
}
}
@keyframes cr-breathe {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.12);
}
}
@media (prefers-reduced-motion: reduce) {
.cr-wrap.is-loading,
.cr-wrap.is-demo {
animation: none !important;
}
.cr-wrap.is-loading .cr-ring,
.cr-wrap.is-demo .cr-ring {
animation: none !important;
}
}
@media (max-width: 340px), (max-height: 220px) {
.cr-wrap, .cr-ring {
width: 116px;
height: 116px;
}
}import { CSSProperties } from "react";
import styles from "./CandyRing.module.scss";
type Vars = CSSProperties & {
"--duration"?: string;
"--easing"?: string;
"--color"?: string;
};
export interface CandyRingProps {
duration?: string;
easing?: string;
color?: string;
}
export default function CandyRing({
duration = "1s",
easing = "ease-out",
color = "#ff9ebb",
}: CandyRingProps) {
const style: Vars = {
"--duration": duration,
"--easing": easing,
"--color": color,
};
return (
<div className={`${styles.wrap} ${styles.demo}`} style={style} aria-hidden="true">
<div className={styles.ring} />
</div>
);
}01 キャンディリング回転 — 計測 累積面積 9.335% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · 【コピペOK】ローディング 画面 デザイン CSSだけ 9選
このデモが属する記事
【コピペOK】ローディング 画面 デザイン CSSだけ 9選ローディング 画面 デザインとは、サービスが開く最初の瞬間に「今準備しています」と伝える待ち画面のことです。デフォルトのスピナーでも動きますが、パステルのリングや満ちていく水のような画面にすると、同じ待ち時間が意図のあるものに見えます。コピ この記事のデモをすべて見る


