y2k webデザインとは?クロムとホログラフィックのLPパーツ9選
y2k webデザインとは、2000年代のCDやホログラム包装紙にあったクロムの輝きと虹色の反射を、CSSだけで蘇らせるスタイルです。CDの裏で虹色ににじんでいたあの光が、今度はボタンや見出しの中に入ってきました。
9個のうち1つは、ホログラフィック デザインの反射をカードではなく回転するアバターの縁に使っています。下にまとめたワンページをまず一度スクロールして、クロムの見出しがどこで明るくなりどこで沈むかを見てください。

ワンページの下には、9個の部品を切り離して格子にも並べ直しました。
01 hero-chrome
02 nav-chrome
03 card-grid-chrome
04 feature-list-chrome
05 pricing-chrome
06 testimonial-chrome
07 cta-band-chrome
08 footer-chrome
09 buttons-inputs-chrome
自動再生 · タイルを押すとその項目へ · すべて1つのzip
- 01 クロム見出しヒーロー
- 02 ブラッシュドメタルナビ
- 03 ホログラフィック箔カード
- 04 クロムボールアイコン
- 05 強調プランのホログラフィック
- 06 CDリングアバター引用
- 07 ブラッシュドアルミCTA帯
- 08 ホログラフィック罫線フッター
- 09 クロムボタン・ホログラフィックフォーカス
並び順は人気順ではありません。目を引かれ、機能を確かめ、価格を見て、他の人の声を信じ、最後にボタンを押す——訪問者が実際にたどる順番のままです。同じdesign柱で光沢を削ぎ落とし直角の影だけを残したのがネオブルータリズム UI キットで、同じ金属質を計器盤の方向へ押し進めたのがHUD デザインのパーツです。
01クロム見出しヒーロー
何が動くか
見出しの文字をbackground-clip: textで切り抜き、その中で白→クリーム→黄→青→オレンジ→白と続く6段グラデーションがbackground-positionだけを動かして流れます。

<main class="stage">
<section class="hero-c">
<p class="hero-c__eyebrow">CHROME</p>
<h2 class="hero-c__title">また輝きはじめる最初の画面</h2>
<button class="hero-c__cta" type="button">無料で始める</button>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-loop; // 크롬 밴드가 한 바퀴 흐르는 시간
$easing: $ease-out;
$color: $subject-blue; // 그라디언트 안 포인트 색 하나
@include stage($stage-ink);
.hero-c {
position: relative;
overflow: hidden;
box-sizing: border-box;
width: clamp(260px, 86vw, 400px);
min-height: clamp(172px, 48vw, 220px);
border-radius: $r-card;
background: $stage-ink;
box-shadow: $shadow-raised;
padding: $sp-6;
display: flex;
flex-direction: column;
justify-content: center;
gap: $sp-3;
}
.hero-c__eyebrow {
position: relative;
z-index: 1;
margin: 0;
color: $stage-yellow;
font-weight: 700;
font-size: clamp(10px, 2.4vw, 12px);
line-height: 1;
letter-spacing: .06em;
text-transform: uppercase;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
// 헤드라인 — 크롬 그라디언트를 글자 모양으로 잘라내고, 그 안의 밝은 밴드가 background-position 만 바뀌며 흐른다 (유일한 애니메이션)
.hero-c__title {
position: relative;
z-index: 1;
margin: 0;
font-weight: 800;
font-size: clamp(18px, 4.6vw, 23px);
line-height: 1.2;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
background: linear-gradient(100deg,
$stage-paper 0%, rgba($stage-paper, .35) 14%, $subject-cream 28%,
$stage-yellow 40%, $color 52%, $stage-orange 64%,
$stage-paper 80%, rgba($stage-paper, .4) 100%);
background-size: 240% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: chromeFlow $duration $easing infinite;
}
.hero-c__cta {
position: relative;
z-index: 1;
align-self: flex-start;
border: 0;
border-radius: $r-pill;
background: linear-gradient(100deg, $subject-cream, $stage-paper, $subject-cream);
color: $stage-ink;
padding: $sp-2 $sp-5;
font-weight: 700;
font-size: clamp(11px, 2.6vw, 13px);
line-height: 1;
font-family: inherit;
}
@keyframes chromeFlow { 0% { background-position: 0% 50%; } 100% { background-position: 240% 50%; } }
@media (prefers-reduced-motion: reduce) {
.hero-c__title { animation: none; }
}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);
}
.hero-c {
position: relative;
overflow: hidden;
box-sizing: border-box;
width: clamp(260px, 86vw, 400px);
min-height: clamp(172px, 48vw, 220px);
border-radius: 16px;
background: #17141a;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
padding: 24px;
display: flex;
flex-direction: column;
justify-content: center;
gap: 12px;
}
.hero-c__eyebrow {
position: relative;
z-index: 1;
margin: 0;
color: #ffd23f;
font-weight: 700;
font-size: clamp(10px, 2.4vw, 12px);
line-height: 1;
letter-spacing: 0.06em;
text-transform: uppercase;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.hero-c__title {
position: relative;
z-index: 1;
margin: 0;
font-weight: 800;
font-size: clamp(18px, 4.6vw, 23px);
line-height: 1.2;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
background: linear-gradient(100deg, #ffffff 0%, rgba(255, 255, 255, 0.35) 14%, #fff7e6 28%, #ffd23f 40%, #2f6df6 52%, #ff4d1f 64%, #ffffff 80%, rgba(255, 255, 255, 0.4) 100%);
background-size: 240% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: chromeFlow 2s ease-out infinite;
}
.hero-c__cta {
position: relative;
z-index: 1;
align-self: flex-start;
border: 0;
border-radius: 999px;
background: linear-gradient(100deg, #fff7e6, #ffffff, #fff7e6);
color: #17141a;
padding: 8px 20px;
font-weight: 700;
font-size: clamp(11px, 2.6vw, 13px);
line-height: 1;
font-family: inherit;
}
@keyframes chromeFlow {
0% {
background-position: 0% 50%;
}
100% {
background-position: 240% 50%;
}
}
@media (prefers-reduced-motion: reduce) {
.hero-c__title {
animation: none;
}
}import type { CSSProperties } from "react";
import styles from "./HeroChrome.module.scss";
export type HeroChromeProps = {
eyebrow?: string;
title?: string;
cta?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function HeroChrome({
eyebrow = "CHROME",
title = "The first screen, shining again",
cta = "Start for free",
duration = 2000,
easing = "ease-out",
color = "#2f6df6",
}: HeroChromeProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<section className={styles.hero} style={vars}>
<p className={styles.eyebrow}>{eyebrow}</p>
<h2 className={styles.title}>{title}</h2>
<button type="button" className={styles.cta}>{cta}</button>
</section>
);
}01 · クロム見出しヒーロー — 計測 累積面積 1.539% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る
コードで見る所
文字そのものには色を塗らず、グラデーションを文字の形の窓として使うだけです。
.hero-c__title {
background: linear-gradient(100deg,
$stage-paper 0%, rgba($stage-paper, .35) 14%, $subject-cream 28%,
$stage-yellow 40%, $color 52%, $stage-orange 64%,
$stage-paper 80%, rgba($stage-paper, .4) 100%);
background-size: 240% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: chromeFlow $duration $easing infinite;
}
@keyframes chromeFlow {
0% { background-position: 0% 50%; }
100% { background-position: 240% 50%; }
}
02ブラッシュドメタルナビ
何が動くか
メニューバーの背景はインク色の濃淡が交互に並ぶグラデーションでブラッシュドメタルの質感を作り、その上を細い白の光の筋がleftの値だけで左から右へ通り過ぎます。

<main class="stage">
<nav class="nav-c">
<span class="nav-c__brand">Chroma</span>
<div class="nav-c__menu">
<span class="nav-c__label nav-c__label--active">ホーム</span>
<span class="nav-c__label">機能</span>
<span class="nav-c__label">料金</span>
</div>
<button class="nav-c__cta" type="button">無料で試す</button>
</nav>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop;
$easing: linear;
$color: $subject-blue;
@include stage($stage-yellow);
.nav-c {
position: relative;
overflow: hidden;
box-sizing: border-box;
display: flex;
align-items: center;
gap: $sp-3;
width: clamp(240px, 84vw, 380px);
padding: $sp-2 $sp-3;
border-radius: $r-pill;
background: linear-gradient(100deg, rgba($stage-ink, .92) 0%, rgba($stage-ink, .78) 22%, rgba($stage-ink, .95) 40%, rgba($stage-ink, .8) 60%, rgba($stage-ink, .95) 100%);
}
// 브러시드 메탈 하이라이트 — 가는 빛줄기가 바 전체를 좌우로 훑는다 (이 항목의 유일한 애니메이션)
.nav-c::after {
content: "";
position: absolute;
top: 0;
left: -20%;
width: 14%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba($stage-paper, .55), transparent);
animation: glint $duration $easing infinite;
}
.nav-c__brand {
position: relative;
z-index: 1;
flex-shrink: 0;
white-space: nowrap;
padding-left: $sp-2;
color: $stage-paper;
font-weight: 700;
font-size: clamp(12px, 3vw, 14px);
line-height: 1;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.nav-c__menu { position: relative; z-index: 1; min-width: 0; flex: 1; display: flex; }
.nav-c__label {
flex: 1;
white-space: nowrap;
text-align: center;
padding: $sp-2 0;
color: rgba($stage-paper, .8);
font-weight: 600;
font-size: clamp(10.5px, 2.6vw, 12px);
line-height: 1;
font-family: inherit;
}
.nav-c__label--active { color: $stage-paper; border-radius: $r-pill; background: rgba($stage-paper, .12); }
.nav-c__cta {
position: relative;
z-index: 1;
flex-shrink: 0;
white-space: nowrap;
border: 0;
border-radius: $r-pill;
background: $stage-paper;
color: $stage-ink;
padding: $sp-2 $sp-4;
font-weight: 700;
font-size: clamp(10.5px, 2.6vw, 12px);
line-height: 1;
font-family: inherit;
}
@keyframes glint { 0% { left: -20%; } 100% { left: 106%; } }
@media (prefers-reduced-motion: reduce) {
.nav-c::after { animation: none; opacity: 0; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffd23f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.nav-c {
position: relative;
overflow: hidden;
box-sizing: border-box;
display: flex;
align-items: center;
gap: 12px;
width: clamp(240px, 84vw, 380px);
padding: 8px 12px;
border-radius: 999px;
background: linear-gradient(100deg, rgba(23, 20, 26, 0.92) 0%, rgba(23, 20, 26, 0.78) 22%, rgba(23, 20, 26, 0.95) 40%, rgba(23, 20, 26, 0.8) 60%, rgba(23, 20, 26, 0.95) 100%);
}
.nav-c::after {
content: "";
position: absolute;
top: 0;
left: -20%;
width: 14%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.55), transparent);
animation: glint 2s linear infinite;
}
.nav-c__brand {
position: relative;
z-index: 1;
flex-shrink: 0;
white-space: nowrap;
padding-left: 8px;
color: #ffffff;
font-weight: 700;
font-size: clamp(12px, 3vw, 14px);
line-height: 1;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.nav-c__menu {
position: relative;
z-index: 1;
min-width: 0;
flex: 1;
display: flex;
}
.nav-c__label {
flex: 1;
white-space: nowrap;
text-align: center;
padding: 8px 0;
color: rgba(255, 255, 255, 0.8);
font-weight: 600;
font-size: clamp(10.5px, 2.6vw, 12px);
line-height: 1;
font-family: inherit;
}
.nav-c__label--active {
color: #ffffff;
border-radius: 999px;
background: rgba(255, 255, 255, 0.12);
}
.nav-c__cta {
position: relative;
z-index: 1;
flex-shrink: 0;
white-space: nowrap;
border: 0;
border-radius: 999px;
background: #ffffff;
color: #17141a;
padding: 8px 16px;
font-weight: 700;
font-size: clamp(10.5px, 2.6vw, 12px);
line-height: 1;
font-family: inherit;
}
@keyframes glint {
0% {
left: -20%;
}
100% {
left: 106%;
}
}
@media (prefers-reduced-motion: reduce) {
.nav-c::after {
animation: none;
opacity: 0;
}
}import type { CSSProperties } from "react";
import styles from "./NavChrome.module.scss";
export type NavChromeProps = {
brand?: string;
links?: string[];
activeIndex?: number;
cta?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function NavChrome({
brand = "Chroma",
links = ["Home", "Features", "Pricing"],
activeIndex = 0,
cta = "Try it free",
duration = 2000,
easing = "linear",
color = "#2f6df6",
}: NavChromeProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<nav className={styles.nav} style={vars}>
<span className={styles.brand}>{brand}</span>
<div className={styles.menu}>
{links.map((label, i) => (
<span key={label} className={i === activeIndex ? `${styles.label} ${styles.labelActive}` : styles.label}>
{label}
</span>
))}
</div>
<button type="button" className={styles.cta}>{cta}</button>
</nav>
);
}02 · ブラッシュドメタルナビ — 計測 累積面積 9.886% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る
コードで見る所
現在地のメニューは背景をわずかに明るくするだけで、それ自体は動きません。
.nav-c::after {
content: "";
position: absolute;
top: 0; left: -20%; width: 14%; height: 100%;
background: linear-gradient(90deg, transparent, rgba($stage-paper, .55), transparent);
animation: glint $duration linear infinite;
}
@keyframes glint { 0% { left: -20%; } 100% { left: 106%; } }
03ホログラフィック箔カード
何が動くか
カードの表面にmix-blend-mode: overlayでホログラフィック箔の帯を重ね、background-positionを対角線方向に流します。

<main class="stage">
<article class="card-c">
<span class="card-c__icon" aria-hidden="true"></span>
<h3 class="card-c__title">リアルタイムプレビュー</h3>
<p class="card-c__desc">変更はすぐに画面へ反映されます。</p>
</article>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop;
$easing: linear;
$color: $subject-blue;
@include stage($stage-orange);
.stage { padding: clamp(8px, 3vw, 20px); }
.card-c {
position: relative;
overflow: hidden;
box-sizing: border-box;
width: min(240px, 78vw);
border-radius: $r-card;
background: $stage-ink;
box-shadow: $shadow-raised;
padding: $sp-5;
display: flex;
flex-direction: column;
gap: $sp-2;
}
// 홀로그램 포일 띠 — mix-blend-mode 로 카드 표면 위에 얹혀 대각선으로 한 바퀴씩 지나간다 (유일한 애니메이션)
.card-c::after {
content: "";
position: absolute;
inset: -30% -10%;
pointer-events: none;
mix-blend-mode: overlay;
opacity: .8;
background: repeating-linear-gradient(115deg,
transparent 0 18px,
rgba($color, .55) 18px 30px,
rgba($stage-yellow, .55) 30px 42px,
rgba($subject-cream, .6) 42px 54px,
transparent 54px 72px);
background-size: 200% 200%;
animation: foilSweep $duration $easing infinite;
}
.card-c__icon { position: relative; z-index: 1; width: $sp-8; height: $sp-8; border-radius: $r-sm; background: radial-gradient(circle at 30% 30%, $subject-cream, $color 55%, $stage-ink 100%); }
.card-c__title {
position: relative;
z-index: 1;
margin: 0;
color: $stage-paper;
font-weight: 700;
font-size: clamp(13px, 3.2vw, 15px);
line-height: 1.3;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.card-c__desc {
position: relative;
z-index: 1;
margin: 0;
color: rgba($stage-paper, .7);
font-weight: 500;
font-size: clamp(11px, 2.6vw, 12.5px);
line-height: 1.4;
font-family: inherit;
}
@keyframes foilSweep { 0% { background-position: 0% 0%; } 100% { background-position: -140% -140%; } }
@media (prefers-reduced-motion: reduce) {
.card-c::after { animation: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ff4d1f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.stage {
padding: clamp(8px, 3vw, 20px);
}
.card-c {
position: relative;
overflow: hidden;
box-sizing: border-box;
width: min(240px, 78vw);
border-radius: 16px;
background: #17141a;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
padding: 20px;
display: flex;
flex-direction: column;
gap: 8px;
}
.card-c::after {
content: "";
position: absolute;
inset: -30% -10%;
pointer-events: none;
mix-blend-mode: overlay;
opacity: 0.8;
background: repeating-linear-gradient(115deg, transparent 0 18px, rgba(47, 109, 246, 0.55) 18px 30px, rgba(255, 210, 63, 0.55) 30px 42px, rgba(255, 247, 230, 0.6) 42px 54px, transparent 54px 72px);
background-size: 200% 200%;
animation: foilSweep 2s linear infinite;
}
.card-c__icon {
position: relative;
z-index: 1;
width: 32px;
height: 32px;
border-radius: 8px;
background: radial-gradient(circle at 30% 30%, #fff7e6, #2f6df6 55%, #17141a 100%);
}
.card-c__title {
position: relative;
z-index: 1;
margin: 0;
color: #ffffff;
font-weight: 700;
font-size: clamp(13px, 3.2vw, 15px);
line-height: 1.3;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.card-c__desc {
position: relative;
z-index: 1;
margin: 0;
color: rgba(255, 255, 255, 0.7);
font-weight: 500;
font-size: clamp(11px, 2.6vw, 12.5px);
line-height: 1.4;
font-family: inherit;
}
@keyframes foilSweep {
0% {
background-position: 0% 0%;
}
100% {
background-position: -140% -140%;
}
}
@media (prefers-reduced-motion: reduce) {
.card-c::after {
animation: none;
}
}import type { CSSProperties } from "react";
import styles from "./CardGridChrome.module.scss";
export type CardGridChromeProps = {
title?: string;
desc?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function CardGridChrome({
title = "Live preview",
desc = "Every change reflects on screen instantly.",
duration = 2000,
easing = "linear",
color = "#2f6df6",
}: CardGridChromeProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<article className={styles.card} style={vars}>
<span className={styles.icon} aria-hidden="true" />
<h3 className={styles.title}>{title}</h3>
<p className={styles.desc}>{desc}</p>
</article>
);
}03 · ホログラフィック箔カード — 計測 累積面積 10.607% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る
コードで見る所
ホログラムシールを実際に傾けたときに見える虹色の反射を再現したもので、カード自体は濃いインク色に固定して反射だけが際立つようにしています。
.card-c::after {
content: "";
position: absolute;
inset: -30% -10%;
pointer-events: none;
mix-blend-mode: overlay;
opacity: .8;
background: repeating-linear-gradient(115deg,
transparent 0 18px,
rgba($color, .55) 18px 30px,
rgba($stage-yellow, .55) 30px 42px,
rgba($subject-cream, .6) 42px 54px,
transparent 54px 72px);
background-size: 200% 200%;
animation: foilSweep $duration linear infinite;
}
04クロムボールアイコン
何が動くか
アイコンバッジそれぞれに、光源が左上に固定されているように見えるradial-gradientの球体を乗せ、そのハイライトの位置だけをゆっくり動かして光源が表面を回っているように見せます。

<main class="stage">
<ul class="feat-c">
<li class="feat-c__row">
<span class="feat-c__icon" aria-hidden="true"></span>
<p class="feat-c__text">ワンクリックデプロイ</p>
</li>
<li class="feat-c__row">
<span class="feat-c__icon" aria-hidden="true"></span>
<p class="feat-c__text">自動保存</p>
</li>
<li class="feat-c__row">
<span class="feat-c__icon" aria-hidden="true"></span>
<p class="feat-c__text">チーム同時編集</p>
</li>
</ul>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop;
$easing: steps(8, jump-none); // 부드러운 곡선은 프레임 간 변화가 너무 작아 강도 측정에 안 잡힌다
$color: $subject-blue;
@include stage($stage-paper);
.feat-c {
margin: 0;
list-style: none;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: $sp-3;
width: clamp(240px, 80vw, 360px);
padding: $sp-5;
border-radius: $r-card;
background: $stage-ink;
box-shadow: $shadow-raised;
}
.feat-c__row { margin: 0; display: flex; align-items: center; gap: $sp-3; }
// 크롬 구슬 — radial-gradient 하이라이트의 위치만 천천히 옮겨 광원이 표면을 도는 느낌을 낸다 (유일한 애니메이션)
.feat-c__icon {
flex: none;
width: $sp-8;
height: $sp-8;
border-radius: $r-pill;
background: radial-gradient(circle at 30% 30%, $stage-paper, $color 40%, $stage-ink 82%);
background-size: 280% 280%;
animation: ballShine $duration $easing infinite;
}
.feat-c__text {
margin: 0;
color: $stage-paper;
font-weight: 600;
font-size: clamp(11.5px, 2.8vw, 13px);
line-height: 1.4;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
@keyframes ballShine { 0% { background-position: 0% 0%; } 100% { background-position: 180% 180%; } }
@media (prefers-reduced-motion: reduce) {
.feat-c__icon { animation: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffffff;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.feat-c {
margin: 0;
list-style: none;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 12px;
width: clamp(240px, 80vw, 360px);
padding: 20px;
border-radius: 16px;
background: #17141a;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
}
.feat-c__row {
margin: 0;
display: flex;
align-items: center;
gap: 12px;
}
.feat-c__icon {
flex: none;
width: 32px;
height: 32px;
border-radius: 999px;
background: radial-gradient(circle at 30% 30%, #ffffff, #2f6df6 40%, #17141a 82%);
background-size: 280% 280%;
animation: ballShine 2s steps(8, jump-none) infinite;
}
.feat-c__text {
margin: 0;
color: #ffffff;
font-weight: 600;
font-size: clamp(11.5px, 2.8vw, 13px);
line-height: 1.4;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
@keyframes ballShine {
0% {
background-position: 0% 0%;
}
100% {
background-position: 180% 180%;
}
}
@media (prefers-reduced-motion: reduce) {
.feat-c__icon {
animation: none;
}
}import type { CSSProperties } from "react";
import styles from "./FeatureListChrome.module.scss";
export type FeatureListChromeProps = {
items?: string[];
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function FeatureListChrome({
items = ["One-click deploy", "Auto save", "Live co-editing"],
duration = 2000,
easing = "steps(8, jump-none)",
color = "#2f6df6",
}: FeatureListChromeProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<ul className={styles.list} style={vars}>
{items.map((text) => (
<li key={text} className={styles.row}>
<span className={styles.icon} aria-hidden="true" />
<p className={styles.text}>{text}</p>
</li>
))}
</ul>
);
}04 · クロムボールアイコン — 計測 累積面積 1.75% · 動いたフレーム 7/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る
コードで見る所
3つのアイコンは同じキーフレーム1つを共有しています。
.feat-c__icon {
background: radial-gradient(circle at 30% 30%, $stage-paper, $color 40%, $stage-ink 82%);
background-size: 280% 280%;
animation: ballShine $duration steps(8, jump-none) infinite;
}
@keyframes ballShine {
0% { background-position: 0% 0%; }
100% { background-position: 180% 180%; }
}
05強調プランのホログラフィック
何が動くか
おすすめプランのカードにだけ5色グラデーションの背景を敷き、background-positionで流すことで、比較表がなくてもどのプランを推しているか一目で伝わります。

<main class="stage">
<section class="pricing-c">
<div class="pricing-c__plan">
<p class="pricing-c__name">無料</p>
<p class="pricing-c__price">₩0</p>
</div>
<div class="pricing-c__plan pricing-c__plan--pro">
<span class="pricing-c__badge">おすすめ</span>
<p class="pricing-c__name">プロ</p>
<p class="pricing-c__price">月15,000円</p>
</div>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop;
$easing: linear;
$color: $subject-blue;
@include stage($stage-ink);
.pricing-c { display: flex; align-items: flex-end; gap: $sp-4; width: clamp(240px, 82vw, 380px); }
.pricing-c__plan {
flex: 1;
border-radius: $r-card;
padding: $sp-5 $sp-4;
background: rgba($stage-paper, .06);
border: 1px solid rgba($stage-paper, .12);
}
.pricing-c__plan--pro {
position: relative;
overflow: hidden;
flex: 1.15;
padding-top: $sp-8;
box-shadow: $shadow-raised;
}
// 추천 플랜만 다색 홀로그램 그라디언트가 배경 위치를 흐르며 이동한다 (유일한 애니메이션). 무료 플랜은 무광 회색 그대로
.pricing-c__plan--pro::before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(100deg, $color, $stage-yellow, $stage-orange, $subject-cream, $color);
background-size: 320% 100%;
animation: holoShift $duration $easing infinite;
}
.pricing-c__badge {
position: relative;
z-index: 1;
display: inline-block;
margin-bottom: $sp-2;
border-radius: $r-pill;
background: rgba($stage-ink, .4);
color: $stage-paper;
padding: $sp-1 $sp-3;
font-weight: 700;
font-size: clamp(9.5px, 2.2vw, 11px);
line-height: 1;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.pricing-c__name {
position: relative;
z-index: 1;
margin: 0 0 $sp-1;
color: $stage-paper;
font-weight: 700;
font-size: clamp(12px, 3vw, 14px);
line-height: 1.3;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.pricing-c__price {
position: relative;
z-index: 1;
margin: 0;
color: rgba($stage-paper, .75);
font-weight: 600;
font-size: clamp(11px, 2.6vw, 12.5px);
line-height: 1.3;
font-family: inherit;
}
.pricing-c__plan--pro .pricing-c__name { color: $stage-ink; }
.pricing-c__plan--pro .pricing-c__price { color: rgba($stage-ink, .8); }
@keyframes holoShift { 0% { background-position: 0% 50%; } 100% { background-position: 320% 50%; } }
@media (prefers-reduced-motion: reduce) {
.pricing-c__plan--pro::before { animation: none; }
}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);
}
.pricing-c {
display: flex;
align-items: flex-end;
gap: 16px;
width: clamp(240px, 82vw, 380px);
}
.pricing-c__plan {
flex: 1;
border-radius: 16px;
padding: 20px 16px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.pricing-c__plan--pro {
position: relative;
overflow: hidden;
flex: 1.15;
padding-top: 32px;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
}
.pricing-c__plan--pro::before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(100deg, #2f6df6, #ffd23f, #ff4d1f, #fff7e6, #2f6df6);
background-size: 320% 100%;
animation: holoShift 2s linear infinite;
}
.pricing-c__badge {
position: relative;
z-index: 1;
display: inline-block;
margin-bottom: 8px;
border-radius: 999px;
background: rgba(23, 20, 26, 0.4);
color: #ffffff;
padding: 4px 12px;
font-weight: 700;
font-size: clamp(9.5px, 2.2vw, 11px);
line-height: 1;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.pricing-c__name {
position: relative;
z-index: 1;
margin: 0 0 4px;
color: #ffffff;
font-weight: 700;
font-size: clamp(12px, 3vw, 14px);
line-height: 1.3;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.pricing-c__price {
position: relative;
z-index: 1;
margin: 0;
color: rgba(255, 255, 255, 0.75);
font-weight: 600;
font-size: clamp(11px, 2.6vw, 12.5px);
line-height: 1.3;
font-family: inherit;
}
.pricing-c__plan--pro .pricing-c__name {
color: #17141a;
}
.pricing-c__plan--pro .pricing-c__price {
color: rgba(23, 20, 26, 0.8);
}
@keyframes holoShift {
0% {
background-position: 0% 50%;
}
100% {
background-position: 320% 50%;
}
}
@media (prefers-reduced-motion: reduce) {
.pricing-c__plan--pro::before {
animation: none;
}
}import type { CSSProperties } from "react";
import styles from "./PricingChrome.module.scss";
export type PricingChromeProps = {
freeName?: string;
freePrice?: string;
proName?: string;
proPrice?: string;
badge?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function PricingChrome({
freeName = "Free",
freePrice = "$0",
proName = "Pro",
proPrice = "$15/mo",
badge = "Recommended",
duration = 2000,
easing = "linear",
color = "#2f6df6",
}: PricingChromeProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<section className={styles.pricing} style={vars}>
<div className={styles.plan}>
<p className={styles.name}>{freeName}</p>
<p className={styles.price}>{freePrice}</p>
</div>
<div className={`${styles.plan} ${styles.plaPro}`}>
<span className={styles.badge}>{badge}</span>
<p className={styles.name}>{proName}</p>
<p className={styles.price}>{proPrice}</p>
</div>
</section>
);
}05 · 強調プランのホログラフィック — 計測 累積面積 15.662% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る
コードで見る所
通常プランはマットなグレーのまま残してコントラストを作ります。
.pricing-c__plan--pro::before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(100deg, $color, $stage-yellow, $stage-orange, $subject-cream, $color);
background-size: 320% 100%;
animation: holoShift $duration linear infinite;
}
.pricing-c__plan--pro .pricing-c__name { color: $stage-ink; }
06CDリングアバター引用
何が動くか
引用カードの横に並ぶアバターが、CDの縁のようにゆっくり回るconic-gradientのリングの中に収まります。

<main class="stage">
<figure class="test-c">
<div class="test-c__avatar" aria-hidden="true">
<div class="test-c__avatar-fill"></div>
</div>
<blockquote class="test-c__card">
<p class="test-c__quote">"ローンチ最初の週にクリック率が2倍になりました"</p>
<figcaption class="test-c__name">パク・ジウン · マーケティングリード</figcaption>
</blockquote>
</figure>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop;
$easing: linear;
$color: $subject-blue;
@include stage($stage-yellow);
.test-c {
margin: 0;
position: relative;
box-sizing: border-box;
width: clamp(240px, 82vw, 360px);
padding: $sp-5;
display: flex;
flex-direction: row;
align-items: flex-start;
gap: $sp-3;
}
.test-c__avatar { position: relative; flex-shrink: 0; width: $sp-10; height: $sp-10; border-radius: 50%; }
// CD 테두리처럼 도는 conic-gradient 링 — mask 로 링 모양만 남기고 회전만 시킨다 (유일한 애니메이션)
.test-c__avatar::before {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
padding: $sp-1;
background: conic-gradient($color, $stage-orange, $subject-cream, $stage-ink, $color);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
animation: cdSpin $duration $easing infinite;
}
.test-c__avatar-fill { position: absolute; inset: $sp-1; border-radius: 50%; background: $stage-ink; }
.test-c__card {
margin: 0;
min-width: 0;
position: relative;
border-radius: $r-card;
padding: $sp-5;
background: $stage-ink;
border: 1px solid rgba($stage-paper, .18);
outline: 1px solid rgba($subject-cream, .35);
outline-offset: -4px;
}
.test-c__quote {
margin: 0 0 $sp-3;
color: $stage-paper;
font-weight: 600;
font-size: clamp(12px, 3vw, 13.5px);
line-height: 1.5;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.test-c__name {
margin: 0;
color: rgba($stage-paper, .7);
font-weight: 600;
font-size: clamp(10.5px, 2.4vw, 11.5px);
line-height: 1.3;
font-family: inherit;
}
@keyframes cdSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
.test-c__avatar::before { animation: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffd23f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.test-c {
margin: 0;
position: relative;
box-sizing: border-box;
width: clamp(240px, 82vw, 360px);
padding: 20px;
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 12px;
}
.test-c__avatar {
position: relative;
flex-shrink: 0;
width: 40px;
height: 40px;
border-radius: 50%;
}
.test-c__avatar::before {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
padding: 4px;
background: conic-gradient(#2f6df6, #ff4d1f, #fff7e6, #17141a, #2f6df6);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
animation: cdSpin 2s linear infinite;
}
.test-c__avatar-fill {
position: absolute;
inset: 4px;
border-radius: 50%;
background: #17141a;
}
.test-c__card {
margin: 0;
min-width: 0;
position: relative;
border-radius: 16px;
padding: 20px;
background: #17141a;
border: 1px solid rgba(255, 255, 255, 0.18);
outline: 1px solid rgba(255, 247, 230, 0.35);
outline-offset: -4px;
}
.test-c__quote {
margin: 0 0 12px;
color: #ffffff;
font-weight: 600;
font-size: clamp(12px, 3vw, 13.5px);
line-height: 1.5;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.test-c__name {
margin: 0;
color: rgba(255, 255, 255, 0.7);
font-weight: 600;
font-size: clamp(10.5px, 2.4vw, 11.5px);
line-height: 1.3;
font-family: inherit;
}
@keyframes cdSpin {
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: reduce) {
.test-c__avatar::before {
animation: none;
}
}import type { CSSProperties } from "react";
import styles from "./TestimonialChrome.module.scss";
export type TestimonialChromeProps = {
quote?: string;
name?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function TestimonialChrome({
quote = "Our click-through rate doubled in launch week.",
name = "Jieun Park · Marketing Lead",
duration = 2000,
easing = "linear",
color = "#2f6df6",
}: TestimonialChromeProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<figure className={styles.test} style={vars}>
<div className={styles.avatar} aria-hidden="true">
<div className={styles.avatarFill} />
</div>
<blockquote className={styles.card}>
<p className={styles.quote}>“{quote}”</p>
<figcaption className={styles.name}>{name}</figcaption>
</blockquote>
</figure>
);
}06 · CDリングアバター引用 — 計測 累積面積 0.397% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る
コードで見る所
maskでリングの形だけを切り出し、内側のアバターの丸と自然に重なるようにしています。
.test-c__avatar::before {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
padding: 3px;
background: conic-gradient($color, $stage-orange, $subject-cream, $stage-ink, $color);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
mask-composite: exclude;
animation: cdSpin $duration linear infinite;
}
@keyframes cdSpin { to { transform: rotate(360deg); } }
07ブラッシュドアルミCTA帯
何が動くか
画面幅いっぱいの帯の上を、半透明のクリーム色と白の斜めストライプが2組background-positionで流れ、ブラッシュドアルミが光を受けながら通り過ぎる瞬間を再現します。

<main class="stage">
<section class="cta-c">
<h3 class="cta-c__title">今日、画面を変えてみましょう</h3>
<button class="cta-c__btn" type="button">無料で始める</button>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop;
$easing: linear;
$color: $subject-cream;
@include stage($stage-orange);
.stage { padding: clamp(8px, 4vw, 24px); }
.cta-c {
position: relative;
overflow: hidden;
box-sizing: border-box;
width: clamp(260px, 90vw, 420px);
border-radius: $r-card;
padding: $sp-8 $sp-6;
background: $stage-ink;
}
// 브러시드 알루미늄 — 대각선 빛줄기 두 가닥이 천천히 흘러간다 (유일한 애니메이션)
.cta-c::before {
content: "";
position: absolute;
inset: 0;
background: repeating-linear-gradient(115deg,
rgba($stage-ink, 0) 0 60px,
rgba($color, .28) 60px 70px,
rgba($stage-ink, 0) 70px 160px,
rgba($stage-paper, .22) 160px 172px,
rgba($stage-ink, 0) 172px 260px);
background-size: 220% 100%;
animation: bandSweep $duration $easing infinite;
}
.cta-c__title {
position: relative;
z-index: 1;
margin: 0 0 $sp-4;
color: $stage-paper;
font-weight: 700;
font-size: clamp(16px, 4vw, 20px);
line-height: 1.3;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.cta-c__btn {
position: relative;
z-index: 1;
border: 0;
border-radius: $r-pill;
background: linear-gradient(100deg, $color, $stage-paper, $color);
color: $stage-ink;
padding: $sp-2 $sp-6;
font-weight: 700;
font-size: clamp(11px, 2.6vw, 13px);
line-height: 1;
font-family: inherit;
}
@keyframes bandSweep { 0% { background-position: 0% 50%; } 100% { background-position: -220% 50%; } }
@media (prefers-reduced-motion: reduce) {
.cta-c::before { animation: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ff4d1f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.stage {
padding: clamp(8px, 4vw, 24px);
}
.cta-c {
position: relative;
overflow: hidden;
box-sizing: border-box;
width: clamp(260px, 90vw, 420px);
border-radius: 16px;
padding: 32px 24px;
background: #17141a;
}
.cta-c::before {
content: "";
position: absolute;
inset: 0;
background: repeating-linear-gradient(115deg, rgba(23, 20, 26, 0) 0 60px, rgba(255, 247, 230, 0.28) 60px 70px, rgba(23, 20, 26, 0) 70px 160px, rgba(255, 255, 255, 0.22) 160px 172px, rgba(23, 20, 26, 0) 172px 260px);
background-size: 220% 100%;
animation: bandSweep 2s linear infinite;
}
.cta-c__title {
position: relative;
z-index: 1;
margin: 0 0 16px;
color: #ffffff;
font-weight: 700;
font-size: clamp(16px, 4vw, 20px);
line-height: 1.3;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.cta-c__btn {
position: relative;
z-index: 1;
border: 0;
border-radius: 999px;
background: linear-gradient(100deg, #fff7e6, #ffffff, #fff7e6);
color: #17141a;
padding: 8px 24px;
font-weight: 700;
font-size: clamp(11px, 2.6vw, 13px);
line-height: 1;
font-family: inherit;
}
@keyframes bandSweep {
0% {
background-position: 0% 50%;
}
100% {
background-position: -220% 50%;
}
}
@media (prefers-reduced-motion: reduce) {
.cta-c::before {
animation: none;
}
}import type { CSSProperties } from "react";
import styles from "./CtaBandChrome.module.scss";
export type CtaBandChromeProps = {
title?: string;
cta?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function CtaBandChrome({
title = "Switch your screen today",
cta = "Start for free",
duration = 2000,
easing = "linear",
color = "#fff7e6",
}: CtaBandChromeProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<section className={styles.cta} style={vars}>
<h3 className={styles.title}>{title}</h3>
<button type="button" className={styles.btn}>{cta}</button>
</section>
);
}07 · ブラッシュドアルミCTA帯 — 計測 累積面積 36.008% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る
コードで見る所
帯全体が被写体になるため、この編の中で変化するピクセル面積が最も広い項目です(36.01%)。
.cta-c::before {
content: "";
position: absolute;
inset: 0;
background: repeating-linear-gradient(115deg,
rgba($stage-ink, 0) 0 60px,
rgba($color, .28) 60px 70px,
rgba($stage-ink, 0) 70px 160px,
rgba($stage-paper, .22) 160px 172px,
rgba($stage-ink, 0) 172px 260px);
background-size: 220% 100%;
animation: bandSweep $duration linear infinite;
}
08ホログラフィック罫線フッター
何が動くか
フッター上端の境界に3pxの細い線を引き、その中に4色グラデーションを敷いてbackground-positionで流します。

<main class="stage">
<footer class="footer-c">
<div class="footer-c__col">
<p class="footer-c__label">プロダクト</p>
<p class="footer-c__link">料金</p>
</div>
<div class="footer-c__col">
<p class="footer-c__label">会社</p>
<p class="footer-c__link">チーム紹介</p>
</div>
</footer>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop;
$easing: linear;
$color: $subject-blue;
@include stage($stage-paper);
.footer-c {
position: relative;
box-sizing: border-box;
width: clamp(260px, 88vw, 400px);
border-radius: $r-card;
padding: $sp-6;
background: $stage-ink;
display: grid;
grid-template-columns: 1fr 1fr;
gap: $sp-4;
}
// 상단 경계의 홀로그램 실선 — 색이 자리를 옮기며 은은하게 반짝인다 (유일한 애니메이션)
.footer-c::before {
content: "";
position: absolute;
top: 0;
left: $sp-6;
right: $sp-6;
height: 3px;
border-radius: $r-pill;
background: linear-gradient(90deg, $color, $stage-yellow, $stage-orange, $subject-cream, $color);
background-size: 260% 100%;
animation: hairlineShift $duration $easing infinite;
}
.footer-c__col { position: relative; z-index: 1; border-radius: $r-sm; background: rgba($stage-paper, .05); padding: $sp-3; display: flex; flex-direction: column; gap: $sp-1; }
.footer-c__label {
margin: 0;
color: rgba($stage-paper, .5);
font-weight: 700;
font-size: clamp(9.5px, 2.2vw, 10.5px);
line-height: 1;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
text-transform: uppercase;
letter-spacing: .06em;
}
.footer-c__link {
margin: 0;
color: $stage-paper;
font-weight: 600;
font-size: clamp(11px, 2.6vw, 12px);
line-height: 1.4;
font-family: inherit;
}
@keyframes hairlineShift { 0% { background-position: 0% 50%; } 100% { background-position: 260% 50%; } }
@media (prefers-reduced-motion: reduce) {
.footer-c::before { animation: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffffff;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.footer-c {
position: relative;
box-sizing: border-box;
width: clamp(260px, 88vw, 400px);
border-radius: 16px;
padding: 24px;
background: #17141a;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.footer-c::before {
content: "";
position: absolute;
top: 0;
left: 24px;
right: 24px;
height: 3px;
border-radius: 999px;
background: linear-gradient(90deg, #2f6df6, #ffd23f, #ff4d1f, #fff7e6, #2f6df6);
background-size: 260% 100%;
animation: hairlineShift 2s linear infinite;
}
.footer-c__col {
position: relative;
z-index: 1;
border-radius: 8px;
background: rgba(255, 255, 255, 0.05);
padding: 12px;
display: flex;
flex-direction: column;
gap: 4px;
}
.footer-c__label {
margin: 0;
color: rgba(255, 255, 255, 0.5);
font-weight: 700;
font-size: clamp(9.5px, 2.2vw, 10.5px);
line-height: 1;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.footer-c__link {
margin: 0;
color: #ffffff;
font-weight: 600;
font-size: clamp(11px, 2.6vw, 12px);
line-height: 1.4;
font-family: inherit;
}
@keyframes hairlineShift {
0% {
background-position: 0% 50%;
}
100% {
background-position: 260% 50%;
}
}
@media (prefers-reduced-motion: reduce) {
.footer-c::before {
animation: none;
}
}import type { CSSProperties } from "react";
import styles from "./FooterChrome.module.scss";
export type FooterCol = { label: string; link: string };
export type FooterChromeProps = {
columns?: FooterCol[];
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function FooterChrome({
columns = [
{ label: "Product", link: "Pricing" },
{ label: "Company", link: "About" },
],
duration = 2000,
easing = "linear",
color = "#2f6df6",
}: FooterChromeProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<footer className={styles.footer} style={vars}>
{columns.map((col) => (
<div key={col.label} className={styles.col}>
<p className={styles.label}>{col.label}</p>
<p className={styles.link}>{col.link}</p>
</div>
))}
</footer>
);
}08 · ホログラフィック罫線フッター — 計測 累積面積 0.733% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る
コードで見る所
線自体が細いため変化するピクセル面積はこの編で最も小さい項目です(0.73%)が、色の対比がはっきりしているぶん目にはくっきり光って見えます。
.footer-c::before {
content: "";
position: absolute;
top: 0; left: $sp-6; right: $sp-6; height: 3px;
border-radius: $r-pill;
background: linear-gradient(90deg, $color, $stage-yellow, $stage-orange, $subject-cream, $color);
background-size: 260% 100%;
animation: hairlineShift $duration linear infinite;
}
09クロムボタン・ホログラフィックフォーカス
何が動くか
ボタンを押すと、上にあった明るいハイライトが下へ反転し、実際の金属が押し込まれるような感触を生みます。

<main class="stage">
<form class="bi-c is-demo">
<input class="bi-c__input" type="email" placeholder="メールアドレス" readonly>
<button class="bi-c__btn" type="button">登録する</button>
</form>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop;
$easing: $ease-out;
$color: $subject-cream;
@include stage($stage-ink);
.bi-c { display: flex; flex-direction: column; gap: $sp-4; width: clamp(220px, 78vw, 320px); }
.bi-c__input {
border: 0;
border-radius: $r-control;
padding: $sp-3 $sp-4;
background: rgba($stage-paper, .06);
color: $stage-paper;
font-weight: 500;
font-size: clamp(12px, 3vw, 13.5px);
line-height: 1.3;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
box-shadow: 0 0 0 2px rgba($subject-blue, .5), 0 0 16px rgba($stage-yellow, .4), 0 0 22px rgba($stage-orange, .3);
}
.bi-c__input::placeholder { color: rgba($stage-paper, .4); }
// 버튼 — 위에 있던 스펙큘러 하이라이트가 눌리면 아래로 뒤집힌다 (유일한 애니메이션, .is-demo 로 강제 발동)
.bi-c__btn {
position: relative;
overflow: hidden;
align-self: flex-start;
border: 0;
border-radius: $r-control;
padding: $sp-3 $sp-6;
color: $stage-ink;
font-weight: 700;
font-size: clamp(12px, 3vw, 13.5px);
line-height: 1;
font-family: inherit;
background: linear-gradient(180deg, $color, $stage-paper 55%, rgba($stage-paper, .85));
}
.bi-c__btn::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba($stage-paper, .9), transparent 55%);
}
.is-demo .bi-c__btn::after,
.bi-c__btn:hover::after,
.bi-c__btn:focus-visible::after {
animation: pressFlip $duration $easing infinite;
}
@keyframes pressFlip {
0%, 40% { transform: translateY(0); opacity: 1; }
50%, 60% { transform: translateY(100%); opacity: .7; }
70%, 100% { transform: translateY(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.is-demo .bi-c__btn::after, .bi-c__btn:hover::after, .bi-c__btn:focus-visible::after { animation: none; }
}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);
}
.bi-c {
display: flex;
flex-direction: column;
gap: 16px;
width: clamp(220px, 78vw, 320px);
}
.bi-c__input {
border: 0;
border-radius: 12px;
padding: 12px 16px;
background: rgba(255, 255, 255, 0.06);
color: #ffffff;
font-weight: 500;
font-size: clamp(12px, 3vw, 13.5px);
line-height: 1.3;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
box-shadow: 0 0 0 2px rgba(47, 109, 246, 0.5), 0 0 16px rgba(255, 210, 63, 0.4), 0 0 22px rgba(255, 77, 31, 0.3);
}
.bi-c__input::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.bi-c__btn {
position: relative;
overflow: hidden;
align-self: flex-start;
border: 0;
border-radius: 12px;
padding: 12px 24px;
color: #17141a;
font-weight: 700;
font-size: clamp(12px, 3vw, 13.5px);
line-height: 1;
font-family: inherit;
background: linear-gradient(180deg, #fff7e6, #ffffff 55%, rgba(255, 255, 255, 0.85));
}
.bi-c__btn::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.9), transparent 55%);
}
.is-demo .bi-c__btn::after,
.bi-c__btn:hover::after,
.bi-c__btn:focus-visible::after {
animation: pressFlip 2s ease-out infinite;
}
@keyframes pressFlip {
0%, 40% {
transform: translateY(0);
opacity: 1;
}
50%, 60% {
transform: translateY(100%);
opacity: 0.7;
}
70%, 100% {
transform: translateY(0);
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.is-demo .bi-c__btn::after, .bi-c__btn:hover::after, .bi-c__btn:focus-visible::after {
animation: none;
}
}import type { CSSProperties } from "react";
import styles from "./ButtonsInputsChrome.module.scss";
export type ButtonsInputsChromeProps = {
placeholder?: string;
cta?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function ButtonsInputsChrome({
placeholder = "Email address",
cta = "Sign up",
duration = 2000,
easing = "ease-out",
color = "#fff7e6",
}: ButtonsInputsChromeProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<form className={styles.form} style={vars}>
<input className={styles.input} type="email" placeholder={placeholder} readOnly />
<button type="button" className={styles.btn}>{cta}</button>
</form>
);
}09 · クロムボタン・ホログラフィックフォーカス — 計測 累積面積 1.061% · 動いたフレーム 6/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る
コードで見る所
入力欄は動かず、青・黄・オレンジの3色box-shadowグローがいつも静かに光っています。
.bi-c__btn::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba($stage-paper, .9), transparent 55%);
}
.bi-c__btn:hover::after, .bi-c__btn:focus-visible::after {
animation: pressFlip $duration $ease-out infinite;
}
.bi-c__input {
box-shadow: 0 0 0 2px rgba($subject-blue, .5), 0 0 16px rgba($stage-yellow, .4), 0 0 22px rgba($stage-orange, .3);
}
tailwind.config カスタム
9個の部品が共有する色と半径の値はこの設定1つだけにまとまっているので、ここで色を変えれば全部まとめて追従します——ただし1つだけ例外があります。backgroundImage.chromeユーティリティは上のcolorsを参照せず、同じ6つのhex値をリテラルのまま書いているため、colorsだけを直してもこのユーティリティは自動では変わりません。色を変えるたびにこの2箇所を一緒に直す必要があります。
theme: {
extend: {
colors: {
ink: "#17141a", cream: "#fff7e6", paper: "#ffffff",
blue: "#2f6df6", yellow: "#ffd23f", orange: "#ff4d1f",
},
borderRadius: { tile: "16px", control: "12px", pill: "999px" },
backgroundImage: {
chrome: "linear-gradient(100deg, #ffffff, #fff7e6, #ffd23f, #2f6df6, #ff4d1f, #ffffff)",
},
},
},
バイブコーディング プロンプト
上の設定を先にプロジェクトへ入れておけば、あとはClaude CodeやCursorに下の断片を渡すだけで画面がこのスタイルに変わります。プロンプトの全文はzip内のprompt.mdに入っています。
見出しは background-clip: text で文字の形だけ残し、白→クリーム→黄→青→
オレンジ→白の6段グラデーションを background-position のアニメーションだけで
流す。
カード表面のホログラフィック帯は mix-blend-mode: overlay で、インク色の
カードにだけ重ねる。
ボタンを押すとハイライトが上から下へ反転する感触を出す。
この断片を実際にpage/index.htmlへ適用し、1280px幅のビューポートでページ全体をキャプチャした結果が下のスクリーンショットです。

どこで壊れるか — 落とし穴
y2k デザイン、どこで壊れるか?
クロム・ホログラフィック効果は、曲線が滑らかすぎるとかえって動いていないように測定されます。04番のクロムボールアイコンを最初にease-outで往復させて作ったところ、レンダー測定の強度が14.6で、通過基準(15)にわずか0.4足りませんでした。タイミングを8段階のsteps(8, jump-none)に変え、動く範囲も広げたところ、強度は96.2まで上がりました——階段状にはっきり切り替えないと、フレームごとの差が測定に乗らないということです。06番のCDリングアバターは別種の落とし穴でした。アバターと引用カードを縦に積んだところ、幅320pxの画面で縦にはみ出してしまい(測定値もそのまま「はみ出しあり」)、2つを横に並べ直してようやく狭い画面でもスクロールバーなしに収まりました。クロムグラデーションをメール本文へ移すとどこまで残るかは、メルマガ デザイン9種のほうで別に確かめています。9個の部品とワンページ全体のソースをまとめた圧縮ファイルは、開くときにパスワードhxqy3hr4が必要で、ボタンを押せばすぐダウンロードが始まります。
最後の落とし穴は抑制です。9個すべてに同時にグラデーション・回転・グローを入れると、画面は輝いて見えるのではなくうるさく見えます。クロム効果は画面の中で1〜2箇所だけに絞り、残りはインク色のマットのまま沈めておくと、光る場所がはっきり際立ちます。
アクセシビリティ
押さえた点
09番ボタンのハイライト反転を含め、01・02・03・04・05・06・07・08番のグラデーションの流れや回転はすべてMDNのprefers-reduced-motionドキュメントが定義するメディアクエリで包んでおり、動きを減らす設定をオンにした訪問者には静止したグラデーションだけが見えます。06番のようにmask-compositeでリング状に切り出す項目はMDNのmask-compositeドキュメントを参考に、旧サファリ向けの-webkit-mask-compositeもあわせて指定しています。見出しやボタンの文字は、グラデーションが流れている間も常にインク色の背景に明るい文字、または明るい背景にインク色の文字という組み合わせを保ち、コントラストが崩れないようにしています。
次に見る所
クロム以外の流行スタイルも見てみたい方はデザイントレンドカテゴリーに他の編がまとまっており、このサイト全体の性格は紹介ページで確認できます。
FAQ
y2k デザイン 作り方として、クロム・ホログラフィック効果はCSSだけで作れますか?
作れます。この9個の部品にJavaScriptは1行も使っていません。09番ボタンの押し込み反応さえ:hover・:focus-visibleの2つの疑似クラスだけで組んであり、スクリプトはgifやmp4でデモを収録するときにアニメーションを強制的に再生させる道具の中だけに登場します。
クロムグラデーションの文字はどのブラウザでも表示されますか?
background-clip: textは最近のブラウザではおおむね対応していますが、-webkit-background-clipも併記しておくと旧サファリや一部のモバイルブラウザで安全です。対応状況はcaniuseで確認でき、未対応環境向けにcolorのフォールバックを用意しておくと文字が消えずに済みます。
ホログラフィックのカード効果は明るい背景でも使えますか?
グラデーション自体は明るい背景でも使えますが、この編のmix-blend-mode: overlayによる箔の帯は濃いインク色のカードを前提にコントラストを作っています。明るい背景では混合モードをmultiplyかsoft-lightに変え、帯の不透明度を下げないと文字のコントラストが崩れます。明るい地の上に声のカードを置く形だけを集めたのがお客様の声 デザインの型9選です。









