소개 페이지 전면 재디자인 — 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:
parent
6e05e69ca4
commit
be343cdab5
22 changed files with 1695 additions and 439 deletions
158
apps/site/src/components/Materials.astro
Normal file
158
apps/site/src/components/Materials.astro
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
---
|
||||
import type { Content } from "../i18n/content";
|
||||
|
||||
interface Props { c: Content["materials"]; }
|
||||
const { c } = Astro.props;
|
||||
---
|
||||
|
||||
{/*
|
||||
레이아웃 패밀리: 레이어드 캔버스.
|
||||
배경은 three.js 셰이더 플레인, 전경은 SVG 필터를 실제로 먹인 타일 3장.
|
||||
둘 다 설명이 아니라 지금 이 화면에서 돌고 있는 것이다.
|
||||
*/}
|
||||
<section class="section materials" aria-labelledby="mat-h">
|
||||
<h2 id="mat-h" class="reveal">{c.h2}</h2>
|
||||
<p class="lead mat-lead reveal">{c.lead}</p>
|
||||
|
||||
<div class="full stage glass">
|
||||
<div class="stage-fallback" aria-hidden="true"></div>
|
||||
|
||||
<ul class="tiles">
|
||||
{
|
||||
c.tiles.map((t, i) => (
|
||||
<li class="tile reveal" style={`--reveal-delay:${i * 80}ms`}>
|
||||
<div class="shell">
|
||||
<div class="core">
|
||||
<div class={`swatch swatch-${t.id}`} aria-hidden="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="tile-name">{t.name}</h3>
|
||||
<p class="tile-how muted">{t.how}</p>
|
||||
<p class="tile-cost mono">{t.cost}</p>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p class="mat-note muted reveal">{c.note}</p>
|
||||
</section>
|
||||
|
||||
{/* 필터 정의. 화면에 그려지지 않고 참조만 된다 */}
|
||||
<svg class="visually-hidden" aria-hidden="true" focusable="false">
|
||||
<defs>
|
||||
<!-- 종이: 미세한 프랙탈 노이즈를 표면에 얹는다 -->
|
||||
<filter id="f-grain" x="0" y="0" width="100%" height="100%">
|
||||
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="3" result="n" />
|
||||
<feColorMatrix in="n" type="saturate" values="0" result="g" />
|
||||
<feComponentTransfer in="g" result="soft">
|
||||
<feFuncA type="linear" slope="0.42" />
|
||||
</feComponentTransfer>
|
||||
<feComposite in="SourceGraphic" in2="soft" operator="over" />
|
||||
</filter>
|
||||
|
||||
<!-- 유리: 난류로 만든 변위 지도를 굴절에 쓴다. 필터 영역을 넓혀 가장자리 잘림을 막는다 -->
|
||||
<filter id="f-glass" x="-12%" y="-12%" width="124%" height="124%"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feTurbulence type="fractalNoise" baseFrequency="0.012 0.02" numOctaves="2" seed="7" result="warp" />
|
||||
<feDisplacementMap in="SourceGraphic" in2="warp" scale="26"
|
||||
xChannelSelector="R" yChannelSelector="G" result="bent" />
|
||||
<feGaussianBlur in="bent" stdDeviation="0.4" />
|
||||
</filter>
|
||||
|
||||
<!-- 인쇄: 명도를 두 색 사이로 다시 매핑한다 -->
|
||||
<filter id="f-duotone" x="0" y="0" width="100%" height="100%"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix type="matrix" result="mono"
|
||||
values="0.33 0.33 0.33 0 0
|
||||
0.33 0.33 0.33 0 0
|
||||
0.33 0.33 0.33 0 0
|
||||
0 0 0 1 0" />
|
||||
<feComponentTransfer in="mono">
|
||||
<feFuncR type="table" tableValues="0.05 0.06 0.42" />
|
||||
<feFuncG type="table" tableValues="0.09 0.42 0.66" />
|
||||
<feFuncB type="table" tableValues="0.08 0.39 0.60" />
|
||||
</feComponentTransfer>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
.mat-lead { margin-block: var(--space-4) var(--space-6); }
|
||||
.mat-note { margin-top: var(--space-5); font-size: var(--step-0); }
|
||||
|
||||
.stage {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
max-width: 1180px;
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding: var(--space-6) var(--space-4);
|
||||
border-radius: var(--radius-shell);
|
||||
overflow: hidden;
|
||||
}
|
||||
.stage-fallback {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
.stage-fallback {
|
||||
background:
|
||||
radial-gradient(120% 90% at 12% 8%, color-mix(in oklab, var(--accent) 22%, transparent), transparent 60%),
|
||||
radial-gradient(90% 80% at 88% 92%, color-mix(in oklab, var(--accent) 14%, transparent), transparent 62%),
|
||||
var(--surface-raised);
|
||||
}
|
||||
|
||||
/* 3개 항목에 3개 셀. 빈 칸이 생기지 않고 균등 3열도 아니다 */
|
||||
.tiles {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
gap: var(--space-4);
|
||||
grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
|
||||
grid-template-areas:
|
||||
"big top"
|
||||
"big bottom";
|
||||
}
|
||||
.tile:nth-child(1) { grid-area: big; }
|
||||
.tile:nth-child(2) { grid-area: top; }
|
||||
.tile:nth-child(3) { grid-area: bottom; }
|
||||
/* 큰 셀은 더 넓은 화면을 갖는다 */
|
||||
.tile:nth-child(1) .swatch { aspect-ratio: 5 / 4; }
|
||||
.tile:nth-child(n + 2) .swatch { aspect-ratio: 16 / 7; }
|
||||
.tile { display: grid; gap: var(--space-2); align-content: start; }
|
||||
.tile .shell { box-shadow: var(--glass-hi), var(--shadow-2); }
|
||||
.tile .core { overflow: hidden; }
|
||||
|
||||
/* 필터를 먹일 대상. 같은 도형에 서로 다른 재질을 입힌다 */
|
||||
.swatch {
|
||||
aspect-ratio: 4 / 3;
|
||||
background:
|
||||
repeating-linear-gradient(
|
||||
58deg,
|
||||
var(--accent) 0 14px,
|
||||
color-mix(in oklab, var(--accent) 55%, var(--surface)) 14px 28px
|
||||
);
|
||||
}
|
||||
.swatch-grain { filter: url(#f-grain); }
|
||||
.swatch-glass { filter: url(#f-glass); }
|
||||
.swatch-duotone { filter: url(#f-duotone); }
|
||||
|
||||
/* 필터를 지원하지 않거나 저사양이면 줄무늬만 남는다. 그래도 성립한다 */
|
||||
@supports not (filter: url(#f-grain)) {
|
||||
.swatch-grain, .swatch-glass, .swatch-duotone { filter: none; }
|
||||
}
|
||||
|
||||
.tile-name { font-size: var(--step-1); margin-top: var(--space-1); }
|
||||
.tile-how { margin: 0; font-size: var(--step-0); }
|
||||
.tile-cost { margin: 0; color: var(--accent); }
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.tiles { grid-template-columns: 1fr; grid-template-areas: "big" "top" "bottom"; }
|
||||
.tile:nth-child(n + 2) .swatch { aspect-ratio: 5 / 4; }
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue