From 14cb138e1af9dd1aee47afb31a6563a1ef061055 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Sun, 23 Aug 2026 14:33:15 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(site,skill):=20=EB=AA=A8=EB=B0=94?= =?UTF-8?q?=EC=9D=BC=20=EC=95=B1=20=EC=88=98=EC=A4=80=20UX=20=E2=80=94=20?= =?UTF-8?q?=EB=B0=94=ED=85=80=EB=82=B4=EB=B9=84=C2=B7=EB=B0=B1=20=ED=82=A4?= =?UTF-8?q?=20=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=C2=B7=EB=B0=94=ED=85=80?= =?UTF-8?q?=20=EC=8B=9C=ED=8A=B8=C2=B7safe=20area=C2=B7PWA=20=EC=99=80=20L?= =?UTF-8?q?6=20=EC=8B=9C=EB=82=98=EB=A6=AC=EC=98=A4=20E2E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HIG(탭바 5~6·시트)·M3(바텀내비 3~5·48dp)·NN/g·CSS-Tricks 조사(research/mobile-app-ux-v1.md) 후 Nielsen 10 감사로 위반 도출 - 바텀 내비 5탭 + 더보기 바텀 시트(그룹 헤더), 뷰·다이얼로그 pushState/popstate — 안드로이드 백 키가 앱 내 뒤로 작동 - safe area(env+max)·입력 16px(iOS 확대 방지)·coarse 44px 타깃·tap-highlight·overscroll 위생, 인쇄에서 모바일 크롬 제거 - PWA: manifest + sharp 생성 아이콘(가온=장부 문항, 두레=시간표 블록) - tools/e2e.mjs L6 시나리오: RED 1,742 단언 318 실패 → GREEN 1,762/0, verify 체인 7계층 통과 - 실기기(안드로이드 에뮬레이터·Chrome) 텍스트 근거 검증 — 백 키·시트·탭 전환, iOS 는 WebKit(L4) 대체 - 스킬 내재화: mobile-app-ux.md, audit-gate 7계층·하니스 12~14, design-gate 터치타깃 검사, SKILL.md 규칙 7 - 도그푸드: 게이트가 죽은 선택자 8종·thresholds 병합 버그 적발 — CSS v22(양앱) 범프 후 전체 재검증 --- apps/site/design.md | 37 ++ apps/site/gate-report.md | 21 +- apps/site/gate.config.json | 10 +- apps/site/public/work/dure-enrollment/calc.js | 13 +- .../public/work/dure-enrollment/icon-192.png | Bin 0 -> 3915 bytes .../public/work/dure-enrollment/icon-512.png | Bin 0 -> 15332 bytes .../public/work/dure-enrollment/index.html | 257 ++++++++-- .../work/dure-enrollment/manifest.webmanifest | 21 + apps/site/public/work/dure-enrollment/og.png | Bin 78198 -> 83316 bytes .../work/dure-enrollment/styles/main.css | 260 ++++++++-- .../work/dure-enrollment/styles/tokens.css | 30 ++ apps/site/public/work/gaon-lms/calc.js | 12 +- apps/site/public/work/gaon-lms/icon-192.png | Bin 0 -> 3724 bytes apps/site/public/work/gaon-lms/icon-512.png | Bin 0 -> 13710 bytes apps/site/public/work/gaon-lms/index.html | 220 ++++++++- .../public/work/gaon-lms/manifest.webmanifest | 21 + apps/site/public/work/gaon-lms/og.png | Bin 113466 -> 120352 bytes .../site/public/work/gaon-lms/styles/main.css | 299 ++++++++--- .../public/work/gaon-lms/styles/tokens.css | 35 +- apps/site/research/mobile-app-ux-v1.md | 79 +++ .../assets/shots/work-dure-enrollment-2.png | Bin 134419 -> 119866 bytes .../src/assets/shots/work-dure-enrollment.png | Bin 80818 -> 83316 bytes .../site/src/assets/shots/work-gaon-lms-2.png | Bin 146850 -> 143577 bytes apps/site/src/assets/shots/work-gaon-lms.png | Bin 413654 -> 120349 bytes apps/site/src/data/work.ts | 4 +- apps/site/tools/__scan.mjs | 30 ++ .../baseline/dure-enrollment-archive-1440.png | Bin 93165 -> 109050 bytes .../baseline/dure-enrollment-archive-390.png | Bin 45936 -> 52306 bytes .../baseline/dure-enrollment-catalog-1440.png | Bin 106951 -> 121317 bytes .../baseline/dure-enrollment-catalog-390.png | Bin 54820 -> 57452 bytes .../baseline/dure-enrollment-credits-1440.png | Bin 57709 -> 66231 bytes .../baseline/dure-enrollment-credits-390.png | Bin 48626 -> 50959 bytes .../dure-enrollment-enrollments-1440.png | Bin 63368 -> 71755 bytes .../dure-enrollment-enrollments-390.png | Bin 50770 -> 57889 bytes .../baseline/dure-enrollment-notices-1440.png | Bin 76148 -> 90093 bytes .../baseline/dure-enrollment-notices-390.png | Bin 58528 -> 63388 bytes .../baseline/dure-enrollment-starred-1440.png | Bin 75484 -> 84514 bytes .../baseline/dure-enrollment-starred-390.png | Bin 56070 -> 61288 bytes .../baseline/gaon-lms-attendance-1440.png | Bin 90135 -> 100352 bytes .../baseline/gaon-lms-attendance-390.png | Bin 65666 -> 69981 bytes .../baseline/gaon-lms-calendar-1440.png | Bin 111343 -> 126186 bytes .../baseline/gaon-lms-calendar-390.png | Bin 57596 -> 64038 bytes .../baseline/gaon-lms-counsel-1440.png | Bin 126560 -> 144718 bytes .../baseline/gaon-lms-counsel-390.png | Bin 66836 -> 70654 bytes .../baseline/gaon-lms-dashboard-1440.png | Bin 133844 -> 152838 bytes .../baseline/gaon-lms-dashboard-390.png | Bin 59089 -> 63019 bytes .../baseline/gaon-lms-files-1440.png | Bin 108229 -> 123211 bytes .../__shots__/baseline/gaon-lms-files-390.png | Bin 50307 -> 53311 bytes .../baseline/gaon-lms-grades-1440.png | Bin 115621 -> 130652 bytes .../baseline/gaon-lms-grades-390.png | Bin 64398 -> 64968 bytes .../baseline/gaon-lms-notice-1440.png | Bin 87236 -> 98448 bytes .../baseline/gaon-lms-notice-390.png | Bin 62211 -> 70446 bytes .../baseline/gaon-lms-report-1440.png | Bin 113145 -> 128727 bytes .../baseline/gaon-lms-report-390.png | Bin 56048 -> 54332 bytes .../baseline/gaon-lms-students-1440.png | Bin 141117 -> 159213 bytes .../baseline/gaon-lms-students-390.png | Bin 64142 -> 63743 bytes .../current/dure-enrollment-archive-1440.png | Bin 69525 -> 76492 bytes .../current/dure-enrollment-archive-390.png | Bin 34819 -> 37683 bytes .../current/dure-enrollment-catalog-1440.png | Bin 77602 -> 82857 bytes .../current/dure-enrollment-catalog-390.png | Bin 40533 -> 39684 bytes .../current/dure-enrollment-credits-1440.png | Bin 40258 -> 44094 bytes .../current/dure-enrollment-credits-390.png | Bin 35267 -> 36008 bytes .../dure-enrollment-enrollments-1440.png | Bin 39586 -> 42704 bytes .../dure-enrollment-enrollments-390.png | Bin 33326 -> 35587 bytes .../current/dure-enrollment-notices-1440.png | Bin 52649 -> 56753 bytes .../current/dure-enrollment-notices-390.png | Bin 39805 -> 39474 bytes .../current/dure-enrollment-starred-1440.png | Bin 50618 -> 54741 bytes .../current/dure-enrollment-starred-390.png | Bin 38802 -> 39871 bytes .../current/gaon-lms-attendance-1440.png | Bin 73342 -> 78284 bytes .../current/gaon-lms-attendance-390.png | Bin 48327 -> 49311 bytes .../current/gaon-lms-calendar-1440.png | Bin 95016 -> 102135 bytes .../current/gaon-lms-calendar-390.png | Bin 49265 -> 49292 bytes .../current/gaon-lms-counsel-1440.png | Bin 102361 -> 107149 bytes .../current/gaon-lms-counsel-390.png | Bin 57402 -> 53157 bytes .../current/gaon-lms-dashboard-1440.png | Bin 113217 -> 119952 bytes .../current/gaon-lms-dashboard-390.png | Bin 51429 -> 48828 bytes .../__shots__/current/gaon-lms-files-1440.png | Bin 92983 -> 98876 bytes .../__shots__/current/gaon-lms-files-390.png | Bin 43370 -> 44132 bytes .../current/gaon-lms-grades-1440.png | Bin 100744 -> 107624 bytes .../__shots__/current/gaon-lms-grades-390.png | Bin 51004 -> 51318 bytes .../current/gaon-lms-notice-1440.png | Bin 74691 -> 78921 bytes .../__shots__/current/gaon-lms-notice-390.png | Bin 51404 -> 51149 bytes .../current/gaon-lms-report-1440.png | Bin 90649 -> 94099 bytes .../__shots__/current/gaon-lms-report-390.png | Bin 45084 -> 43264 bytes .../current/gaon-lms-students-1440.png | Bin 123047 -> 130056 bytes .../current/gaon-lms-students-390.png | Bin 55939 -> 53156 bytes .../diff-dure-enrollment-archive-1440.png | Bin 73798 -> 71049 bytes .../diff-dure-enrollment-archive-390.png | Bin 37088 -> 37940 bytes .../diff-dure-enrollment-catalog-1440.png | Bin 88712 -> 87564 bytes .../diff-dure-enrollment-catalog-390.png | Bin 45101 -> 43939 bytes .../diff-dure-enrollment-credits-1440.png | Bin 46908 -> 46758 bytes .../diff-dure-enrollment-credits-390.png | Bin 39045 -> 38765 bytes .../diff-dure-enrollment-enrollments-1440.png | Bin 55723 -> 51072 bytes .../diff-dure-enrollment-enrollments-390.png | Bin 44015 -> 46005 bytes .../diff-dure-enrollment-notices-1440.png | Bin 67121 -> 58898 bytes .../diff-dure-enrollment-notices-390.png | Bin 50262 -> 47684 bytes .../diff-dure-enrollment-starred-1440.png | Bin 65936 -> 60228 bytes .../diff-dure-enrollment-starred-390.png | Bin 48889 -> 48188 bytes .../diff-gaon-lms-attendance-1440.png | Bin 64411 -> 69850 bytes .../diff-gaon-lms-attendance-390.png | Bin 42900 -> 45224 bytes .../__shots__/diff-gaon-lms-calendar-1440.png | Bin 81734 -> 90554 bytes .../__shots__/diff-gaon-lms-calendar-390.png | Bin 42955 -> 44323 bytes .../__shots__/diff-gaon-lms-counsel-1440.png | Bin 93879 -> 100749 bytes .../__shots__/diff-gaon-lms-counsel-390.png | Bin 50731 -> 49062 bytes .../diff-gaon-lms-dashboard-1440.png | Bin 101584 -> 105148 bytes .../__shots__/diff-gaon-lms-dashboard-390.png | Bin 43511 -> 42832 bytes .../__shots__/diff-gaon-lms-files-1440.png | Bin 81162 -> 89355 bytes .../__shots__/diff-gaon-lms-files-390.png | Bin 37442 -> 36818 bytes .../__shots__/diff-gaon-lms-grades-1440.png | Bin 86636 -> 91449 bytes .../__shots__/diff-gaon-lms-grades-390.png | Bin 44574 -> 43781 bytes .../__shots__/diff-gaon-lms-notice-1440.png | Bin 64323 -> 71409 bytes .../__shots__/diff-gaon-lms-notice-390.png | Bin 45525 -> 45426 bytes .../__shots__/diff-gaon-lms-report-1440.png | Bin 83168 -> 91393 bytes .../__shots__/diff-gaon-lms-report-390.png | Bin 40842 -> 36889 bytes .../__shots__/diff-gaon-lms-students-1440.png | Bin 104389 -> 109486 bytes .../__shots__/diff-gaon-lms-students-390.png | Bin 44617 -> 44592 bytes apps/site/tools/audit.mjs | 68 +++ apps/site/tools/capture-work-shots.mjs | 47 ++ apps/site/tools/e2e-report.md | 18 + apps/site/tools/e2e.mjs | 467 ++++++++++++++++++ apps/site/tools/exploratory.mjs | 4 +- apps/site/tools/unit/calc.test.mjs | 32 ++ apps/site/tools/verify-report.md | 19 +- apps/site/tools/verify.mjs | 1 + apps/site/tools/webkit.mjs | 2 +- packages/core/CHANGELOG.md | 2 + packages/core/package.json | 2 +- packages/skill/SKILL.md | 19 +- packages/skill/references/audit-gate.md | 20 +- packages/skill/references/mobile-app-ux.md | 144 ++++++ packages/skill/references/motion.md | 1 + packages/skill/references/preflight.md | 21 +- packages/skill/tools/design-gate.mjs | 131 ++++- 133 files changed, 2139 insertions(+), 208 deletions(-) create mode 100644 apps/site/public/work/dure-enrollment/icon-192.png create mode 100644 apps/site/public/work/dure-enrollment/icon-512.png create mode 100644 apps/site/public/work/dure-enrollment/manifest.webmanifest create mode 100644 apps/site/public/work/gaon-lms/icon-192.png create mode 100644 apps/site/public/work/gaon-lms/icon-512.png create mode 100644 apps/site/public/work/gaon-lms/manifest.webmanifest create mode 100644 apps/site/research/mobile-app-ux-v1.md create mode 100644 apps/site/tools/__scan.mjs create mode 100644 apps/site/tools/capture-work-shots.mjs create mode 100644 apps/site/tools/e2e-report.md create mode 100644 apps/site/tools/e2e.mjs create mode 100644 packages/skill/references/mobile-app-ux.md diff --git a/apps/site/design.md b/apps/site/design.md index 9cde6d9..0f7ba59 100644 --- a/apps/site/design.md +++ b/apps/site/design.md @@ -790,3 +790,40 @@ max-age=0 지정(존 설정이 우선하면 효과는 제한적). **운칙: /wor - 사용자 보고 "왼쪽 타임 라벨 깨짐" — 계측으로 확정: 라벨 top 간격이 22·194·43·22 불규칙, 일부 라벨 폭 78px(요일 열에 배치). **원인: span-2 셀이 섞인 그리드 자동 배치(auto-placement)의 취약성** — 시간 라벨(1행)과 셀(2행 스팬)이 교차하며 희소 배치 커서가 열1의 빈 행을 잡아 라벨이 엉뚱한 행·열로 흩어졌다. 세션 초반 의심 목록에 있던 "--rows 미사용 문제"의 실체이기도 하다. - 수리: buildAxis 를 **전 요소 명시적 격자 좌표**(corner 1/1, day 1/(d+1), hour (h-9)*2+2행/1열, cell 행 span2/열 d+1)로 재작성 — 자동 배치 의존 제거. 검증: 9라벨 전부 1열(40px 균일), 간격 Δ43~44px 등차(2행×21.6px 설계 정합). 시각 diff 0.38%(라벨 재배치 = 의도) 확인 후 기준 갱신, 게이트 통과, v18 배포. - **교훈: 혼합 span 의 그리드는 자동 배치를 믿지 않는다** — 좌표를 명시하라. 격자 레이아웃 검사에는 '라벨 등간격(Δ 균일)' 불변식을 넣을 가치가 있다(스킬 하니스 규칙 후보). + +## 2026-08-22 — 프로덕션 마감(v18/v19): 상태 완결성 · 인쇄 · 단축키 · 브랜드 정합 + +디자인 스킬 13종 전량 병행 — designpaca 연장 경로(토큰·컨셉 승계) + redesign-existing-projects 감사 우선. taste 계열(안티슬롭·하이엔드·GSAP·미니멀·브루탈리스트·스티치)은 관리 콘솔에 적용되는 규칙만(상태 완결·대비·모션 문법·카드 절제·메타라벨 금지), 이미지젠 계열 4종은 구성·계층·가독성 기준으로 감사에 반영(관리 콘솔에 사진 소재는 부적합 — 생성 도구도 이 하니스에 없음). + +- **브랜드 낡은 값 교정**: theme-color 가 종이 시대 값(#F7F5EF/#FBFAF7), 파비콘이 v7 프리셋 전환 이전 문양(형광펜/남색)이었다. #fafafa + 현 브랜드 마크(가온=에메랄드 '가', 두레=벽돌 申)로 교체, og.png 재캡처. +- **상태 완결성**: 가온 채점 입력에 범위 검증(GAON.validateScore — 0–100 밖이면 인라인 경고 + 저장 차단 + aria-invalid, 평균은 유효값만 계산). 두레 취소에 실행취소(상태줄 안에 되돌리기 버튼 — 재신청은 canEnroll 로 재심사, 사이에 다른 신청이 끼면 정당하게 막힘). 알림 센터 Esc 닫힘. 로딩 상태는 의도적으로 만들지 않았다 — 동기 인라인 데이터에 스켈레톤은 '저장을 흉내내지 않는다' 원칙 위반. +- **도메인 기능 — 인쇄**: @media print 2종. 가온은 활성 뷰를 서류로(모달이 열려 있으면 body:has(dialog[open]) 로 앱을 숨기고 그 서류만), 두레는 A4 가로 시간표 + 강의계획서. print-color-adjust: exact 로 상태색 보존. 탑바/앱바에 인쇄 버튼. +- **키보드 단축키**: 숫자키 뷰 전환(가온 1–9, 두레 1–6), / 검색 포커스(입력 요소 가드). kbd 물리 키 칩 안내 — 가온 사이드 하단, 두레 앱바(모바일 숨김). +- **모션 문법 토큰화**: --ease-out(0.16,1,0.3,1)·--dur-1/2/3 한 곡선 세 지속시간. 뷰 전환 70ms 상승 — 시각회귀 스크린샷 대기(100ms) 안에 종료시켜 흔들림 없음. prefers-reduced-motion 전역 블랭킷(무한 루프·전환 정지, 기능은 유지). +- **토큰 위생**: 지각 상태쌍(--late/--late-soft), --primary-hover, --accent-strong, --on-accent 를 tokens.css 로 이동 — main.css 의 상수색 제거. radius 를 5종 규모(--radius-xs/6/--radius-md/12/pill)로 정리, 난립하던 2/3/4/8/999px 리터럴 종식. +- **죽은 코드 제거**: 가온 .page-head·.btn-primary/outline/ghost·.hl-count·.stamp(HTML 3곳·CSS), 두레 .modal-title/.modal-meta/.modal-inner/.facts(+모바일 쿼리 잔여), 두레 renderCreditsView 이중 계산 블록(첫 블록이 즉시 덮임), conflictsWith(canEnroll 로 흡수). +- **calc.js 확장**: GAON.validateScore, DURE.canEnroll(정원·겹침·학점 한 번에 판정 — 모달 경고 문장과 실행취소 재심사가 같은 순수 함수 공유). 단위 테스트 30→41건. +- **감사 도구 확장(도구도 제품)**: audit.mjs +24항목(채점 차단/해제·실행취소 복원·단축키 4건·인쇄 CSS 존재). exploratory 차터1 을 실제 완주로 고침 — type("88") 이 기존 값에 덧붙어 "9288"이 되던 조용한 테스트 결함(검증 추가로 처음 드러남; number 입력은 선택 덮어쓰기 불가 — 비우고 타이핑). +- **사고 2건, 게이트가 잡음**: ①두레 320px +18px — 인쇄 버튼이 앱바 최소폭 합을 넘김 → ≤680px 액션 줄 2행 랩. ②가온 390px 6% 시각 diff — .side-kbd 기본 display:flex 가 파일 뒤쪽에 있어 앞쪽 900px 미디어쿼리의 display:none 을 캐스케이드로 이김 → 숨김 오버라이드는 기본 규칙 **보다 뒤에** 두어야 한다. 캐시 운칙 준수: main.css v18(가온)/v19(두레), calc.js?v=2. +- 쇼케이스 -2컷(모달 열림) 재캡처 — capture-work-shots.mjs 신설(CLI 크롬 --screenshot 은 모달을 못 열어 puppeteer 가 담당). 게이트 전 계층 통과(L0 2종·L1 41·L2 72+10·L3 30·L4 8·L5 14). + +## 2026-08-22 — 스킬 내재화(v20): 이번 사고를 스킬 규칙으로 승격 + +프로덕션 마감 라운드에서 게이트가 *사후에* 잡은 결함 11종을 "스킬이 처음부터 조였다면 원천 차단됐을 규칙"으로 도출해 designpaca 스킬에 박았다(packages/skill — 하드게이트 13·14, 4-5 서류 기능 단계, preflight 상태 완결성 §4-1, 하니스 규칙 8~11, design-gate 신규 3검사). 이 앱들은 그 도그푸드 대상이다. +- **게이트가 수작업 스윕을 이겼다**: 새 deadCss 검사가 내가 직접 정리하고도 놓친 `.rep-stat-sub`·`:not(.page-head)` 잔여(모바일 쿼리)를, cssHygiene 이 `.bell-head button`의 `border-radius: 4px` 리터럴을 각각 잡음. "눈 검사로는 절대 전부 못 잡는다"가 이 라운드의 입증이다. 세 건 정리. +- 색 리터럴 잔여 전량 토큰화: 표지판 위 잉크(--panel-muted/faint/line/mark)·백드롭(--backdrop-dim)·인쇄 매체(--print-line/paper)·두레 평가 농도(--accent-tint-2/3) — main.css 색·radius 리터럴 0건(양 앱, 게이트 실측). +- 검증: 스킬 게이트 신규 검사 도그푸드 양방향 — 고의 결함 픽스처(죽은 클래스·토큰 밖 색·theme-color 불일치)는 3검사 정확히 FAIL, 실제 두 앱은 전 PASS. 리포 게이트 L0~L5 재통과. CSS v20(가온). + +## 2026-08-23 — 모바일 앱 수준 UX(v21/v22): HIG·M3 표준 이행 · L6 시나리오 E2E · 실기기 검증 + +13종 디자인 스킬 재동원(designpaca 연장 + redesign 감사 우선). 조사를 먼저 — Apple HIG(탭바 5~6·시트·safe area), Material 3(바텀내비 3~5·48dp), NN/g(바텀 시트·터치타깃), CSS-Tricks(iOS 16px 확대)를 근거로 Nielsen 10 휴리스틱 감사로 위반 도출(백 키 앱 종료·중앙 모달 썸존 밖·safe area 무시·입력 확대 유발). 조사·계획: research/mobile-app-ux-v1.md. + +- **앱 탐색 구조**: 바텀 내비 5탭(가온 홈/수강생/출석부/성적/더보기, 두레 과목 찾기/관심/신청 내역/이수/더보기) + 더보기 바텀 시트(그룹 헤더 — 가온 학적/수업/학사 9항목, 두레 6항목). ≤900px 사이드내비/탭 전환. +- **하드웨어 뒤로가기 = 앱 내 히스토리**: 뷰 전환 pushState, 다이얼로그도 같은 스택(suppressPop/popClosing 상호 무시 플래그, 더보기 선택은 pendingMoreSelect 로 back+push 접합). 백 키가 앱을 종료하지 않는다. +- **safe area·입력·터치**: viewport-fit=cover + env(safe-area-inset-*) max() 패딩, ≤680px 입력 16px(iOS 확대 방지 — maximum-scale=1 은 WCAG 위반으로 안 씀), coarse 44px 타깃, tap-highlight·overscroll·touch-action 위생. +- **바텀 시트**: ≤680px 모달이 하단 시트로(그래버 36×4, 92dvh, sheet-up 모션), 인쇄에서 모바일 크롬 제거. +- **PWA**: manifest.webmanifest + sharp 생성 아이콘 192/512(가온=장부 문항 에메랄드, 두레=시간표 블록 벽돌) + apple-touch-icon·모바일 메타 2종. +- **TDD RED→GREEN**: tools/e2e.mjs 신설 — 앱 기반·320/390/768 매트릭스·터치타깃 231·대비 1,120·백 키·시트·더보기·강사 하루 장기 여정(S1 25단언)·정정기간 여정(S2 27). RED 1,742 단언 318 실패(신규 기능에 정확히 집중) → GREEN 1,762/0. verify 체인 L6 등록, 전 계층 재통과. +- **실기기 검증(안드로이드 에뮬레이터·실제 Chrome)**: adb reverse + uiautomator dump 텍스트 근거로 — 가온 렌더·수강생 탭·**백 키→대시보드 복귀**·더보기 시트·공지 선택·백 복귀, 두레 렌더·관심 탭·백 복귀. iOS 는 WebKit 엔진(L4)으로 대체(Windows 제약 문서화). 비전은 전환 중 프레임 오탐이 있어 '여부' 판정은 계측에만. +- **도그푸드가 잡은 사고**: ①스킬 게이트 새 tapTargets 검사 개발 중 thresholds 얕은 병합 버그(구형 설정이 신규 기본값 지움, "≥undefinedpx") → 깊은 병합. ②죽은 선택자 검사가 이번 라운드 내가 만든 `.btn-seal/.btn-star`(가온에 없는 버튼)과 두레 포크 잔재 6종을 적발 → 삼위일체 제거, 세그먼트 `seg-${n}` JS 조립은 무시 목록(seg-)으로 오탐 차단. ③두레 `.btn-seal`은 생존(신청 버튼) — coarse 사이징 실수로 뺐다가 게이트 재실행으로 복원. CSS v22(양앱). +- **스킬 내재화(v0.7 예정)**: references/mobile-app-ux.md 신규(5축 표·뒤로가기 전체 패턴·실기기 검증법·출처), audit-gate 7계층(L6 시나리오)·하니스 12~14(NaN 방어·fixed 판정·실기기 텍스트 근거), design-gate tapTargets 내장, SKILL.md 규칙 7 "OS 관습은 조사 없이 바꾸지 않는다". diff --git a/apps/site/gate-report.md b/apps/site/gate-report.md index b351de0..bd4f2f9 100644 --- a/apps/site/gate-report.md +++ b/apps/site/gate-report.md @@ -1,9 +1,13 @@ -# 게이트 리포트 — 2026-08-22 12:20 +# 게이트 리포트 — 2026-08-23 04:48 **전 항목 통과 — 배포 가능** ``` PASS L0 정적 — SKIP — checks.static=false +PASS gaon-lms 죽은 선택자 — 2개 CSS 전 클래스 사용 중(무시 2접두사) +PASS dure-enrollment 죽은 선택자 — 2개 CSS 전 클래스 사용 중(무시 2접두사) +PASS gaon-lms 토큰 위생 — 색 0·radius 0 리터럴(토큰 1파일) +PASS dure-enrollment 토큰 위생 — 색 0·radius 0 리터럴(토큰 1파일) PASS gaon-lms SEO/meta — title 18자·desc 74자·OG✓·alt 0결 PASS gaon-lms/dashboard 오버플로 — 320/375/390/768/1440 PASS gaon-lms/dashboard h1 1줄 @@ -52,6 +56,15 @@ PASS gaon-lms/notice 리듬·트랙 PASS gaon-lms/notice 대비 AA PASS gaon-lms 스케일×폭 h1 — scales 1/1.3 PASS gaon-lms 브랜드 1줄 +PASS gaon-lms/dashboard 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS gaon-lms/students 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS gaon-lms/attendance 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS gaon-lms/grades 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS gaon-lms/files 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS gaon-lms/calendar 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS gaon-lms/counsel 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS gaon-lms/report 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS gaon-lms/notice 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px PASS dure-enrollment SEO/meta — title 15자·desc 79자·OG✓·alt 0결 PASS dure-enrollment/catalog 오버플로 — 320/375/390/768/1440 PASS dure-enrollment/catalog h1 1줄 @@ -85,6 +98,12 @@ PASS dure-enrollment/notices 리듬·트랙 PASS dure-enrollment/notices 대비 AA PASS dure-enrollment 스케일×폭 h1 — scales 1/1.3 PASS dure-enrollment 브랜드 1줄 +PASS dure-enrollment/catalog 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS dure-enrollment/starred 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS dure-enrollment/enrollments 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS dure-enrollment/credits 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS dure-enrollment/archive 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px +PASS dure-enrollment/notices 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px PASS L3 시각 회귀 — SKIP — checks.visual=false PASS L4 WebKit — SKIP — checks.webkit=false ``` diff --git a/apps/site/gate.config.json b/apps/site/gate.config.json index 03362ed..65f8b75 100644 --- a/apps/site/gate.config.json +++ b/apps/site/gate.config.json @@ -61,5 +61,13 @@ "visual": false, "webkit": false }, - "chromePath": null + "chromePath": null, + "deadCssIgnore": [ + "is-", + "seg-" + ], + "tapTargetsInline": [ + ".row-open", + ".undo-btn" + ] } diff --git a/apps/site/public/work/dure-enrollment/calc.js b/apps/site/public/work/dure-enrollment/calc.js index 61a26fd..1bd883d 100644 --- a/apps/site/public/work/dure-enrollment/calc.js +++ b/apps/site/public/work/dure-enrollment/calc.js @@ -24,7 +24,18 @@ // 이수 잔여 — 음수면 '초과' const remaining = (need, done, now) => need - done - now; - const api = { isOverlap, credits, wouldExceed, seatsState, remaining }; + // 신청 가능 판정 — 정원·겹침·학점을 한 번에. 모달의 경고 문장과 실행취소 재심사가 같은 판정을 공유한다. + const canEnroll = (course, enrolledList, currentCredits, max) => { + if (!course || !course.seats) return { ok: false, reason: "full" }; + const names = (enrolledList || []) + .filter((o) => isOverlap(course, o)) + .map((o) => o.name); + if (names.length) return { ok: false, reason: "overlap", names }; + if (wouldExceed(currentCredits, course.credits, max)) return { ok: false, reason: "credits" }; + return { ok: true }; + }; + + const api = { isOverlap, credits, wouldExceed, seatsState, remaining, canEnroll }; global.DURE = api; if (typeof module !== "undefined") module.exports = api; })(typeof window !== "undefined" ? window : globalThis); diff --git a/apps/site/public/work/dure-enrollment/icon-192.png b/apps/site/public/work/dure-enrollment/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..aeebe2e924d3750d6a8b5b7e144b91c668e9c083 GIT binary patch literal 3915 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 lyX2#7CUJR7j - + 두레대학 수강신청 · 시간표 + + + + @@ -18,7 +22,7 @@ - + @@ -31,10 +35,10 @@ rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=IBM+Plex+Sans+KR:wght@400;500;600&family=Noto+Serif+KR:wght@500;600&display=swap" /> - + @@ -48,9 +52,15 @@ 두레대학 수강신청 -

- 신청 학점 0 / 18 -

+
+

1–6 화면 전환 · / 과목 검색

+

+ 신청 학점 0 / 18 +

+ +