패럴랙스에 기준면을 준다 — 배경 격자와 광원 레이어
배경이 단색이면 패럴랙스는 성립하지 않는다. 앞의 것이 몇 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개 통과
This commit is contained in:
parent
9c9ab2de7e
commit
0e03bbafac
9 changed files with 158 additions and 19 deletions
|
|
@ -204,6 +204,36 @@ WebGL 게이트: `prefers-reduced-motion` 이면 **아예 시작하지 않는다
|
|||
|
||||
마지막 줄이 결정이다. 숫자가 스크롤에 흔들리면 그 값도 흔들려 보인다.
|
||||
|
||||
## 배경 레이어 — 패럴랙스의 기준면
|
||||
|
||||
**배경이 단색이면 패럴랙스는 성립하지 않는다.** 앞의 것이 몇 px 어긋나도 비교할 대상이 없다.
|
||||
히어로 말고는 기준면이 없어서 시차가 안 보였다.
|
||||
|
||||
배경 이미지를 사 오는 대신 **이 페이지가 이미 주장하는 것**을 그렸다.
|
||||
|
||||
| 레이어 | 무엇 | 어디 | 근거 |
|
||||
|---|---|---|---|
|
||||
| `.bg-grid` | 간격 눈금 격자. 셀이 `--grid-cell = --space-6` | 파이프라인 · 수치 | "모든 값이 토큰에서 나온다"가 배경에서도 성립해야 한다. 계기판의 눈금이기도 하다 |
|
||||
| `.bg-glow` | 광원 블롭 두 개. 섹션마다 `--glow-x` 로 자리를 옮긴다 | 재료 · 설치 | 히어로의 빛이 페이지를 관통해 마지막 CTA 에서 돌아온다 |
|
||||
|
||||
격자는 가장자리를 마스크로 지운다 — 화면 끝까지 가면 배경이 아니라 **표**가 된다.
|
||||
배경이 가장 적게 움직인다. 앞의 것보다 크게 움직이면 앞으로 튀어나와 보인다.
|
||||
|
||||
**실측 3층** (스크롤 1000 → 1600): 격자 **+14.9px** · 산출물 **-14px** · 단계명 **0**.
|
||||
한 층은 반드시 고정이다. 전부 움직이면 기준이 없어 그냥 흔들리는 페이지가 된다.
|
||||
|
||||
## 그리드의 `wide` 트랙을 좁은 화면에서 닫는다
|
||||
|
||||
`--wide-pad: clamp(0rem, -3.7rem + 15.2vw, 10rem)`
|
||||
|
||||
고정 `10rem` 으로 두는 동안 **모바일에서도 좌우 80px 씩 살아남아** 390px 화면에서
|
||||
본문이 `x=112` 부터 시작하고 폭이 278px 로 줄었다. 파이프라인 제목이 네 줄로 접혔다.
|
||||
닫고 나서 모든 텍스트가 `x=32` 에서 시작하고 제목이 두 줄이 됐다.
|
||||
|
||||
같은 이유로 파이프라인의 제목·리드·브리프를 스파인과 한 컨테이너에 넣었다.
|
||||
섹션 기본 그리드에 두면 제목 `x=371` · 리드 `x=435` · 스파인 `x=143` 으로 **정렬선이 셋**이 된다.
|
||||
지금은 전부 `x=183` 이다.
|
||||
|
||||
## 디자인 SSOT
|
||||
|
||||
토큰이 없어서 깨져 있던 세 축을 채웠다.
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ const { c, ui } = Astro.props;
|
|||
position: relative;
|
||||
isolation: isolate;
|
||||
overflow: clip;
|
||||
min-height: min(88dvh, 52rem);
|
||||
min-height: min(78dvh, 46rem);
|
||||
display: grid;
|
||||
align-content: center;
|
||||
padding-block: calc(var(--header-h) + var(--space-6)) var(--space-7);
|
||||
|
|
@ -72,8 +72,8 @@ const { c, ui } = Astro.props;
|
|||
/* 아래로 갈수록 지운다. black 은 색이 아니라 마스크 알파다.
|
||||
없으면 히어로 하단에서 배경이 가로선으로
|
||||
뚝 끊겨 다음 섹션과 두 장의 종이처럼 보인다. */
|
||||
-webkit-mask-image: linear-gradient(to bottom, black 0%, black 62%, transparent 96%);
|
||||
mask-image: linear-gradient(to bottom, black 0%, black 62%, transparent 96%);
|
||||
-webkit-mask-image: linear-gradient(to bottom, black 0%, black 82%, transparent 100%);
|
||||
mask-image: linear-gradient(to bottom, black 0%, black 82%, transparent 100%);
|
||||
}
|
||||
.hero-fallback,
|
||||
.hero-canvas,
|
||||
|
|
@ -143,7 +143,10 @@ const { c, ui } = Astro.props;
|
|||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.hero { min-height: min(94dvh, 46rem); }
|
||||
/* min-height 를 걸지 않는다. 콘텐츠가 390px 인데 736px 을 씌우면
|
||||
여백이 화면의 41% 를 먹고, 다음 섹션이 화면 밖으로 밀려나
|
||||
스크롤할 것이 있다는 단서까지 사라진다. */
|
||||
.hero { min-height: 0; }
|
||||
.hero-grid { grid-template-columns: 1fr; gap: var(--space-5); }
|
||||
.hero-title { max-width: 100%; }
|
||||
/* 세로 화면에서는 글자가 위쪽 절반을 다 쓴다. 가로 방향 스크림으로는
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const { c, ui } = Astro.props;
|
|||
---
|
||||
|
||||
|
||||
<section id="install" class="full install" aria-labelledby="install-h">
|
||||
<section id="install" class="full install bg-glow" style="--glow-x: 30%" aria-labelledby="install-h">
|
||||
<div class="install-grid">
|
||||
<div class="install-head">
|
||||
<h2 id="install-h">{c.h2}</h2>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const { c } = Astro.props;
|
|||
배경은 three.js 셰이더 플레인, 전경은 SVG 필터를 실제로 먹인 타일 3장.
|
||||
둘 다 설명이 아니라 지금 이 화면에서 돌고 있는 것이다.
|
||||
*/}
|
||||
<section class="section materials" aria-labelledby="mat-h">
|
||||
<section class="section materials bg-glow" style="--glow-x: 70%" aria-labelledby="mat-h">
|
||||
<h2 id="mat-h" class="reveal">{c.h2}</h2>
|
||||
<p class="lead mat-lead reveal">{c.lead}</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const rest = c.items.filter((m) => !m.feature);
|
|||
흔들리면 그 값도 흔들리는 것처럼 읽힌다. 계기판의 바늘은 떨지 않는다.
|
||||
움직임이 의미를 만드는 자리가 있으면, 움직이지 않는 것이 의미인 자리도 있다.
|
||||
*/}
|
||||
<section id="metrics" class="section metrics" aria-labelledby="metrics-h">
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -14,17 +14,20 @@ const { c } = Astro.props;
|
|||
③ 의미 — <ol role="list"> — 화면에 번호는 없지만 보조기술은 "6개 중 n번째"로 읽는다
|
||||
role="list" 는 list-style:none 일 때 Safari/VoiceOver 가 목록 의미를 버리는 것을 되돌린다
|
||||
*/}
|
||||
<section id="pipeline" class="section" aria-labelledby="pipeline-h">
|
||||
<p class="eyebrow reveal">{c.eyebrow}</p>
|
||||
<h2 id="pipeline-h" class="reveal">{c.h2}</h2>
|
||||
<p class="lead pipeline-lead reveal">{c.lead}</p>
|
||||
|
||||
<div class="brief reveal"><div class="brief-core">
|
||||
<span class="brief-label">{c.briefLabel}</span>
|
||||
<p class="brief-text">{c.brief}</p>
|
||||
</div></div>
|
||||
|
||||
<section id="pipeline" class="section bg-grid" aria-labelledby="pipeline-h">
|
||||
{/* 제목·리드·브리프·스파인을 한 컨테이너에 담는다.
|
||||
섹션 기본 그리드에 두면 제목은 main 칼럼의 음수 마진으로 x=371,
|
||||
리드는 x=435, 스파인은 x=143 에 서서 정렬선이 셋이 된다. */}
|
||||
<div class="full spine-wrap">
|
||||
<p class="eyebrow reveal">{c.eyebrow}</p>
|
||||
<h2 id="pipeline-h" class="reveal">{c.h2}</h2>
|
||||
<p class="lead pipeline-lead reveal">{c.lead}</p>
|
||||
|
||||
<div class="brief reveal"><div class="brief-core">
|
||||
<span class="brief-label">{c.briefLabel}</span>
|
||||
<p class="brief-text">{c.brief}</p>
|
||||
</div></div>
|
||||
|
||||
<ol role="list" class="spine">
|
||||
{
|
||||
c.stages.map((s) => (
|
||||
|
|
@ -43,9 +46,13 @@ const { c } = Astro.props;
|
|||
</section>
|
||||
|
||||
<style>
|
||||
/* 컨테이너는 넓어도 읽는 것은 measure 안에 둔다 */
|
||||
.pipeline-lead {
|
||||
margin-bottom: var(--space-6);
|
||||
max-width: var(--measure);
|
||||
}
|
||||
.brief { max-width: var(--measure); }
|
||||
#pipeline-h { max-width: 24ch; }
|
||||
|
||||
/* 스파인은 페이지의 본체다. main 칼럼(561px)에 가두면
|
||||
페이지에서 가장 긴 섹션이 가장 좁아진다.
|
||||
|
|
|
|||
|
|
@ -139,9 +139,9 @@ section[id] { scroll-margin-top: calc(var(--header-h) + var(--space-4)); }
|
|||
display: grid;
|
||||
grid-template-columns:
|
||||
[full-start] minmax(var(--gutter), 1fr)
|
||||
[wide-start] minmax(0, var(--space-8))
|
||||
[wide-start] minmax(0, var(--wide-pad))
|
||||
[main-start] minmax(0, var(--measure)) [main-end]
|
||||
minmax(0, var(--space-8)) [wide-end]
|
||||
minmax(0, var(--wide-pad)) [wide-end]
|
||||
minmax(var(--gutter), 1fr) [full-end];
|
||||
}
|
||||
.page > * { grid-column: main; }
|
||||
|
|
@ -415,6 +415,61 @@ a:hover { color: var(--accent); }
|
|||
}
|
||||
}
|
||||
|
||||
/* ---- 배경 레이어 --------------------------------------------
|
||||
패럴랙스는 깊이 차가 보여야 성립한다. 배경이 단색이면 앞의 것이
|
||||
몇 px 어긋나 봐야 아무도 못 느낀다 — 기준면이 없기 때문이다.
|
||||
|
||||
그래서 배경을 깐다. 다만 사 온 이미지가 아니라 이 페이지가 주장하는 것,
|
||||
곧 간격 눈금 자체를 격자로 그린다. --grid-cell 은 --space-5 다. */
|
||||
.bg-grid,
|
||||
.bg-glow {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
}
|
||||
.bg-grid::before,
|
||||
.bg-glow::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -12% 0;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.bg-grid::before {
|
||||
background-image:
|
||||
repeating-linear-gradient(to right,
|
||||
var(--line-strong) 0 1px, transparent 1px var(--grid-cell)),
|
||||
repeating-linear-gradient(to bottom,
|
||||
var(--line-strong) 0 1px, transparent 1px var(--grid-cell));
|
||||
/* 가장자리에서 지운다. 화면 끝까지 격자가 가면 표가 되어버린다 */
|
||||
-webkit-mask-image: radial-gradient(135% 95% at 50% 45%, black 22%, transparent 92%);
|
||||
mask-image: radial-gradient(135% 95% at 50% 45%, black 22%, transparent 92%);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 히어로의 두 광원이 페이지를 관통한다. 섹션마다 자리가 다르다 */
|
||||
.bg-glow::before {
|
||||
background:
|
||||
radial-gradient(42% 46% at var(--glow-x, 78%) var(--glow-y, 32%),
|
||||
color-mix(in oklab, var(--accent) 15%, transparent), transparent 70%),
|
||||
radial-gradient(38% 42% at calc(100% - var(--glow-x, 78%)) 78%,
|
||||
color-mix(in oklab, var(--glow-2) 13%, transparent), transparent 68%);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
@supports (animation-timeline: view()) {
|
||||
/* 배경이 가장 느리다. 콘텐츠보다 적게 움직여야 뒤에 있는 것으로 읽힌다 */
|
||||
.bg-grid::before { --par-from: -34px; --par-to: 34px; }
|
||||
.bg-glow::before { --par-from: -58px; --par-to: 58px; }
|
||||
.bg-grid::before,
|
||||
.bg-glow::before {
|
||||
animation: par linear both;
|
||||
animation-timeline: view();
|
||||
animation-range: cover 0% cover 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 유틸 -------------------------------------------------- */
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -128,6 +128,18 @@
|
|||
모바일 전 구간 32px 을 깔고 넓은 화면에서만 40px 까지 연다. */
|
||||
--gutter: clamp(2rem, 1.4rem + 1.8vw, 2.5rem);
|
||||
|
||||
/* 배경 격자의 한 칸. 간격 스케일에서 가져온다 —
|
||||
장식용 격자가 아니라 이 페이지가 실제로 쓰는 눈금이어야
|
||||
"모든 값이 토큰에서 나온다"는 주장이 배경에서도 성립한다. */
|
||||
--grid-cell: var(--space-6);
|
||||
|
||||
/* main 칼럼 바깥의 여유 폭(wide 트랙). 넓은 화면에서 제목과 넓은 블록이
|
||||
본문보다 밖으로 나갈 자리를 만든다.
|
||||
이 값을 고정으로 두면 모바일에서도 좌우 80px 씩 살아남아, 390px 화면에서
|
||||
본문이 x=112 부터 시작하고 폭이 278px 로 줄어든다 — 제목이 네 줄로 접힌다.
|
||||
좁은 화면에서는 0 으로 닫는다. */
|
||||
--wide-pad: clamp(0rem, -3.7rem + 15.2vw, 10rem);
|
||||
|
||||
/* 헤더가 차지하는 높이. 떠 있는 헤더라 문서 흐름에서 빠져 있고,
|
||||
그래서 히어로 상단 여백과 앵커 목적지가 이 값을 알아야 한다.
|
||||
간격 토큰을 clamp 로 바꾸는 순간 이 관계가 끊겨 h1 이 헤더 뒤로
|
||||
|
|
|
|||
|
|
@ -448,6 +448,38 @@ JS 쪽 판정도 한 곳에 모은다. 설정이 도중에 바뀌면 반영한
|
|||
안 움직이는 섹션이 하나도 없으면 움직임에 의미가 없다는 뜻이고,
|
||||
`design.md` 에 "여기는 의도적으로 정지"라고 적어두지 않으면 다음 사람이 "빠뜨린 것"으로 보고 채운다.
|
||||
|
||||
### 패럴랙스에는 기준면이 필요하다
|
||||
|
||||
**배경이 단색이면 패럴랙스는 성립하지 않는다.** 앞의 것이 몇 px 어긋나 봐야
|
||||
비교할 대상이 없어서 아무도 못 느낀다. 시차는 두 층 사이에서만 보인다.
|
||||
|
||||
그렇다고 배경 이미지를 사 오지 마라. **그 페이지가 이미 주장하는 것을 배경으로 그려라.**
|
||||
|
||||
| 페이지가 주장하는 것 | 배경으로 그릴 것 |
|
||||
|---|---|
|
||||
| 값이 토큰 체계에서 나온다 | **간격 눈금 격자**. 셀 크기를 실제 space 토큰으로 |
|
||||
| 재질·표면이 주제다 | 광원 블롭. 섹션마다 자리를 옮긴다 |
|
||||
| 데이터·계측이 주제다 | 눈금, 축, 등고선 |
|
||||
| 문서·아카이브다 | 괘선, 여백선 |
|
||||
|
||||
격자를 그릴 때 두 가지를 지켜라.
|
||||
|
||||
1. **가장자리를 마스크로 지운다.** 화면 끝까지 격자가 가면 배경이 아니라 **표**가 된다
|
||||
2. **배경이 가장 적게 움직인다.** 앞의 것보다 크게 움직이면 앞으로 튀어나와 보인다
|
||||
|
||||
```css
|
||||
.bg-grid::before {
|
||||
content: ""; position: absolute; inset: -12% 0; z-index: -1;
|
||||
background-image:
|
||||
repeating-linear-gradient(to right, var(--line-strong) 0 1px, transparent 1px var(--grid-cell)),
|
||||
repeating-linear-gradient(to bottom, var(--line-strong) 0 1px, transparent 1px var(--grid-cell));
|
||||
mask-image: radial-gradient(135% 95% at 50% 45%, black 22%, transparent 92%);
|
||||
}
|
||||
```
|
||||
|
||||
세 층이 되면 시차가 확실해진다 — 실측: 배경 격자 **+14.9px**, 산출물 **-14px**, 단계명 **0**.
|
||||
**한 층은 반드시 고정이어야 한다.** 전부 움직이면 기준이 없어 그냥 흔들리는 페이지다.
|
||||
|
||||
### 구현은 scroll-driven animation 으로
|
||||
|
||||
```css
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue