06Highlighter sweep
When scroll reaches it, a highlighter color sweeps in behind the key phrase from left to right.
Best forSection headline emphasis, sale copy
background-sizeview()글자 안 건드림

<main class="stage">
<div class="mk">
<div class="mk__scroll">
<div class="mk__spacer"></div>
<h2 class="mk__title">This season <span class="mk__hl is-demo">one single sale</span></h2>
<p class="mk__sub">The highlight sweeps in like a marker</p>
<div class="mk__spacer"></div>
</div>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-loop;
$easing: $ease-out;
$color: $subject-blue;
@include stage($stage-yellow);
.mk { width: min(400px, 88vw); }
.mk__scroll {
height: clamp(150px, 62vh, 216px);
overflow-y: auto;
scrollbar-width: none;
&::-webkit-scrollbar { display: none; }
}
.mk__spacer { height: 30%; }
.mk__title {
margin: 0;
padding: 0 $sp-4;
text-align: center;
color: $subject-ink;
font: 800 clamp(22px, 5.5vw, 30px)/1.5 -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
letter-spacing: -.01em;
}
.mk__sub {
margin: $sp-3 0 0;
text-align: center;
color: rgba(23, 20, 26, .72);
font: 600 13px/1.5 -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
// ── 실제 동작: 형광펜 = 배경 그라디언트의 폭. 글자는 그대로 두고 배경만 자란다 ──
@supports (animation-timeline: view()) {
.mk__hl:not(.is-demo) {
animation: mk-sw linear both;
animation-timeline: view();
animation-range: entry 30% cover 60%;
}
}
@supports not (animation-timeline: view()) {
// 미지원 브라우저(Firefox·Safari): 강조가 처음부터 끝까지 그어져 있다
.mk__hl:not(.is-demo) { background-size: 100% 58%; }
}
.mk__hl {
background: linear-gradient($color, $color) no-repeat 0 62%;
background-size: 0% 58%;
border-radius: $r-xs;
padding: 0 $sp-1;
}
@keyframes mk-sw {
from { background-size: 0% 58%; }
to { background-size: 100% 58%; }
}
// 데모 루프: 실제 스크롤 대신 형광펜이 좌에서 우로 그어지는 걸 흉내(캡처용)
.mk__hl.is-demo { animation: mk-loop $duration $easing infinite; }
@keyframes mk-loop {
0%, 10% { background-size: 0% 58%; }
55%, 88% { background-size: 100% 58%; }
100% { background-size: 0% 58%; }
}
@media (prefers-reduced-motion: reduce) {
.mk__hl.is-demo { animation: none !important; background-size: 100% 58% !important; }
.mk__scroll { scroll-behavior: auto; }
}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);
}
.mk {
width: min(400px, 88vw);
}
.mk__scroll {
height: clamp(150px, 62vh, 216px);
overflow-y: auto;
scrollbar-width: none;
}
.mk__scroll::-webkit-scrollbar {
display: none;
}
.mk__spacer {
height: 30%;
}
.mk__title {
margin: 0;
padding: 0 16px;
text-align: center;
color: #17141a;
font: 800 clamp(22px, 5.5vw, 30px)/1.5 -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
letter-spacing: -0.01em;
}
.mk__sub {
margin: 12px 0 0;
text-align: center;
color: rgba(23, 20, 26, 0.72);
font: 600 13px/1.5 -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
@supports (animation-timeline: view()) {
.mk__hl:not(.is-demo) {
animation: mk-sw linear both;
animation-timeline: view();
animation-range: entry 30% cover 60%;
}
}
@supports not (animation-timeline: view()) {
.mk__hl:not(.is-demo) {
background-size: 100% 58%;
}
}
.mk__hl {
background: linear-gradient(#2f6df6, #2f6df6) no-repeat 0 62%;
background-size: 0% 58%;
border-radius: 4px;
padding: 0 4px;
}
@keyframes mk-sw {
from {
background-size: 0% 58%;
}
to {
background-size: 100% 58%;
}
}
.mk__hl.is-demo {
animation: mk-loop 2s ease-out infinite;
}
@keyframes mk-loop {
0%, 10% {
background-size: 0% 58%;
}
55%, 88% {
background-size: 100% 58%;
}
100% {
background-size: 0% 58%;
}
}
@media (prefers-reduced-motion: reduce) {
.mk__hl.is-demo {
animation: none !important;
background-size: 100% 58% !important;
}
.mk__scroll {
scroll-behavior: auto;
}
}// 실제 동작: view() 타임라인이 background-size 를 0% → 100% 로 늘린다(0줄).
// is-demo 클래스는 캡처용 미리보기 루프일 뿐 — 실제 스크롤 반응은 그대로 살아있다.import type { CSSProperties } from "react";
import styles from "./HighlighterSweep.module.scss";
export type HighlighterSweepProps = {
duration?: number;
easing?: string;
color?: string;
before?: string;
highlight?: string;
after?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function HighlighterSweep({
duration = 300,
easing = "linear",
color = "#2f6df6",
before = "이번 시즌",
highlight = "단 하나의 세일",
after,
}: HighlighterSweepProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<div className={styles.mk} style={vars}>
{/* 형광펜 = 배경 그라디언트의 폭(view() 타임라인). 글자는 그대로 두고 배경만 자란다 */}
<div className={styles.scroll}>
<div className={styles.spacer} />
<h2 className={styles.title}>
{before} <span className={styles.hl}>{highlight}</span> {after}
</h2>
<div className={styles.spacer} />
</div>
</div>
);
}06 Highlighter sweep — Measured area 1.592% · moving frames 14/23Original by GODRICH · SCSS and React ship in the zip · 9 Website Scroll Effects — Apple-Style, CSS Only
The post this demo lives in
9 Website Scroll Effects — Apple-Style, CSS OnlyWebsite scroll effects, done with no scroll library at all, feel the way Apple's product pages do. Nine of them are here See all demos in this post


