소개 페이지 전면 재디자인 — Ethereal Glass

design-taste-frontend, high-end-visual-design, redesign-existing-projects 를
로드해 리디자인 모드로 진행했다. 이전 페이지는 디자인 스킬을 파는 자리가
문서처럼 생겼다는 지적을 받았다.

Design Read
- redesign-overhaul / 개발자 대상 / 정밀기기 언어
- DESIGN_VARIANCE 8 · MOTION_INTENSITY 6 · VISUAL_DENSITY 3

방향
- 다크 잉크 바탕에 three.js 셰이더 광원. 유리 패널이 그 위에 뜬다
- 리퀴드 글래스는 이 브리프에서 장식이 아니라 제품 시연이다.
  SVG 필터와 three.js 의 결합 그 자체이기 때문이다
- 밝은 밴드는 조판 섹션 하나뿐. 한글이 종이 위에 있어야 하는 자리다

제품 증명
- 히어로 배경에 셰이더(초기 번들 밖, 첫 페인트 후 지연 로드)
- 재료 섹션에 SVG 필터 3종을 실제로 먹인 타일: 그레인, 굴절, 듀오톤
- 도그푸딩 산출물 2개를 실제 스크린샷으로. 가짜 목업이 아니다

감사에서 걸려 고친 것
- backdrop-filter 에 SVG 를 얹으면 Chrome 이 선언 전체를 버린다.
  CSS.supports 는 참을 주면서 computed 는 none 이 된다
- main 에서 그리드 클래스가 빠져 subgrid 가 붕괴, 가로 444px 오버플로
- 진입 연출이 두 번 콘텐츠를 삼켰다. opacity 를 버리고 transform 만 남겼다
- eyebrow 4개, 제목 em-dash 2건

수치는 불리해진 것도 그대로 싣는다
- 반투명 패널 1개 -> 29개. 유리가 주제가 됐으므로
- three.js 185KB(gzip). 데모라서 JS 예산을 올렸고 그 사실을 페이지에 적었다
This commit is contained in:
Yun Chan 2026-08-20 11:45:38 +09:00
parent 6e05e69ca4
commit be343cdab5
22 changed files with 1695 additions and 439 deletions

View file

@ -1,40 +1,79 @@
---
import type { Content } from "../i18n/content";
interface Props {
c: Content["metrics"];
}
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);
---
<section class="section" aria-labelledby="metrics-h">
<p class="eyebrow">{c.eyebrow}</p>
<h2 id="metrics-h">{c.h2}</h2>
<p class="lead metrics-lead">{c.lead}</p>
{/* 레이아웃 패밀리: 벤토. 항목 수만큼 셀이 있고 빈 칸이 없다 */}
<section class="section metrics" aria-labelledby="metrics-h">
<p class="eyebrow reveal">{c.eyebrow}</p>
<h2 id="metrics-h" class="reveal">{c.h2}</h2>
<p class="lead metrics-lead reveal">{c.lead}</p>
<div class="table-scroll">
<table>
<tbody>
{
c.items.map((m) => (
<tr>
<th scope="row">{m.label}</th>
<td class="metric-value mono">{m.value}</td>
<td class="metric-note muted">{m.note}</td>
</tr>
))
}
</tbody>
</table>
<div class="full bento">
{
feature.map((m, i) => (
<div class="cell cell-lg glass glass-2 reveal" style={`--reveal-delay:${i * 70}ms`}>
<span class="cell-value nums">{m.value}</span>
<span class="cell-label">{m.label}</span>
<span class="cell-note muted">{m.note}</span>
</div>
))
}
{
rest.map((m, i) => (
<div class="cell glass reveal" style={`--reveal-delay:${(i + 2) * 70}ms`}>
<span class="cell-value nums">{m.value}</span>
<span class="cell-label">{m.label}</span>
<span class="cell-note muted">{m.note}</span>
</div>
))
}
</div>
<p class="caveat muted">{c.caveat}</p>
<p class="caveat muted reveal">{c.caveat}</p>
</section>
<style>
.metrics-lead { margin-bottom: var(--space-5); }
.metric-value { white-space: nowrap; }
.metric-note { font-size: var(--step-0); }
.caveat { margin-top: var(--space-4); font-size: var(--step-0); }
th[scope="row"] { font-weight: 400; color: var(--ink); letter-spacing: 0; }
.metrics-lead { margin-block: var(--space-4) var(--space-6); }
.bento {
display: grid;
gap: var(--space-3);
grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
max-width: 1180px;
margin-inline: auto;
padding-inline: var(--space-4);
}
.cell {
display: grid;
align-content: start;
gap: var(--space-1);
padding: var(--space-4);
}
/* 두 항목만 크게. 나머지는 조용히 받친다 */
.cell-lg {
grid-column: span 2;
box-shadow: var(--glass-hi), var(--shadow-3);
}
.cell-lg .cell-value { color: var(--accent); }
.cell-value {
font-size: var(--step-3);
font-weight: 600;
line-height: 1.1;
letter-spacing: var(--tracking-display);
}
.cell-lg .cell-value { font-size: var(--step-4); }
.cell-label { font-weight: 500; }
.cell-note { font-size: var(--step-0); }
.caveat { margin-top: var(--space-5); font-size: var(--step-0); }
@media (max-width: 700px) {
.cell-lg { grid-column: span 1; }
}
</style>