06밝기 값이 층마다 달라 광택이 도는 금속 글자
그림자 층마다 밝기 값을 조금씩 다르게 겹쳐, 위쪽은 밝고 아래쪽은 어두운 금속 광택처럼 보인다.
쓰임프리미엄 브랜드 로고 타입
SHEEN_LAYER_COUNTbuildSheenShadowrenderSheen

<main class="stage">
<div class="mt is-demo" id="mt">
<p class="mt__badge"><span class="mt__badgeLabel">광택 각도</span> <span class="mt__badgeValue" id="mtValue">0deg</span></p>
<h1 class="mt__word" id="mtWord">금속</h1>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// 핵심: 각도 값 하나로 SHEEN_LAYER_COUNT 층마다 밝기를 사인 곡선을 따라 다르게 겹친다.
// 각도가 돌면 밝은 띠가 층을 타고 흘러 금속 표면에 빛이 지나가는 것처럼 보인다.
// 상표명 없이 「금속 광택」이라고만 부른다.
$duration: $dur-loop;
$easing: linear;
$color: $subject-ink;
@include stage($stage-yellow);
.mt {
display: grid;
gap: $sp-4;
justify-items: center;
text-align: center;
}
.mt__badge {
margin: 0;
padding: $sp-1 $sp-3;
border-radius: $r-pill;
background: rgba(23, 20, 26, .08);
color: $subject-ink;
font-weight: 700;
font-size: 11px;
font-variant-numeric: tabular-nums;
}
.mt__word {
margin: 0;
font-weight: 800;
font-size: clamp(72px, 24vw, 140px);
line-height: 1;
white-space: nowrap;
color: $color;
will-change: text-shadow;
}
@media (max-width: 340px) {
.mt__word { font-size: clamp(52px, 26vw, 92px); }
}
@media (prefers-reduced-motion: reduce) {
.mt__word { transition: 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);
}
.mt {
display: grid;
gap: 16px;
justify-items: center;
text-align: center;
}
.mt__badge {
margin: 0;
padding: 4px 12px;
border-radius: 999px;
background: rgba(23, 20, 26, 0.08);
color: #17141a;
font-weight: 700;
font-size: 11px;
font-variant-numeric: tabular-nums;
}
.mt__word {
margin: 0;
font-weight: 800;
font-size: clamp(72px, 24vw, 140px);
line-height: 1;
white-space: nowrap;
color: #17141a;
will-change: text-shadow;
}
@media (max-width: 340px) {
.mt__word {
font-size: clamp(52px, 26vw, 92px);
}
}
@media (prefers-reduced-motion: reduce) {
.mt__word {
transition: none;
}
}(function () {
var SHEEN_LAYER_COUNT = 7;
var PERIOD_MS = 900;
var word = document.getElementById('mtWord');
var value = document.getElementById('mtValue');
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// 각도 값 하나로 층마다 밝기를 사인 곡선을 따라 다르게 겹쳐, 위·아래가 서로 다르게 빛나는
// 금속 광택을 만든다. 층 위상(phase)이 갈릴수록 밝은 띠가 층을 타고 흐른다
function buildSheenShadow(angleDeg) {
var rad = (angleDeg * Math.PI) / 180;
var parts = [];
for (var i = 1; i <= SHEEN_LAYER_COUNT; i++) {
var phase = rad + (i / SHEEN_LAYER_COUNT) * Math.PI * 1.4;
var bright = (Math.sin(phase) + 1) / 2;
var l = Math.round(22 + bright * 58);
var y = (i * 6.8).toFixed(2);
parts.push('0px ' + y + 'px 0 hsl(220 10% ' + l + '%)');
}
return parts.join(', ');
}
function renderSheen(angleDeg) {
word.style.textShadow = buildSheenShadow(angleDeg);
value.textContent = Math.round(angleDeg % 360) + 'deg';
}
var startTime = null;
function loop(now) {
if (startTime === null) startTime = now;
var t = (now - startTime) % PERIOD_MS;
var angle = (t / PERIOD_MS) * 360;
renderSheen(angle);
requestAnimationFrame(loop);
}
if (reduce) {
renderSheen(90);
} else {
requestAnimationFrame(loop);
}
})();// TextShadowMetalSheenLayer — vanilla 판(run/412/demo/06-text-shadow-metal-sheen-layer/index.html) 의 React 포트.
// 각도 값 하나로 SHEEN_LAYER_COUNT 층마다 밝기를 사인 곡선을 따라 다르게 겹친다.
// 각도가 돌면 밝은 띠가 층을 타고 흘러 금속 표면에 빛이 지나가는 것처럼 보인다.
import { useEffect, useRef, useState } from "react";
import styles from "./TextShadowMetalSheenLayer.module.scss";
type Box<T> = { current: T };
const SHEEN_LAYER_COUNT = 7;
const PERIOD_MS = 900;
function buildSheenShadow(angleDeg: number): string {
const rad = (angleDeg * Math.PI) / 180;
const parts: string[] = [];
for (let i = 1; i <= SHEEN_LAYER_COUNT; i++) {
const phase = rad + (i / SHEEN_LAYER_COUNT) * Math.PI * 1.4;
const bright = (Math.sin(phase) + 1) / 2;
const l = Math.round(22 + bright * 58);
const y = (i * 6.8).toFixed(2);
parts.push("0px " + y + "px 0 hsl(220 10% " + l + "%)");
}
return parts.join(", ");
}
export default function TextShadowMetalSheenLayer() {
const wordRef = useRef<HTMLHeadingElement | null>(null);
const valueRef = useRef<HTMLSpanElement | null>(null);
const t0Ref = useRef<number>(0) as Box<number>;
const frameRef = useRef<number>(0) as Box<number>;
const [demo] = useState(true);
const [reduce, setReduce] = useState(false);
function render(angleDeg: number): void {
const word = wordRef.current;
const value = valueRef.current;
if (!word || !value) return;
word.style.textShadow = buildSheenShadow(angleDeg);
value.textContent = Math.round(angleDeg % 360) + "deg";
}
useEffect(() => {
setReduce(window.matchMedia("(prefers-reduced-motion: reduce)").matches);
}, []);
useEffect(() => {
if (reduce) {
render(90);
return;
}
function loop(now: number): void {
if (t0Ref.current === 0) t0Ref.current = now;
const t = (now - t0Ref.current) % PERIOD_MS;
const angle = (t / PERIOD_MS) * 360;
render(angle);
frameRef.current = requestAnimationFrame(loop);
}
frameRef.current = requestAnimationFrame(loop);
return () => cancelAnimationFrame(frameRef.current);
}, [reduce]);
const cls = [styles.mt, demo ? styles.isDemo : ""].filter(Boolean).join(" ");
return (
<main className={styles.stage}>
<div className={cls}>
<p className={styles.badge}><span>광택 각도</span> <span ref={valueRef}>0deg</span></p>
<h1 className={styles.word} ref={wordRef}>금속</h1>
</div>
</main>
);
}06 밝기 값이 층마다 달라 광택이 도는 금속 글자 — 측정 누적면적 10.386% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · css 텍스트 그림자 효과 9가지 — 층 값이 입체를 만든다
이 데모가 속한 글
css 텍스트 그림자 효과 9가지 — 층 값이 입체를 만든다css 텍스트 그림자 효과는 text-shadow 값 하나를 층 문자열로 계산해 조립하는 아홉 가지 패턴이다. 그림자는 빛이 사라진 자리인데, 이 아홉 글자는 그림자만으로 빛을 대신한다 — 01번은 겹친 층 값으로 이 글의 데모 전부 보기


