From 6e05e69ca4fabd724f585f9cebd065a5115155f5 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Thu, 20 Aug 2026 11:09:53 +0900 Subject: [PATCH] =?UTF-8?q?=EC=86=8C=EA=B0=9C=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=205=EB=8B=A8=EA=B3=84=20=EA=B0=90=EC=82=AC=20?= =?UTF-8?q?=E2=80=94=20=EC=8B=A4=EB=A0=8C=EB=8D=94=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EA=B2=B0=ED=95=A8=203=EA=B1=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 정적 검사로는 안 잡히고 320px 실렌더에서만 나온 것들이다. 발견 내용은 페이지의 프리플라이트 로그 섹션에 그대로 실었다. 1. 320px 가로 오버플로 13px 표를 overflow 컨테이너로 감쌌는데도 넘쳤다. grid item 의 기본 min-width 가 auto 라 콘텐츠가 부모를 밀어낸다. min-width:0 이 있어야 overflow-x 가 실제로 작동한다. 2. 다크 밴드 버튼 대비 2.88:1 (기준 4.5:1) 밴드가 --accent 만 반전시키고 --accent-ink 는 흰색으로 뒀다. --accent-ink-inverse 토큰을 추가해 함께 반전시킨다. 6.21:1 로 회복. 3. 파이프라인 6단계 중 뒤 3개가 영영 opacity 0 ← 가장 심각 animation-range 의 cover 22% 에 페이지 하단 요소는 도달하지 못한다. 스크롤을 끝까지 내려도 이 페이지의 본체가 절반 사라진 상태였다. 이펙트를 끄면 보이지만 켜면 사라지는 것은 폴백이 있는 것과 다르다. 스크롤 진입 연출을 제거했다. 남은 모션은 호버·포커스뿐이다. 하드 게이트 12/12 전부 통과 (8개 정적 + 4개 실렌더). 실렌더 대비 최저 6.21:1, AA 미달 0건. --- apps/site/design.md | 16 ++++++----- apps/site/src/components/CopyCommand.astro | 4 ++- apps/site/src/components/Pipeline.astro | 27 ++++-------------- apps/site/src/components/Typography.astro | 2 +- apps/site/src/i18n/content.ts | 32 +++++++++++++++++++--- apps/site/src/styles/base.css | 13 +++++++-- apps/site/src/styles/tokens.css | 3 ++ 7 files changed, 61 insertions(+), 36 deletions(-) diff --git a/apps/site/design.md b/apps/site/design.md index 105f646..2977cae 100644 --- a/apps/site/design.md +++ b/apps/site/design.md @@ -72,18 +72,21 @@ | 이펙트 | 구현 | 폴백 | |---|---|---| -| 스파인 진입 페이드 + 8px 상승 | `animation-timeline: view()`, JS 0 | 미지원 브라우저에서는 아무 일도 안 일어난다. 콘텐츠는 처음부터 visible | | 복사 버튼 | 인라인 314B | 클립보드 권한 실패 시 텍스트를 선택 상태로 만들어 사용자가 직접 복사 | -`prefers-reduced-motion: no-preference` 안에 `@supports (animation-timeline: view())` 로 이중 게이팅. +**스파인 진입 페이드는 넣었다가 뺐다.** `animation-timeline: view()` 에 `animation-range: entry 10% cover 22%` 를 걸었는데, **페이지 하단 요소는 `cover 22%` 에 영영 도달하지 못한다.** 스크롤을 끝까지 내려도 6개 정거장 중 뒤 3개가 opacity 0 으로 남았다 — 이 페이지의 본체가 절반 사라진 것이다. +정적 검사로는 안 잡혔다. 320px 실렌더에서 스크롤을 끝까지 돌려보고야 나왔다. +**이펙트를 끄면 보이지만 켜면 사라지는 것은 폴백이 있는 것과 다르다.** 이 페이지에 남은 모션은 호버·포커스 상태 변화뿐이다. ## 5단계 감사 결과 -- 하드 게이트: **정적 검사 가능한 8개 전부 통과.** 나머지 4개(가로 스크롤·버튼 줄바꿈·실렌더 대비·테마 반전)는 브라우저 측정 대기 +- 하드 게이트: **12개 전부 통과.** 8개는 정적 검사, 4개(가로 스크롤·버튼 줄바꿈·실렌더 대비·테마 반전)는 **320px 실렌더**로 측정 - 슬롭 지문 grep 9개 패턴: **0건** - 카운트 규칙: 섹션 7 / eyebrow 3 (상한 3) / 마퀴 0 / h1 1 — 통과 +- 실렌더 대비: 본문·링크·버튼 전부 AA 이상. 최저 6.21:1 (다크 밴드 버튼) -감사 중 걸려서 고친 것 4건은 페이지의 "프리플라이트 로그" 섹션에 그대로 실려 있다. +감사 중 걸려서 고친 것 **7건**은 페이지의 "프리플라이트 로그" 섹션에 그대로 실려 있다. +**뒤 3건은 정적 검사로는 안 잡히고 실렌더에서만 나왔다** — 이 사실 자체가 `preflight.md` 의 "프레임워크 프로젝트는 프로덕션 빌드 결과에 돌린다" 규칙을 뒷받침한다. ## 의도적으로 하지 않은 것과 그 이유 @@ -100,9 +103,8 @@ ## 다음 실행이 해야 할 일 1. **배포 후 Lighthouse 로 첫 인터랙션·LCP·CLS 를 재고, "이 페이지의 수치" 섹션의 `—` 를 채운다.** 채우기 전까지 그 항목은 비워둔다 -2. 브라우저로 나머지 하드 게이트 4개를 검사하고 `8/12` 를 갱신한다 -3. 폰트를 CDN 의존에서 자체 호스팅으로 옮길지 판단한다 (현재 jsDelivr · Fontshare · Google Fonts 3곳) -4. OG 이미지가 없다. 공유될 자리라면 만든다 +2. 폰트를 CDN 의존에서 자체 호스팅으로 옮길지 판단한다 (현재 jsDelivr · Fontshare · Google Fonts 3곳) +3. OG 이미지가 없다. 공유될 자리라면 만든다 --- diff --git a/apps/site/src/components/CopyCommand.astro b/apps/site/src/components/CopyCommand.astro index 1d9a149..7933942 100644 --- a/apps/site/src/components/CopyCommand.astro +++ b/apps/site/src/components/CopyCommand.astro @@ -19,6 +19,7 @@ const { command, copyLabel, copiedLabel, note } = Astro.props; diff --git a/apps/site/src/components/Typography.astro b/apps/site/src/components/Typography.astro index 11249d8..a00366b 100644 --- a/apps/site/src/components/Typography.astro +++ b/apps/site/src/components/Typography.astro @@ -37,7 +37,7 @@ const { c } = Astro.props; .demo { display: grid; gap: var(--space-4); - grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr)); margin-bottom: var(--space-5); } .demo-col { diff --git a/apps/site/src/i18n/content.ts b/apps/site/src/i18n/content.ts index e12db85..c677af0 100644 --- a/apps/site/src/i18n/content.ts +++ b/apps/site/src/i18n/content.ts @@ -174,7 +174,7 @@ export const ko: Content = { { label: "강조색", value: "1개", note: "딥 틸. 라이트·다크는 같은 색의 대응값이다" }, { label: "히어로까지 JS", value: "314B", note: "gzip. 예산 50KB의 0.6%. 복사 버튼 하나뿐이다" }, { label: "슬롭 지문 grep", value: "0건", note: "9개 패턴 검사" }, - { label: "하드 게이트", value: "8/12", note: "정적으로 검사 가능한 8개. 나머지 4개는 브라우저 측정 대기" }, + { label: "하드 게이트", value: "12/12", note: "12개 전부 검사. 4개는 320px 실렌더로 측정했다" }, { label: "첫 인터랙션", value: "—", note: "미측정. 프로덕션 배포 후 Lighthouse 로 잰다" }, ], caveat: "값은 프로덕션 빌드에서 측정한다. 아직 측정하지 않은 항목은 비워둔다 — 지어낸 숫자보다 구멍이 정직하다.", @@ -200,8 +200,20 @@ export const ko: Content = { caught: "본문 컬럼이 960px — 우리가 정한 한 줄 길이(561px)를 넘는다", fixed: "그리드의 본문 폭을 --measure 로 바꿨다. 스킬 문서의 예시 코드도 같이 고쳤다", }, + { + caught: "320px 에서 가로로 13px 넘쳤다 — 표를 overflow 컨테이너로 감쌌는데도", + fixed: "grid item 의 기본 min-width 는 auto 라 콘텐츠가 부모를 밀어낸다. min-width:0 을 걸어야 overflow-x 가 실제로 작동한다", + }, + { + caught: "다크 밴드 안 버튼의 대비가 2.88:1 — 기준은 4.5:1", + fixed: "밴드가 강조색만 반전시키고 그 위 글자색은 흰색 그대로였다. 반전 대응 토큰을 하나 더 두고 함께 바꿨다", + }, + { + caught: "스크롤을 끝까지 내려도 파이프라인 6단계 중 뒤 3개가 opacity 0 으로 남았다 — 이 페이지의 본체가 절반 사라졌다", + fixed: "animation-range 의 cover 22% 에 페이지 하단 요소는 영영 도달하지 못한다. 이펙트를 끄면 보이지만 켜면 사라지는 것은 폴백이 있는 것과 다르다. 연출을 뺐다", + }, ], - note: "네 건 모두 만들면서 걸린 것이고, 걸린 뒤에 고쳤다. 이 목록이 짧아 보인다면 그건 이 페이지가 작기 때문이다.", + note: "일곱 건 모두 만들면서 걸린 것이고, 걸린 뒤에 고쳤다. 마지막 셋은 정적 검사로는 안 잡히고 320px 실렌더에서만 나왔다.", }, typography: { eyebrow: "한글 조판", @@ -300,7 +312,7 @@ export const en: Content = { { label: "accent colours", value: "1", note: "deep teal. Light and dark are the same colour" }, { label: "JS to first paint", value: "314B", note: "gzip. 0.6% of the 50KB budget. One copy button" }, { label: "slop fingerprints", value: "0", note: "9 patterns checked" }, - { label: "hard gates", value: "8/12", note: "the 8 that are statically checkable. 4 await browser measurement" }, + { label: "hard gates", value: "12/12", note: "all twelve checked. Four were measured on a real 320px render" }, { label: "time to interactive", value: "—", note: "not measured yet. Lighthouse, after deploy" }, ], caveat: "Measured on the production build. Unmeasured entries stay empty — a hole is more honest than an invented number.", @@ -326,8 +338,20 @@ export const en: Content = { caught: "The text column was 960px — wider than the measure we had just set (561px)", fixed: "Bound the grid's main column to --measure. The skill's own example code was wrong too, so that got fixed as well", }, + { + caught: "13px of horizontal overflow at 320px — even with the table inside an overflow container", + fixed: "A grid item's default min-width is auto, so content pushes the parent wider. overflow-x only works once min-width:0 is set", + }, + { + caught: "The button inside the dark band sat at 2.88:1 — the threshold is 4.5:1", + fixed: "The band inverted the accent colour but left the text on it white. Added an inverted counterpart token and switched both together", + }, + { + caught: "Scrolling to the very bottom still left the last 3 of 6 pipeline stages at opacity 0 — half of this page's core section was gone", + fixed: "Elements near the page bottom never reach cover 22% in animation-range. Visible with effects off but missing with them on is not the same as having a fallback. The effect was removed", + }, ], - note: "All four were caught while building this page, and fixed after being caught. If the list looks short, that is because the page is small.", + note: "All seven were caught while building this page, and fixed after being caught. The last three only surfaced on a real 320px render — static checks missed them.", }, typography: { eyebrow: "Your language is probably not Latin", diff --git a/apps/site/src/styles/base.css b/apps/site/src/styles/base.css index 595337d..625eccc 100644 --- a/apps/site/src/styles/base.css +++ b/apps/site/src/styles/base.css @@ -140,8 +140,17 @@ th { font-size: var(--step-0); letter-spacing: var(--tracking-label); } -/* 좁은 화면에서 표가 페이지를 밀지 않게 — 하드 게이트 #5 */ -.table-scroll { overflow-x: auto; } +/* 좁은 화면에서 표가 페이지를 밀지 않게 — 하드 게이트 #5. + min-width:0 이 없으면 grid item 의 기본 min-width:auto 때문에 + overflow-x:auto 가 걸려 있어도 페이지 자체가 넓어진다. */ +.table-scroll { + overflow-x: auto; + min-width: 0; +} + +/* 같은 이유로 그리드·플렉스 자식은 전부 줄어들 수 있어야 한다 */ +.page > *, +.section > * { min-width: 0; } /* ---- 유틸 -------------------------------------------------- */ .visually-hidden { diff --git a/apps/site/src/styles/tokens.css b/apps/site/src/styles/tokens.css index e52a94d..9b5e8be 100644 --- a/apps/site/src/styles/tokens.css +++ b/apps/site/src/styles/tokens.css @@ -22,6 +22,7 @@ --accent: #0E6B63; /* 딥 틸. 대비 6.04:1 */ --accent-ink: #FFFFFF; /* 강조 위 글자. 6.36:1 */ --accent-inverse: #3FA89D; /* 밴드 위 강조. 6.30:1 */ + --accent-ink-inverse: #0B1A18; /* 밴드 위 강조 위의 글자. 6.43:1 */ /* ---- 형태: radius 2종 (게이트 #3) ------------------------ */ --radius-sm: 3px; /* 버튼·입력·배지 */ @@ -65,6 +66,7 @@ --accent: #3FA89D; /* 6.30:1 */ --accent-ink: #0B1A18; /* 6.21:1 */ --accent-inverse: #0E6B63; + --accent-ink-inverse: #FFFFFF; } } @@ -119,6 +121,7 @@ --ink-muted: var(--ink-muted-inverse); --line: var(--line-inverse); --accent: var(--accent-inverse); + --accent-ink: var(--accent-ink-inverse); background: var(--surface); color: var(--ink); padding-block: var(--space-8);