designpaca/apps/site/src/components/Metrics.astro
Yun Chan 0e03bbafac 패럴랙스에 기준면을 준다 — 배경 격자와 광원 레이어
배경이 단색이면 패럴랙스는 성립하지 않는다. 앞의 것이 몇 px 어긋나도
비교할 대상이 없다. 히어로 말고는 기준면이 없어 시차가 안 보였다.

배경 이미지를 사 오는 대신 이 페이지가 이미 주장하는 것을 그렸다
  .bg-grid  간격 눈금 격자. 셀이 --grid-cell = --space-6
            파이프라인 · 수치. "모든 값이 토큰에서 나온다"가 배경에서도 성립
  .bg-glow  광원 블롭. 섹션마다 --glow-x 로 자리를 옮긴다
            재료 · 설치. 히어로의 빛이 마지막 CTA 에서 돌아온다

실측 3층 (스크롤 1000 -> 1600)
  격자 +14.9px  ·  산출물 -14px  ·  단계명 0
  한 층은 반드시 고정이다. 전부 움직이면 그냥 흔들리는 페이지다

그리드 wide 트랙을 좁은 화면에서 닫는다
  고정 10rem 이라 모바일에서도 좌우 80px 이 살아남아, 390px 에서 본문이
  x=112 부터 시작하고 폭이 278px 로 줄어 제목이 네 줄로 접혔다.
  --wide-pad 를 clamp 로 바꿔 닫았다. 이제 전부 x=32 에서 시작하고 두 줄이다

파이프라인 정렬선을 하나로
  제목 x=371 / 리드 x=435 / 스파인 x=143 이던 것을 전부 x=183 으로.
  제목·리드·브리프를 스파인과 같은 컨테이너에 넣었다

히어로 높이를 콘텐츠에 맞춘다
  모바일에서 콘텐츠 390px 에 736px 을 씌워 여백이 화면의 41% 였고
  다음 섹션이 화면 밖으로 밀려 스크롤 단서까지 사라졌다.
  모바일 min-height 제거(736 -> 554), 데스크톱 88dvh -> 78dvh
  배경 마스크 62% -> 82% 로 늘려 하단의 죽은 띠를 없앴다

스킬 motion.md 에 "패럴랙스에는 기준면이 필요하다" 를 추가.
페이지가 주장하는 것을 배경으로 그리는 표와, 한 층은 고정이라는 규칙.

390px / 1440px 실렌더: 가로 스크롤 0, 대비 실패 0. 테스트 22개 통과
2026-08-20 14:25:02 +09:00

120 lines
3.8 KiB
Text

---
import type { Content } from "../i18n/content";
interface Props { c: Content["metrics"]; }
const { c } = Astro.props;
const feature = c.items.filter((m) => m.feature);
const rest = c.items.filter((m) => !m.feature);
---
{/*
레이아웃 패밀리: 수치 조판.
카드에 담지 않는다. 숫자가 크기와 정렬선으로 스스로 위계를 만든다.
카드는 elevation 이 진짜 위계를 나를 때만 쓴다는 규칙을 여기서 지킨다.
*/}
{/*
이 섹션에는 패럴랙스를 넣지 않는다. 빠뜨린 것이 아니라 결정이다.
여기가 이 페이지의 수치를 공개하는 자리다. 숫자가 스크롤에 따라
흔들리면 그 값도 흔들리는 것처럼 읽힌다. 계기판의 바늘은 떨지 않는다.
움직임이 의미를 만드는 자리가 있으면, 움직이지 않는 것이 의미인 자리도 있다.
*/}
<section id="metrics" class="section metrics bg-grid" aria-labelledby="metrics-h">
<div class="head">
<p class="eyebrow reveal">{c.eyebrow}</p>
<h2 id="metrics-h" class="reveal">{c.h2}</h2>
</div>
<p class="lead metrics-lead reveal">{c.lead}</p>
<div class="full figures">
{
feature.map((m, i) => (
<div class="fig fig-lead reveal" style={`--reveal-delay:${i * 80}ms`}>
<span class="fig-value nums">{m.value}</span>
<span class="fig-label">{m.label}</span>
<span class="fig-note muted">{m.note}</span>
</div>
))
}
<dl class="fig-rest">
{
rest.map((m, i) => (
<div class="row reveal" style={`--reveal-delay:${(i + 2) * 50}ms`}>
<dt>{m.label}</dt>
<dd class="nums">{m.value}</dd>
<dd class="row-note muted">{m.note}</dd>
</div>
))
}
</dl>
</div>
<p class="caveat muted reveal">{c.caveat}</p>
</section>
<style>
.metrics-lead { margin-block: var(--space-4) var(--space-7); max-width: 30em; }
.figures {
max-width: 1180px;
margin-inline: auto;
padding-inline: var(--gutter);
display: grid;
/* 비대칭. 큰 수치 둘이 왼쪽을 차지하고 나머지는 오른쪽 여백에 흐른다 */
grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
column-gap: var(--space-7);
row-gap: var(--space-6);
align-items: start;
}
.fig-lead { display: grid; gap: var(--space-1); }
.fig-lead:nth-child(2) {
/* 두 번째 큰 수치는 한 칸 내려앉는다 */
margin-top: var(--space-6);
}
.fig-value {
font-size: clamp(3.4rem, 2rem + 5vw, 6rem);
line-height: 0.92;
font-weight: var(--weight-strong);
letter-spacing: -0.04em;
color: var(--accent);
}
.fig-label { font-size: var(--step-1); font-weight: var(--weight-medium); margin-top: var(--space-2); }
.fig-note { font-size: var(--step-0); max-width: 22em; }
/* 나머지 수치는 오른쪽 열에서 두 칸을 세로로 이어 쓴다 */
.fig-rest {
grid-column: 2;
grid-row: 1 / span 2;
margin: 0;
display: grid;
align-content: start;
}
.row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
column-gap: var(--space-4);
align-items: baseline;
padding-block: var(--space-3);
border-top: 1px solid var(--line);
}
.row:last-child { border-bottom: 1px solid var(--line); }
dt { font-weight: var(--weight-medium); }
dd { margin: 0; }
dd.nums { font-size: var(--step-1); color: var(--accent); justify-self: end; }
.row-note {
grid-column: 1 / -1;
font-size: var(--step-0);
margin-top: var(--space-1);
max-width: 32em;
}
.caveat { margin-top: var(--space-6); font-size: var(--step-0); max-width: 34em; }
@media (max-width: 860px) {
.figures { grid-template-columns: 1fr; }
.fig-rest { grid-column: 1; grid-row: auto; }
.fig-lead:nth-child(2) { margin-top: 0; }
}
</style>