지적 세 가지를 전부 확인했고 실제 결함도 하나 나왔다.
1. 재료 섹션 h2 가 아무것도 전달하지 않았다
'표면은 CSS 가 정하고, 공간은 GPU 가 맡는다'
금칙어도 없고 치환 테스트도 통과하는데 독자가 얻는 게 없다.
구현 이야기를 대구로 포장한 것이었다.
-> '텍스처 200KB 를 300바이트가 대신한다'
카피라이팅 방법론(so you can 테스트 / 6~12단어 / 검증 가능한 수치)을
찾아 적용했다. 우리 antipatterns 의 카피 절은 금지 목록뿐이라
이런 문장을 못 잡는다 — 작법 절을 새로 넣었다.
2. 필터 데모가 줄무늬였다
필터가 무엇을 하는지는 보이지만 왜 쓰는지는 안 보인다.
각 필터를 실제로 걸리는 자리로 옮겼다.
그레인 -> 매끈한 그라디언트 (없으면 밋밋하다는 대비)
굴절 -> 글자 (유리 뒤가 실제로 휘는 것)
듀오톤 -> 사진풍 면 (한 팔레트로 묶이는 것)
타일마다 '언제 쓰는가' 한 줄을 추가했다.
3. 그레인 필터가 아무 효과도 못 내고 있었다
feComposite in='SourceGraphic' in2='soft' operator='over' 는
원본을 노이즈 위에 올린다. 노이즈가 통째로 가려진다.
에러도 경고도 없이 조용히 죽는다.
feBlend overlay 로 바꾸고 baseFrequency 0.9 -> 0.62.
섹션 간 검은 띠도 고쳤다. 파이프라인 캔버스가 섹션 패딩(112px) 아래에서
시작해 히어로 배경이 끝난 자리와 사이에 200px 이 비었다.
스킬
antipatterns.md 카피 작법 — 금지 목록만으로는 못 잡는 실패
svg-filters.md 데모는 실무 소재에 · feComposite 입력 순서 함정
This commit is contained in:
parent
52fe0b5030
commit
61fb0cdcfa
6 changed files with 192 additions and 41 deletions
|
|
@ -60,7 +60,7 @@ export interface Content {
|
|||
eyebrow: string;
|
||||
h2: string;
|
||||
lead: string;
|
||||
tiles: { id: "grain" | "glass" | "duotone"; name: string; how: string; cost: string }[];
|
||||
tiles: { id: "grain" | "glass" | "duotone"; name: string; use: string; how: string; cost: string }[];
|
||||
note: string;
|
||||
};
|
||||
metrics: { eyebrow: string; h2: string; lead: string; items: Metric[]; caveat: string };
|
||||
|
|
@ -216,31 +216,34 @@ export const ko: Content = {
|
|||
},
|
||||
materials: {
|
||||
eyebrow: "재료",
|
||||
h2: "표면은 CSS 가 정하고, 공간은 GPU 가 맡는다",
|
||||
h2: "텍스처 200KB 를 300바이트가 대신한다",
|
||||
lead:
|
||||
"아래 배경은 지금 셰이더가 그리고 있고, 세 장의 타일은 같은 줄무늬 하나에 서로 다른 SVG 필터를 먹인 것이다. 스킬이 다루는 재료를 글로 설명하는 대신 화면에서 돌린다.",
|
||||
"필터는 장식이 아니라 재료다. 셋 다 실제로 쓰이는 자리에 걸어 뒀다 — 그레인은 매끈한 면에, 굴절은 유리 뒤 글자에, 듀오톤은 사진에. 배경은 지금 three.js 셰이더가 그리고 있다.",
|
||||
tiles: [
|
||||
{
|
||||
id: "grain",
|
||||
name: "종이",
|
||||
how: "feTurbulence 로 만든 프랙탈 노이즈를 알파로 눌러 표면에 얹는다.",
|
||||
cost: "비용 낮음 · 정지 상태",
|
||||
name: "그레인",
|
||||
use: "매끈한 그라디언트가 싸구려로 보일 때",
|
||||
how: "feTurbulence 가 만든 프랙탈 노이즈를 알파로 눌러 표면에 얹는다. 이미지 요청이 한 건도 늘지 않는다.",
|
||||
cost: "300바이트 · 정지 상태",
|
||||
},
|
||||
{
|
||||
id: "glass",
|
||||
name: "유리",
|
||||
how: "난류를 변위 지도로 써서 굴절시킨다. 필터 영역을 넓혀 가장자리 잘림을 막는다.",
|
||||
cost: "비용 중간 · 애니메이션 금지",
|
||||
name: "굴절",
|
||||
use: "유리 뒤의 글자가 실제로 휘어야 할 때",
|
||||
how: "난류를 변위 지도로 써서 픽셀을 민다. 필터 영역을 넓혀야 가장자리가 잘리지 않는다.",
|
||||
cost: "중간 · 애니메이션 금지",
|
||||
},
|
||||
{
|
||||
id: "duotone",
|
||||
name: "인쇄",
|
||||
how: "명도만 남긴 뒤 feComponentTransfer 로 두 색 사이에 다시 매핑한다.",
|
||||
cost: "비용 낮음 · 사진에도 쓴다",
|
||||
name: "듀오톤",
|
||||
use: "출처가 제각각인 사진을 한 팔레트로 묶을 때",
|
||||
how: "명도만 남긴 뒤 feComponentTransfer 로 두 색 사이에 다시 매핑한다. 원본을 보정하지 않는다.",
|
||||
cost: "낮음 · 사진에 쓴다",
|
||||
},
|
||||
],
|
||||
note:
|
||||
"위 세 장은 SVG 필터가 지금 먹고 있는 화면이고, 이 페이지의 배경 광은 three.js 셰이더가 그리고 있다. three 는 초기 번들에 없다. 첫 화면이 그려진 뒤에 받고, WebGL 이 없거나 모션을 줄이는 설정이면 아예 받지 않는다. 그때 보이는 그라디언트가 폴백이 아니라 그대로 결과물이다.",
|
||||
"이 셋은 SVG 필터가 지금 먹고 있는 화면이고, 그 뒤 배경은 three.js 셰이더가 그리고 있다. three 는 초기 번들에 없다 — 화면에 가까워질 때만 받고, WebGL 이 없거나 모션을 줄이는 설정이면 아예 받지 않는다. 그때 보이는 그라디언트는 폴백이 아니라 그대로 결과물이다."
|
||||
},
|
||||
metrics: {
|
||||
eyebrow: "이 페이지의 수치",
|
||||
|
|
@ -447,31 +450,34 @@ export const en: Content = {
|
|||
},
|
||||
materials: {
|
||||
eyebrow: "Materials",
|
||||
h2: "CSS decides the surface. The GPU handles the space.",
|
||||
h2: "300 bytes replaces a 200KB texture",
|
||||
lead:
|
||||
"The backdrop below is being drawn by a shader right now, and the three tiles are one striped fill wearing three different SVG filters. The skill's materials are running here rather than described.",
|
||||
"Filters are material, not decoration. Each one sits where it is actually used — grain on a flat gradient, refraction behind glass over type, duotone on a photograph. The backdrop is a three.js shader, running now.",
|
||||
tiles: [
|
||||
{
|
||||
id: "grain",
|
||||
name: "Paper",
|
||||
how: "Fractal noise from feTurbulence, pushed down through alpha and laid over the surface.",
|
||||
cost: "low cost · static",
|
||||
name: "Grain",
|
||||
use: "when a smooth gradient reads as cheap",
|
||||
how: "feTurbulence generates fractal noise, pushed down through alpha onto the surface. Not one extra image request.",
|
||||
cost: "300 bytes · static",
|
||||
},
|
||||
{
|
||||
id: "glass",
|
||||
name: "Glass",
|
||||
how: "Turbulence used as a displacement map. The filter region is widened so edges do not clip.",
|
||||
cost: "medium cost · never animate it",
|
||||
name: "Refraction",
|
||||
use: "when type behind glass has to actually bend",
|
||||
how: "Turbulence drives a displacement map that pushes pixels. Widen the filter region or the edges clip.",
|
||||
cost: "medium · never animate",
|
||||
},
|
||||
{
|
||||
id: "duotone",
|
||||
name: "Print",
|
||||
how: "Reduce to luminance, then remap between two colours with feComponentTransfer.",
|
||||
cost: "low cost · works on photography too",
|
||||
name: "Duotone",
|
||||
use: "when photos from different sources need one palette",
|
||||
how: "Keep luminance only, then remap between two colours with feComponentTransfer. The source file is untouched.",
|
||||
cost: "low · use it on photos",
|
||||
},
|
||||
],
|
||||
note:
|
||||
"The three tiles above are SVG filters running right now, and the light behind this page is a three.js shader. three is not in the initial bundle. It arrives after the first paint, and never at all without WebGL or with reduced motion requested. What you see then is not a fallback. It is the result.",
|
||||
"These three are SVG filters running on live pixels, and the field behind them is a three.js shader. three is not in the initial bundle — it arrives only as the section approaches, and never at all when WebGL is missing or motion is reduced. What you see then is not a fallback; it is the result."
|
||||
},
|
||||
metrics: {
|
||||
eyebrow: "This page, measured",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue