feat(skill): 왼쪽 인셋 리듬·픽셀 판정을 스킬에 내재화
이번 라운드(스크롤 잔류·빈약한 헤더·인셋 불일치 — 사용자 보고 3건)의 교훈을 스킬로 승격: - design-gate leftInset 검사 내장(텍스트 노드 Range 기준 ≥12px, 모바일 390px 전 뷰) — 오버플로 검사는 오른쪽만 보는 계측 공백을 메움. 도그푸드: 양앱 통과(가온 16·두레 24 각자 일관 축) - 브랜드 검사 오탐 수정(동일 셀렉터 다중 요소 시 보이는 것 기준 — 도그푸드가 잡음) - preflight §0-E 왼쪽 인셋 리듬, 하니스 규칙 15~17(기준 갱신 의도 리뷰·스크린샷 픽셀 판정·RED 재현), 사고표 4행 - SKILL.md 규칙 6 보강: 계측의 공백 — 보고가 오면 픽셀 판정으로 수치화하고 게이트에 추가
This commit is contained in:
parent
527b9c2379
commit
46b47884c9
7 changed files with 74 additions and 6 deletions
|
|
@ -31,8 +31,8 @@ const DEFAULT_CFG = {
|
|||
],
|
||||
widths: [320, 375, 390, 768, 1024, 1440],
|
||||
fontScales: [1, 1.3],
|
||||
thresholds: { contrastNormal: 4.5, contrastLarge: 3, visualDiffPct: 0.1, stackLines: 3, radiusLiterals: 3, tapTargetMin: 44, inlineTargetMin: 24 },
|
||||
checks: { meta: true, contrast: true, stack: true, wrap: true, rhythm: true, scaleMatrix: true, tapTargets: true, static: false, visual: false, webkit: false, deadCss: true, cssHygiene: true },
|
||||
thresholds: { contrastNormal: 4.5, contrastLarge: 3, visualDiffPct: 0.1, stackLines: 3, radiusLiterals: 3, tapTargetMin: 44, inlineTargetMin: 24, leftInsetMin: 12 },
|
||||
checks: { meta: true, contrast: true, stack: true, wrap: true, rhythm: true, scaleMatrix: true, tapTargets: true, leftInset: true, static: false, visual: false, webkit: false, deadCss: true, cssHygiene: true },
|
||||
deadCssIgnore: ["is-"], // 동적으로 조립되는 클래스 접두사(is-${type} 등). 오탐 방지 — 프로젝트마다 추가한다
|
||||
chromePath: null,
|
||||
};
|
||||
|
|
@ -382,7 +382,12 @@ for (const p of CFG.pages) {
|
|||
await page.goto(href, { waitUntil: "networkidle0" });
|
||||
await page.evaluate(UTILS);
|
||||
if (p.brand) {
|
||||
const bl = await page.evaluate(`__lines(${JSON.stringify(p.brand)})`);
|
||||
// 동일 셀렉터가 여러 개(사이드바+탑바 등)면 보이는 것을 잰다 — display:none 의 rect 는 0
|
||||
const bl = await page.evaluate((sel) => {
|
||||
const els = [...document.querySelectorAll(sel)];
|
||||
const el = els.find((e) => e.offsetParent !== null || getComputedStyle(e).position === "fixed") || els[0];
|
||||
return window.__lines(el);
|
||||
}, p.brand);
|
||||
bl === 1 ? pass(`${p.name} 브랜드 1줄`) : fail(`${p.name} 브랜드 ${bl}줄(수축 의심)`);
|
||||
}
|
||||
}
|
||||
|
|
@ -415,6 +420,40 @@ for (const p of CFG.pages) {
|
|||
bad.length ? fail(`${p.name}/${v} 터치타깃`, bad.join(" · ")) : pass(`${p.name}/${v} 터치타깃`, `컨트롤 ≥${CFG.thresholds.tapTargetMin}px·인라인 ≥${CFG.thresholds.inlineTargetMin}px`);
|
||||
}
|
||||
} else skip("터치타깃", "checks.tapTargets=false");
|
||||
|
||||
// ── 왼쪽 인셋(모바일) — 오버플로 검사는 오른쪽만 본다.
|
||||
// 실측 사고: 제목 16px vs 앱바·카드 24px 혼재가 모든 게이트를 통과했고
|
||||
// 사용자에게 '가장자리에 붙어 깨졌다'고 보고됐다. 텍스트 노드 Range 로 글자 시작점을 잰다
|
||||
// (전폭 컨테이너의 보더박스가 아니라. display:none 의 rect=0 은 보임 검사로 제외). ──
|
||||
if (CFG.checks.leftInset) {
|
||||
await page.setViewport({ width: 390, height: 844, isMobile: true, hasTouch: true });
|
||||
await page.goto(href, { waitUntil: "networkidle0" });
|
||||
for (const v of p.views || ["single"]) {
|
||||
await goView(v);
|
||||
const inset = await page.evaluate(() => {
|
||||
let min = null;
|
||||
const walk = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
||||
let n;
|
||||
while ((n = walk.nextNode())) {
|
||||
if (!n.textContent.trim() || !n.parentElement) continue;
|
||||
const cs = getComputedStyle(n.parentElement);
|
||||
if (cs.display === "none" || cs.visibility === "hidden" || cs.position === "fixed") continue;
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(n);
|
||||
const r = range.getBoundingClientRect();
|
||||
if (r.width < 2 || r.height < 4 || r.left < 0) continue;
|
||||
const l = Math.round(r.left);
|
||||
if (min === null || l < min) min = l;
|
||||
}
|
||||
return min;
|
||||
});
|
||||
inset === null
|
||||
? skip(`${p.name}/${v} 왼쪽 인셋`, "텍스트 없음")
|
||||
: inset >= CFG.thresholds.leftInsetMin
|
||||
? pass(`${p.name}/${v} 왼쪽 인셋`, `최소 텍스트 시작 ${inset}px`)
|
||||
: fail(`${p.name}/${v} 왼쪽 인셋`, `텍스트가 ${inset}px에서 시작 — 기준 ≥${CFG.thresholds.leftInsetMin}px(가장자리에 붙어 보인다)`);
|
||||
}
|
||||
} else skip("왼쪽 인셋", "checks.leftInset=false");
|
||||
}
|
||||
|
||||
// ── L3 시각 회귀 (옵션: pixelmatch·pngjs 설치 시) ──
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue