소개 페이지 5단계 감사 — 실렌더에서 결함 3건

정적 검사로는 안 잡히고 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건.
This commit is contained in:
Yun Chan 2026-08-20 11:09:53 +09:00
parent 6103700d38
commit 6e05e69ca4
7 changed files with 61 additions and 36 deletions

View file

@ -19,6 +19,7 @@ const { command, copyLabel, copiedLabel, note } = Astro.props;
<style>
.cmd {
display: flex;
min-width: 0;
align-items: stretch;
gap: 0;
border: 1px solid var(--line-strong);
@ -27,7 +28,7 @@ const { command, copyLabel, copiedLabel, note } = Astro.props;
max-width: 34em;
}
.cmd-text {
flex: 1 1 auto;
flex: 1 1 0;
min-width: 0;
padding: var(--space-3);
overflow-x: auto;
@ -38,6 +39,7 @@ const { command, copyLabel, copiedLabel, note } = Astro.props;
/* 독립 컨트롤이므로 44px 이상 */
min-width: 5.5em;
min-height: 44px;
white-space: nowrap;
padding-inline: var(--space-3);
border: 0;
border-left: 1px solid var(--line-strong);

View file

@ -128,25 +128,10 @@ const { c } = Astro.props;
margin-top: var(--space-5);
}
/* 진입 연출은 하나뿐이고, 없어도 전부 읽힌다.
scroll-driven animation 이라 scroll 리스너가 없다. */
@media (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: view()) {
.stop {
animation: stop-in linear both;
animation-timeline: view();
animation-range: entry 10% cover 22%;
}
@keyframes stop-in {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: none;
}
}
}
}
/* 스크롤 진입 페이드를 넣었다가 뺐다.
`animation-range: entry 10% cover 22%` 로는 페이지 하단 요소가 cover 22% 에
영영 도달하지 못해, 6개 정거장 중 뒤 3개가 opacity 0 으로 잠겼다.
이펙트를 끄면 보이지만 켜면 사라지는 것은 폴백이 있는 것과 다르다.
이 섹션은 페이지의 본체다. 콘텐츠를 걸고 하는 연출은 하지 않는다. */
</style>

View file

@ -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 {

View file

@ -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",

View file

@ -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 {

View file

@ -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);