feat: strengthen design skill and refresh all showcases
Some checks failed
ci / build (push) Has been cancelled
Some checks failed
ci / build (push) Has been cancelled
This commit is contained in:
parent
69e5cc1efd
commit
e09d4efb1c
257 changed files with 8196 additions and 2374 deletions
|
|
@ -21,6 +21,16 @@ const jclick = (sel) => page.evaluate((s) => { const el = document.querySelector
|
|||
const over = () => page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth);
|
||||
const gotoFile = async (app) =>
|
||||
page.goto(`file:///${path.join(root, "public/work", app, "index.html")}`, { waitUntil: "networkidle0" });
|
||||
const gotoWorkFile = async (...parts) =>
|
||||
page.goto(`file:///${path.join(root, "public/work", ...parts)}`, { waitUntil: "networkidle0" });
|
||||
const centeredDialog = (selector) => page.$eval(selector, (dialog) => {
|
||||
const rect = dialog.getBoundingClientRect();
|
||||
return {
|
||||
deltaX: Math.abs(rect.left + rect.width / 2 - innerWidth / 2),
|
||||
deltaY: Math.abs(rect.top + rect.height / 2 - innerHeight / 2),
|
||||
inViewport: rect.left >= 0 && rect.top >= 0 && rect.right <= innerWidth && rect.bottom <= innerHeight,
|
||||
};
|
||||
});
|
||||
|
||||
// ══════════ 가온 학적부 (8 뷰) ══════════
|
||||
await gotoFile("gaon-lms");
|
||||
|
|
@ -253,6 +263,103 @@ for (const w of [320, 390]) {
|
|||
pass(`두레 ${w}px`, od <= 0, `+${od}px`);
|
||||
}
|
||||
|
||||
// ── 사용자 보고 회귀: 팝업·모달은 좌상단에 붙거나 화면 밖으로 잘리면 안 된다 ──
|
||||
await page.setViewport({ width: 390, height: 320 });
|
||||
await gotoFile("gaon-lms");
|
||||
await jclick("#bell");
|
||||
const bellRect = await page.$eval("#bell-menu", (menu) => {
|
||||
const r = menu.getBoundingClientRect();
|
||||
return { left: r.left, top: r.top, right: r.right, bottom: r.bottom, clientHeight: menu.clientHeight, scrollHeight: menu.scrollHeight };
|
||||
});
|
||||
pass("가온 짧은 화면 알림판 뷰포트·스크롤", bellRect.left >= 0 && bellRect.top >= 0 && bellRect.right <= 390 && bellRect.bottom <= 320 && bellRect.clientHeight > 0 && bellRect.scrollHeight >= bellRect.clientHeight, JSON.stringify(bellRect));
|
||||
|
||||
for (const viewport of [{ width: 390, height: 844 }, { width: 1440, height: 900 }, { width: 2560, height: 900 }]) {
|
||||
await page.setViewport(viewport);
|
||||
await gotoFile("materia-interior");
|
||||
await jclick('[onclick^="openProjectModal"]');
|
||||
const projectPosition = await centeredDialog("#projectModal");
|
||||
pass(`마테리아 프로젝트 모달 중앙 ${viewport.width}px`, projectPosition.inViewport && projectPosition.deltaX <= 1 && projectPosition.deltaY <= 1, JSON.stringify(projectPosition));
|
||||
const projectScrollLock = await page.$eval("html", (root) => getComputedStyle(root).overflow);
|
||||
pass(`마테리아 프로젝트 모달 배경 잠금 ${viewport.width}px`, projectScrollLock === "hidden", projectScrollLock);
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
await gotoWorkFile("materia-interior", "projects", "hannam-duplex", "index.html");
|
||||
await page.waitForSelector(".gallery-card-action");
|
||||
await jclick(".gallery-card-action");
|
||||
const lightboxPosition = await centeredDialog("#lightboxDialog");
|
||||
pass(`마테리아 갤러리 모달 중앙 ${viewport.width}px`, lightboxPosition.inViewport && lightboxPosition.deltaX <= 1 && lightboxPosition.deltaY <= 1, JSON.stringify(lightboxPosition));
|
||||
const lightboxScrollLock = await page.$eval("html", (root) => getComputedStyle(root).overflow);
|
||||
pass(`마테리아 갤러리 모달 배경 잠금 ${viewport.width}px`, lightboxScrollLock === "hidden", lightboxScrollLock);
|
||||
await page.keyboard.press("Escape");
|
||||
}
|
||||
|
||||
await page.setViewport({ width: 390, height: 320 });
|
||||
await gotoFile("hannam-jari");
|
||||
await jclick(".menu-button");
|
||||
const mobileNav = await page.$eval(".site-nav", (nav) => {
|
||||
const r = nav.getBoundingClientRect();
|
||||
return { top: r.top, bottom: r.bottom, clientHeight: nav.clientHeight, scrollHeight: nav.scrollHeight };
|
||||
});
|
||||
pass("한남자리 짧은 화면 메뉴 뷰포트·스크롤", mobileNav.top >= 0 && mobileNav.bottom <= 320 && mobileNav.clientHeight > 0 && mobileNav.scrollHeight >= mobileNav.clientHeight, JSON.stringify(mobileNav));
|
||||
|
||||
await gotoWorkFile("hannam-jari", "admin", "index.html");
|
||||
await jclick("[data-select-row]");
|
||||
const inspectorScroll = await page.$eval(".inspector-scroll", (panel) => ({ clientHeight: panel.clientHeight, scrollHeight: panel.scrollHeight }));
|
||||
pass("한남자리 짧은 화면 조사관 내부 스크롤", inspectorScroll.clientHeight > 0 && inspectorScroll.scrollHeight >= inspectorScroll.clientHeight, JSON.stringify(inspectorScroll));
|
||||
|
||||
// ── 사용자 보고 회귀: 2560px에서 사진 한 장이 본문 밀도를 압도하지 않는다 ──
|
||||
await page.setViewport({ width: 2560, height: 900 });
|
||||
await gotoFile("griddle-club");
|
||||
const griddleWide = await page.$eval(".hero", (hero) => {
|
||||
const rect = (selector, scope = hero) => {
|
||||
const r = scope.querySelector(selector).getBoundingClientRect();
|
||||
return { left: r.left, width: r.width, right: r.right };
|
||||
};
|
||||
const visualLineCount = (node) => {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(node);
|
||||
return new Set([...range.getClientRects()].map((r) => Math.round(r.top))).size;
|
||||
};
|
||||
const heroRect = hero.getBoundingClientRect();
|
||||
const rail = document.querySelector(".menu-grid").getBoundingClientRect();
|
||||
const process = document.querySelector(".process-grid");
|
||||
const processRect = process.getBoundingClientRect();
|
||||
const processHeading = process.querySelector(".process-copy h2");
|
||||
return {
|
||||
hero: { left: heroRect.left, width: heroRect.width, right: heroRect.right },
|
||||
copy: rect(".hero-copy"),
|
||||
media: rect(".hero-media"),
|
||||
contentRail: { left: rail.left, width: rail.width, right: rail.right },
|
||||
process: {
|
||||
rail: { left: processRect.left, width: processRect.width, right: processRect.right },
|
||||
copy: rect(".process-copy", process),
|
||||
media: rect(".process-media", process),
|
||||
heading: rect(".process-copy h2", process),
|
||||
headingLines: visualLineCount(processHeading),
|
||||
},
|
||||
};
|
||||
});
|
||||
const heroCentered = Math.abs(griddleWide.hero.left + griddleWide.hero.width / 2 - 1280) <= 1;
|
||||
pass("철판클럽 2560px 히어로·이미지 비율", heroCentered
|
||||
&& griddleWide.hero.width >= 2140
|
||||
&& griddleWide.hero.width <= 2162
|
||||
&& griddleWide.copy.width >= 840
|
||||
&& griddleWide.copy.width <= 902
|
||||
&& griddleWide.media.width >= 1050
|
||||
&& griddleWide.media.width <= 1121
|
||||
&& griddleWide.media.width <= griddleWide.copy.width * 1.3, JSON.stringify(griddleWide));
|
||||
pass("철판클럽 2560px 후속 콘텐츠 레일", griddleWide.contentRail.width >= 1200 && griddleWide.contentRail.width <= 1482 && griddleWide.contentRail.left >= 0 && griddleWide.contentRail.right <= 2560, JSON.stringify(griddleWide.contentRail));
|
||||
const processCentered = Math.abs(griddleWide.process.rail.left + griddleWide.process.rail.width / 2 - 1280) <= 1;
|
||||
pass("철판클럽 2560px 과정 텍스트·이미지 분할", processCentered
|
||||
&& griddleWide.process.rail.width >= 1678
|
||||
&& griddleWide.process.rail.width <= 1682
|
||||
&& griddleWide.process.copy.width >= 810
|
||||
&& griddleWide.process.copy.width <= 835
|
||||
&& griddleWide.process.media.width >= 840
|
||||
&& griddleWide.process.media.width <= 870
|
||||
&& griddleWide.process.heading.width >= 620
|
||||
&& griddleWide.process.headingLines <= 2, JSON.stringify(griddleWide.process));
|
||||
|
||||
// ── CSS 중괄호 균형 (v11 교훈) ──
|
||||
import fs2 from "node:fs";
|
||||
for (const f of ["gaon-lms/styles/main.css", "dure-enrollment/styles/main.css", "gaon-lms/styles/tokens.css", "dure-enrollment/styles/tokens.css"]) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue