05文字塗りつぶし
見出しの文字色が読んだ割合だけ左から満ちる。background-clip: text で描く。
使いどころヒーローの見出し、記事タイトルの強調
background-clip:textlinear-gradient두 색

<main class="stage">
<h2 class="headline is-demo">読んだ分だけ満ちる見出し</h2>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$color: $subject-blue; // 읽은 구간 색
$dim: rgba(255, 255, 255, .32); // 안 읽은 구간 색
$duration: $dur-loop;
@include stage($stage-ink);
// 퍼센트라야 그라데이션 경계가 매끄럽게 보간된다
@property --p {
syntax: '<percentage>';
initial-value: 0%;
inherits: false;
}
.headline {
--p: 0%;
max-width: 80%;
margin: 0;
text-align: center;
font-weight: 800;
font-size: clamp(20px, 6vw, 28px);
line-height: 1.35;
font-family: inherit;
background-image: linear-gradient(90deg, $color var(--p), $dim var(--p));
background-clip: text;
-webkit-background-clip: text;
color: transparent;
// 실제 사용: 헤드라인 색이 스크롤 진행률만큼 왼쪽부터 채워진다
@supports (animation-timeline: scroll()) {
animation: fill-text linear both;
animation-timeline: scroll(root);
}
}
@keyframes fill-text { from { --p: 0%; } to { --p: 100%; } }
.headline.is-demo { animation: fill-text-loop $duration linear infinite; }
@keyframes fill-text-loop {
0% { --p: 0%; }
92% { --p: 100%; }
100% { --p: 100%; }
}
@media (prefers-reduced-motion: reduce) {
.headline, .headline.is-demo { animation: none !important; --p: 100%; }
}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);
}
@property --p {
syntax: "<percentage>";
initial-value: 0%;
inherits: false;
}
.headline {
--p: 0%;
max-width: 80%;
margin: 0;
text-align: center;
font-weight: 800;
font-size: clamp(20px, 6vw, 28px);
line-height: 1.35;
font-family: inherit;
background-image: linear-gradient(90deg, #2f6df6 var(--p), rgba(255, 255, 255, 0.32) var(--p));
background-clip: text;
-webkit-background-clip: text;
color: transparent;
}
@supports (animation-timeline: scroll()) {
.headline {
animation: fill-text linear both;
animation-timeline: scroll(root);
}
}
@keyframes fill-text {
from {
--p: 0%;
}
to {
--p: 100%;
}
}
.headline.is-demo {
animation: fill-text-loop 2s linear infinite;
}
@keyframes fill-text-loop {
0% {
--p: 0%;
}
92% {
--p: 100%;
}
100% {
--p: 100%;
}
}
@media (prefers-reduced-motion: reduce) {
.headline, .headline.is-demo {
animation: none !important;
--p: 100%;
}
}import { useEffect, useState } from "react";
import type { CSSProperties, ReactNode } from "react";
import styles from "./TextFill.module.scss";
export type TextFillProps = {
children: ReactNode;
/** 읽은 구간 색 */
color?: string;
/** 안 읽은 구간 색 */
dim?: string;
};
type Vars = CSSProperties & { "--p"?: string; "--color"?: string; "--dim"?: string };
// 실제 쓰임: 헤드라인 색이 스크롤 진행률만큼 왼쪽부터 채워진다.
export default function TextFill({ children, color = "#2f6df6", dim = "rgba(255,255,255,.32)" }: TextFillProps) {
const [percent, setPercent] = useState(0);
useEffect(() => {
const onScroll = () => {
const max = document.documentElement.scrollHeight - window.innerHeight;
setPercent(max > 0 ? Math.min(100, (window.scrollY / max) * 100) : 0);
};
onScroll();
window.addEventListener("scroll", onScroll, { passive: true });
return () => window.removeEventListener("scroll", onScroll);
}, []);
const vars: Vars = { "--p": `${percent}%`, "--color": color, "--dim": dim };
return <h2 className={styles.headline} style={vars}>{children}</h2>;
}05 文字塗りつぶし — 計測 累積面積 2.938% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · 【コピペOK】プログレスバー css アニメーション 9選
このデモが属する記事
【コピペOK】プログレスバー css アニメーション 9選プログレスバー css アニメーションは、スクロールした量だけを手がかりに「今どこまで読んだか」を見せるCSSの部品です。CSSだけ・JS不要で、上部バー・円形バッジ・点ナビ・縦バー・文字塗り・数値・章ステップ・目次・残り時間の9種類を集め この記事のデモをすべて見る


