From 072964cb571551c09f24c575bd72e07de374716f Mon Sep 17 00:00:00 2001 From: ldm310 Date: Wed, 20 May 2026 04:00:02 -0700 Subject: [PATCH 01/19] Add web S0-S6 screens, SSE hook, KaTeX renderer, login/samples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 랜딩 추가 컴포넌트 + 출제 워크플로우 전 화면 구현. 랜딩: - components/landing/footer.tsx: 3-col (소개·도움말·피드백) + fine-print - components/landing/faq.tsx: 4 native disclosure-row - components/landing/book-stage.tsx: iPad showcase 5-step walkthrough (S0→S1→S2→S3→S4 자동 순환, BrandMark 사용) - components/landing/nav.tsx: 3-grid 정중앙 + 로그인 버튼 - hero CTA: /app, /samples 로 연결 출제 워크플로우 (S0~S6, productivity surface): - /app: S0 워크스페이스 (hero-tile + 2 entry cards) - /app/new/grade: S1 학년 선택 (3 intent-radio-card) - /app/new/topic: S2 단원 선택 (filter-chip + 30 단원 정적 데이터) - /app/new/intent: S3 동형 모드 + 평가 차원 (radio + checkbox) - /app/new/verify: S4 검증 6 단계 (SSE 실시간) - /app/new/result: S5 결과 + 채택 (mock.ts, agent 미연동) - /app/new/export: S6 PDF 출력 (브라우저 print API) 부가 페이지: - /login: 로그인 폼 (mock auth — 제출 시 /app 라우팅) - /samples: 검증 통과 예시 3 문항 공통 인프라: - hooks/use-verification-stream.ts: @microsoft/fetch-event-source 기반 SSE 컨슈머 + AbortController + React 18 strict-mode 가드 - components/math/latex-renderer.tsx: KaTeX SSR (block + inline + $..$ / $$..$$ 자동 분할 LatexMixed) - components/app/primary-nav.tsx: productivity nav DESIGN.md 준수: - btn-primary drop shadow 제거 (brand-mark glow 재현 금지 규칙) - 한 fold 1 primary: nav 버튼 → btn-ghost / btn-secondary - LaTeX 항상 formula-stage (soft-cloud) 위 — S5 풀이 inline 전환 - inline hex 0건, as any/@ts-ignore 0건 - prefers-reduced-motion: spinner-dot, intent cards, step rows 등 커버 기타: - .gitignore: docs/product/ 화이트리스트 (USER_FLOW, SCREENS, MOCKUPS) - README: 작업 진행 상태 + 라우트 맵 + 신규 deps + mock 한계 명시 - globals.css: orphan CSS ~1000줄 정리 (옛 book v1/v2, tablet v1, mock-intro/result, token-v2, tablet-signal/brand-mark/login-btn) - 새 deps: @microsoft/fetch-event-source 2.0.1, katex 0.16.x --- .gitignore | 2 + docs/product/MOCKUPS.md | 609 ++++ docs/product/SCREENS.md | 555 ++++ docs/product/USER_FLOW.md | 378 +++ packages/web/README.md | 47 +- packages/web/app/app/layout.tsx | 21 + packages/web/app/app/new/export/page.tsx | 52 + packages/web/app/app/new/export/view.tsx | 421 +++ packages/web/app/app/new/grade/page.tsx | 11 + packages/web/app/app/new/grade/picker.tsx | 105 + packages/web/app/app/new/intent/page.tsx | 28 + packages/web/app/app/new/intent/picker.tsx | 257 ++ packages/web/app/app/new/result/mock.ts | 182 ++ packages/web/app/app/new/result/page.tsx | 55 + packages/web/app/app/new/result/view.tsx | 344 +++ packages/web/app/app/new/topic/data.ts | 427 +++ packages/web/app/app/new/topic/page.tsx | 18 + packages/web/app/app/new/topic/picker.tsx | 181 ++ packages/web/app/app/new/verify/page.tsx | 41 + packages/web/app/app/new/verify/view.tsx | 271 ++ packages/web/app/app/page.tsx | 75 + packages/web/app/globals.css | 2707 +++++++++++++++-- packages/web/app/login/form.tsx | 77 + packages/web/app/login/page.tsx | 52 + packages/web/app/page.tsx | 6 +- packages/web/app/samples/page.tsx | 127 + packages/web/components/app/primary-nav.tsx | 36 + .../web/components/landing/book-stage.tsx | 523 ++-- packages/web/components/landing/faq.tsx | 48 + .../web/components/landing/feature-strip.tsx | 2 +- packages/web/components/landing/footer.tsx | 72 + packages/web/components/landing/hero.tsx | 8 +- packages/web/components/landing/nav.tsx | 19 +- .../web/components/math/latex-renderer.tsx | 159 + packages/web/hooks/use-verification-stream.ts | 399 +++ packages/web/package.json | 3 + pnpm-lock.yaml | 33 + 37 files changed, 7867 insertions(+), 484 deletions(-) create mode 100644 docs/product/MOCKUPS.md create mode 100644 docs/product/SCREENS.md create mode 100644 docs/product/USER_FLOW.md create mode 100644 packages/web/app/app/layout.tsx create mode 100644 packages/web/app/app/new/export/page.tsx create mode 100644 packages/web/app/app/new/export/view.tsx create mode 100644 packages/web/app/app/new/grade/page.tsx create mode 100644 packages/web/app/app/new/grade/picker.tsx create mode 100644 packages/web/app/app/new/intent/page.tsx create mode 100644 packages/web/app/app/new/intent/picker.tsx create mode 100644 packages/web/app/app/new/result/mock.ts create mode 100644 packages/web/app/app/new/result/page.tsx create mode 100644 packages/web/app/app/new/result/view.tsx create mode 100644 packages/web/app/app/new/topic/data.ts create mode 100644 packages/web/app/app/new/topic/page.tsx create mode 100644 packages/web/app/app/new/topic/picker.tsx create mode 100644 packages/web/app/app/new/verify/page.tsx create mode 100644 packages/web/app/app/new/verify/view.tsx create mode 100644 packages/web/app/app/page.tsx create mode 100644 packages/web/app/login/form.tsx create mode 100644 packages/web/app/login/page.tsx create mode 100644 packages/web/app/samples/page.tsx create mode 100644 packages/web/components/app/primary-nav.tsx create mode 100644 packages/web/components/landing/faq.tsx create mode 100644 packages/web/components/landing/footer.tsx create mode 100644 packages/web/components/math/latex-renderer.tsx create mode 100644 packages/web/hooks/use-verification-stream.ts diff --git a/.gitignore b/.gitignore index d25d7ca..0f8e104 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ opencode.json docs/* !docs/specs !docs/specs/** +!docs/product +!docs/product/** deliverables/ # Node.js diff --git a/docs/product/MOCKUPS.md b/docs/product/MOCKUPS.md new file mode 100644 index 0000000..fb15dda --- /dev/null +++ b/docs/product/MOCKUPS.md @@ -0,0 +1,609 @@ +# Product Spec — Mockups + +| | | +|---|---| +| Status | Draft | +| Last updated | 2026-05-20 | +| Supersedes | — | +| Depends on | `docs/product/SCREENS.md` (Draft) · `docs/product/USER_FLOW.md` (Draft) · `packages/web/DESIGN.md` (alpha) | + +이 문서는 OpenMath 각 화면의 **레이아웃 mockup** 을 ASCII art 로 보여준다. +"이 화면이 desktop 에서 *대략 이렇게 생겼다*" 를 spec 텍스트만 읽기 전에 한눈에 +파악하기 위한 보조 자료. 픽셀·간격·색은 ASCII 가 표현하지 못하므로 정확한 +시각 토큰은 [DESIGN.md](../../packages/web/DESIGN.md) 를, 컴포넌트 상태·데이터·a11y +는 [SCREENS.md](./SCREENS.md) 를 본다. + +모든 mockup 은 **desktop-large (1440px)** 기준이며, 일부 화면에 한해 mobile (320~599px) +변형을 부록에 둔다. + +## 범례 + +| 글리프 | 의미 | +|---|---| +| `┌─┐ │ └─┘` | 컴포넌트 경계 (카드·섹션·페이지) | +| `├─┤` | row divider (`{colors.hairline-soft}` 또는 `{colors.hairline}`) | +| `┃` | 3px left-indicator (`step-progress-row-active`, `result-card-failed`) | +| `▓` | `{colors.ink}` 솔리드 면 (hero-tile-productivity) | +| `▒` | `{colors.soft-cloud}` 면 (formula-stage, pdf-preview-thumbnail) | +| `░` | `{colors.canvas}` warm ivory (editorial surface) | +| `●` | 선택된 radio dot | +| `○` | 비선택 radio dot | +| `☑` | 체크된 checkbox | +| `☐` | 비체크 checkbox | +| `✓` `✗` `✦` `⚠` | 검증 시그널 아이콘 (pass / fail / concept / warn) | +| `⠋` | `spinner-dot` 진행 인디케이터 | +| `[ 라벨 ]` | pill 모양 CTA (`{rounded.pill}`) | +| `( 라벨 )` | `filter-chip` (`{rounded.pill}` + 1px hairline) | +| `(★)` `(↻)` `(✎)` `(×)` | `button-icon-circular` | + +--- + +## Landing — `/` + +Editorial surface (`{colors.canvas}` warming ivory). 사진 자리에 책 4권 + 떠다니는 수식 토큰. + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ 도움말 · 피드백 │ utility-bar +├────────────────────────────────────────────────────────────────────────┤ +│ ◆ OpenMath 교육과정 검증 문서 연구 [ 문제 생성하기 → ] │ primary-nav +├────────────────────────────────────────────────────────────────────────┤ +│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ +│░ ▢ 검증된 문제 한 세트 ░│ eyebrow +│░ ░│ +│░ 수학 문제는 ┌──┐┌──┐┌──┐┌──┐ ░│ +│░ 그럴듯해서는 │ ││ ││ ││ │ ← book-cover × 4 ░│ +│░ 안 된다. │ ││ ││ ││ │ (rotateX 28°) ░│ +│░ 맞아야 한다. │ ││ ││ ││ │ ░│ display-hero +│░ │ ││ ││ ││ │ ∑ⁿₖ₌₁ k² ░│ (Fraunces +│░ · 회원가입 X └──┘└──┘└──┘└──┘ ░│ 96~104px) +│░ · 중1–중3 전 단원 중1 중2 중3 통합 ░│ +│░ · 출제 5~30초 ░│ +│░ √(b² − 4ac) ░│ floating-token +│░ [ 무료로 시작하기 ] [ 샘플 문제 보기 ] ░│ +│░ ▢ 마우스 hover 시 표지 펼침 ░│ hint-pill +│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ +├────────────────────────────────────────────────────────────────────────┤ +│ 01 │ 02 │ 03 │ +│ 검증된 답 │ 두 가지 동형 │ A4 PDF 출력 │ feature-strip +│ SymPy 가 답을 │ 구조 / 개념을 │ 학원 시험지 그대로 │ (3-col, +│ 직접 증명 │ 분리해 선택 │ 한 번에 다운로드 │ border-right +│ │ │ │ rule) +├────────────────────────────────────────────────────────────────────────┤ +│ © OpenMath · Capstone 2026 build alpha-0.1 │ footline +└────────────────────────────────────────────────────────────────────────┘ +``` + +**핵심 시각 결정:** +- 한 fold 안에 `button-primary` "무료로 시작하기" 와 `button-ghost` "샘플 문제 보기" 두 개. 다른 chrome accent 없음. +- 책 표지 (`{rounded.book}` 비대칭 4/14/14/4) 가 우측 절반 점유. 사진 X. +- 떠다니는 토큰 6개 (mobile 에서 3개) 가 책 주변에서 parallax. + +--- + +## S0 — Workspace — `/app` + +Productivity surface (`{colors.canvas-pure}` 순백). 검은 hero-tile 한 띠 + entry card 2-up. + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ◆ OpenMath 교육과정 검증 문서 연구 [ 문제 생성하기 → ] │ primary-nav +├────────────────────────────────────────────────────────────────────────┤ +│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ +│▓ ▓│ +│▓ 검증된 문제 ▓│ hero-tile- +│▓ 한 세트. ▓│ productivity +│▓ ▓│ (ink bg, +│▓ 학년 → 단원 → 의도 → 검증 → PDF. ▓│ Fraunces 72px +│▓ ▓│ on-ink) +│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ +│ │ +│ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │ +│ │ 새 문제 만들기 │ │ 이 문제처럼 [⚠ 준비 중] │ │ +│ │ │ │ │ │ +│ │ 학년·단원·평가 차원을 골라 │ │ 기존 문제 이미지에서 │ │ job-entry-card +│ │ 동형 문제를 만듭니다. │ │ 동형을 추출합니다. │ │ × 2 (S0-A / S0-B) +│ │ │ │ │ │ +│ │ · 5~30초 출제 │ │ · OCR 입력 │ │ S0-B = disabled +│ │ · 한 번에 3~10문항 │ │ · 캡스톤 데모 단계 비활성 │ │ +│ │ · 검증 6단계 노출 │ │ │ │ +│ │ │ │ │ │ +│ │ [ 시작하기 → ] │ │ [ 준비 중 ] │ │ +│ └─────────────────────────────┘ └─────────────────────────────┘ │ +│ │ +├────────────────────────────────────────────────────────────────────────┤ +│ 소개 도움말 캡스톤 피드백 │ +│ · 문서 · FAQ · 발표 자료 · 이메일 │ footer 4-col +│ · 변경 이력 · 가이드 · 데모 영상 · 설문 │ +├────────────────────────────────────────────────────────────────────────┤ +│ © OpenMath 2026 build alpha-0.1 │ footline +└────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## S1 — 학년 선택 — `/app/new/grade` + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ◆ OpenMath [ 문제 생성하기 → ] │ primary-nav +├────────────────────────────────────────────────────────────────────────┤ +│ ← 워크스페이스 / 새 문제 만들기 (1/4) │ sub-nav +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 어느 학년인가요? │ heading-xl +│ 이번 출제의 대상 학년을 고르세요. │ body-md ink-3 +│ │ +│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ +│ │ ● 중1 │ │ ○ 중2 │ │ ○ 중3 │ │ intent-radio- +│ │ │ │ │ │ │ │ card × 3 +│ │ │ │ │ │ │ │ (3-up grid) +│ └━━━━━━━━━━━━━━━━━━┘ └──────────────────┘ └──────────────────┘ │ +│ ↑ active: border 2px ink, bg soft-cloud │ +│ │ +│ │ +│ │ +├────────────────────────────────────────────────────────────────────────┤ +│ [ 취소 ] [ 다음 → ] │ action-bar- +│ │ sticky +└────────────────────────────────────────────────────────────────────────┘ +``` + +**상태 변형:** Empty (`button-primary` disabled, `{colors.ink-4}` 텍스트) ↔ Selected (위 그림과 동일, "다음 →" 활성). + +--- + +## S2 — 단원 선택 — `/app/new/topic` + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ◆ OpenMath [ 문제 생성하기 → ] │ +├────────────────────────────────────────────────────────────────────────┤ +│ ← 학년 선택 · 중1 (2/4) │ sub-nav +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 어느 단원인가요? │ heading-xl +│ │ +│ (전체) [수와 연산] (문자와 식) (함수) (기하) (확률·통계) │ filter-chip row +│ ↑ ↑ active = bg ink, text on-ink │ ([…] = active) +│ │ +│ ┌──────────────────────────────┐ ┌──────────────────────────────┐ │ +│ │ ● 일차방정식의 풀이 │ │ ○ 일차부등식 │ │ +│ │ [9수04-12] │ │ [9수04-13] │ │ intent-radio- +│ │ 일차식의 양변에 같은 수를… │ │ 일차부등식의 해를 부등식의…│ │ card grid +│ └━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┘ └──────────────────────────────┘ │ (2-up) +│ ┌──────────────────────────────┐ ┌──────────────────────────────┐ │ +│ │ ○ 연립일차방정식 │ │ ○ 일차함수와 그래프 │ │ +│ │ [9수04-14] │ │ [9수05-01] │ │ +│ │ 미지수가 두 개인 일차… │ │ 변화하는 두 양 사이의… │ │ +│ └──────────────────────────────┘ └──────────────────────────────┘ │ +│ ┌──────────────────────────────┐ ┌──────────────────────────────┐ │ +│ │ ○ 부등식의 활용 │ │ ○ 다항식의 사칙 연산 │ │ +│ │ [9수04-15] │ │ [9수03-01] │ │ +│ └──────────────────────────────┘ └──────────────────────────────┘ │ +│ │ +├────────────────────────────────────────────────────────────────────────┤ +│ [ ← 뒤로 ] [ 다음 → ] │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +**Empty 상태:** filter 결과 0건 시 grid 영역에 "해당 학년·영역에 등록된 단원이 없습니다" `{colors.ink-3}` 한 줄. + +--- + +## S3 — 동형 모드 + 평가 차원 — `/app/new/intent` + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ◆ OpenMath [ 문제 생성하기 → ] │ +├────────────────────────────────────────────────────────────────────────┤ +│ ← 단원 선택 · 일차방정식의 풀이 (3/4) 의도 확인 │ sub-nav +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 어떻게 출제할까요? │ heading-xl +│ │ +│ ── 어떤 동형으로 생성할까요? ───────────────────────────── │ heading-md +│ │ +│ ┌──────────────────────────────┐ ┌──────────────────────────────┐ │ +│ │ ● 구조 동형 [ ✓ 구조 ] │ │ ○ 개념 동형 [ ✦ 개념 ] │ │ intent-radio- +│ │ 숫자·계수만 바꿔서 │ │ 풀이 경로는 달라도 │ │ card × 2 +│ │ 같은 풀이 경로 │ │ 같은 학습 목표·평가 차원 │ │ +│ └━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┘ └──────────────────────────────┘ │ +│ │ +│ ── 보존해야 하는 능력은? ──────────────────────────────── │ heading-md +│ │ +│ ┃ ⚠ 이 차원을 보존해야 동형으로 인정됩니다. │ inline-notice- +│ │ warn (left 3px +│ ┌──────────────────────────────────────────────────────────────┐ │ warn-deep) +│ │ ☑ [A] 두 조건식에서 a₁과 d 를 연립으로 구함 │ │ +│ ├──────────────────────────────────────────────────────────────┤ │ intent-checkbox +│ │ ☑ [B] 일차방정식의 해를 등식의 성질로 도출 │ │ × N +│ ├──────────────────────────────────────────────────────────────┤ │ +│ │ ☐ [C] 등식의 양변에 같은 수를 더하는 변형 직접 사용 │ │ +│ ├──────────────────────────────────────────────────────────────┤ │ +│ │ ☐ [D] 해를 원식에 대입하여 검증하는 과정 포함 │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ +├────────────────────────────────────────────────────────────────────────┤ +│ [ ← 뒤로 ] [ 생성하기 → ] │ action-bar +└────────────────────────────────────────────────────────────────────────┘ +``` + +**활성 조건:** Section A radio 1개 + Section B checkbox 1개 이상 → "생성하기 →" 활성. 둘 중 하나라도 미충족이면 disabled + `aria-describedby` 사유 노출. + +--- + +## S4 — 검증 진행 — `/app/new/verify` + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ◆ OpenMath │ primary-nav +├────────────────────────────────────────────────────────────────────────┤ +│ ← 의도 확인 (4/4) 검증 진행 │ sub-nav +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 검증하고 있습니다 │ heading-xl +│ 생성과 검증을 6단계로 진행합니다. 보통 5~30초 걸려요. │ body-lg ink-3 +│ │ +│ ┌────────────────────────────────────────────────────────────────┐ │ +│ │ 1/6 RAG 검색 ✓ 12개 참조 발견 [pass] │ │ step-progress- +│ ├────────────────────────────────────────────────────────────────┤ │ row × 6 +│ │ 2/6 의도 추출 ✓ 학습 목표 · 핵심 제약 [pass] │ │ +│ ├────────────────────────────────────────────────────────────────┤ │ +│ ┃ 3/6 문제 생성 ⠋ 진행 중... [active] │ │ ← border-left +│ ├────────────────────────────────────────────────────────────────┤ │ 3px ink +│ │ 4/6 산술 검증 (SymPy) · [pending] │ │ +│ ├────────────────────────────────────────────────────────────────┤ │ +│ │ 5/6 독립 재풀이 · [pending] │ │ +│ ├────────────────────────────────────────────────────────────────┤ │ +│ │ 6/6 학습 목표 매핑 · [pending] │ │ +│ └────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌────────────────────────────────────────────────────────────────┐ │ +│ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ │ +│ │▒ ▒│ │ formula-stage +│ │▒ 3x + 5 = 14 ▒│ │ (3/6 이후 +│ │▒ ▒│ │ 미리보기) +│ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ │ +│ └────────────────────────────────────────────────────────────────┘ │ +│ │ +├────────────────────────────────────────────────────────────────────────┤ +│ [ 취소 ] │ action-bar +└────────────────────────────────────────────────────────────────────────┘ (primary 비노출) +``` + +### S4 — 에러 상태 (SSE 끊김) + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ← 의도 확인 (4/4) 검증 진행 │ +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┃ ✗ 연결이 끊겼습니다 — 다시 시도해 주세요. │ inline-notice- +│ │ fail (left 3px +│ ┌────────────────────────────────────────────────────────────────┐ │ fail-deep) +│ │ 1/6 RAG 검색 ✓ 12개 참조 발견 [pass] │ │ +│ ├────────────────────────────────────────────────────────────────┤ │ +│ │ 2/6 의도 추출 ✗ 연결 끊김 (재시도 필요) [fail] │ │ +│ ├────────────────────────────────────────────────────────────────┤ │ +│ │ 3/6 문제 생성 · [pending] │ │ +│ │ ... │ │ +│ └────────────────────────────────────────────────────────────────┘ │ +│ │ +├────────────────────────────────────────────────────────────────────────┤ +│ [ 의도 화면으로 ] [ 다시 시작 ] │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## S5 — 결과 확인 + 수정 — `/app/new/result` + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ◆ OpenMath │ +├────────────────────────────────────────────────────────────────────────┤ +│ ← 검증 진행 / 결과 │ sub-nav +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 3개 문항이 준비되었습니다 │ heading-xl +│ │ +│ [전체] (구조동형) (개념동형) (주의) (실패) │ filter-chip +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┃─────────────────┐ │ +│ │ #1 [ ✓ 구조 ] │ │ #2 [ ✦ 개념 ] │ ┃ #3 [ ⚠ 주의 ] │ │ card-head +│ │ (★)(↻)(✎)(×)│ │ (★)(↻)(✎)(×)│ ┃ (★)(↻)(✎)(×)│ │ +│ ├─────────────────┤ ├─────────────────┤ ┃─────────────────┤ │ +│ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ │ +│ │▒ 3x + 5 = 14 ▒│ │▒ 4(x − 1) = 8 ▒│ ┃▒ 답 불일치 ▒│ │ formula-stage +│ │▒ ▒│ │▒ ▒│ ┃▒ 검토 필요 ▒│ │ +│ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ │ +│ ├─────────────────┤ ├─────────────────┤ ┃─────────────────┤ │ +│ │ 답: x = 3 │ │ 답: x = 3 │ ┃ 답: x = 5 │ │ meta (정답 +│ │ ▾ 풀이 보기 │ │ ▾ 풀이 보기 │ ┃ ▾ 풀이 보기 │ │ + disclosure) +│ └─────────────────┘ └─────────────────┘ ┃─────────────────┘ │ +│ pass card concept card ┃ result-card-failed │ +│ ↑ left-border 4px fail │ +│ │ +├────────────────────────────────────────────────────────────────────────┤ +│ [ 다시 검증 ] [ PDF 만들기 → ] │ action-bar +│ (button-secondary) (button-ink, 강조) │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +### S5 — All-failed 상태 (0/3 통과) + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ← 검증 진행 / 결과 │ +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ⚠ 통과한 문항이 없습니다. │ +│ 의도를 조정하거나 다시 검증해 보세요. │ +│ │ +│ ┃─────────────────┐ ┃─────────────────┐ ┃─────────────────┐ │ +│ ┃ #1 [ ✗ 실패 ] │ ┃ #2 [ ✗ 실패 ] │ ┃ #3 [ ✗ 실패 ] │ │ all result- +│ ┃ │ ┃ │ ┃ │ │ card-failed +│ ... │ +│ │ +├────────────────────────────────────────────────────────────────────────┤ +│ [ 의도로 ] [ 다시 검증 ] [ PDF 만들기 → ] (disabled)│ PDF 차단 +└────────────────────────────────────────────────────────────────────────┘ +``` + +### S5 — 수정 모드 (한 카드) + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ #1 [ ✓ 구조 ] (★)(↻)(✎ active)(×) │ │ +│ ├─────────────────────────────────────────────────────────────┤ │ +│ │ ┌───────────────────────┐ ┌──────────────────────────────┐ │ │ +│ │ │ LaTeX 입력 │ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ │ │ inline editor +│ │ │ ┌─────────────────┐ │ │▒ ▒│ │ │ (textarea + +│ │ │ │ 3x + 5 = 14 │ │ │▒ 3x + 5 = 14 ▒│ │ │ KaTeX preview) +│ │ │ │ | │ │ │▒ ▒│ │ │ +│ │ │ │ │ │ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ │ │ +│ │ │ └─────────────────┘ │ └──────────────────────────────┘ │ │ +│ │ │ [취소] [저장 + 재검증 (4/6·5/6)] │ │ +│ │ └────────────────────────────────────────────────────────────│ │ +│ └────────────────────────────────────────────────────────────────┘ │ +│ │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## S6 — PDF 출력 — `/app/new/export` + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ◆ OpenMath │ +├────────────────────────────────────────────────────────────────────────┤ +│ ← 결과 / PDF 출력 │ sub-nav +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 시험지 미리보기 │ heading-xl +│ │ +│ ┌─────────────────────────────┐ ┌────────────────────────────────┐ │ +│ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ │ ▾ 제목 │ │ pdf-preview- +│ │▒ 2026 1학기 중1 수학 ▒│ │ [ 중1 일차방정식 보강 ] │ │ thumbnail (좌 +│ │▒ ───────────────────── ▒│ │ ▾ 날짜 │ │ 60%, A4 +│ │▒ 이름: 반: __ 번호:__ ▒│ │ [ 2026-05-22 ] │ │ 1:1.41) +│ │▒ ───────────────────── ▒│ │ ▾ 학년·반 │ │ +│ │▒ ▒│ │ [ 중1 / 3반 ] │ │ disclosure-row +│ │▒ 1. 3x + 5 = 14 의 해는? ▒│ │ ▾ 정답표 포함 │ │ × N (우 40%) +│ │▒ ① 1 ② 2 ③ 3 ④ 4 ⑤ 5 ▒│ │ ☑ 별지로 포함 │ │ +│ │▒ ▒│ │ ▾ 문항 순서 │ │ +│ │▒ 2. 4(x − 1) = 8 … ▒│ │ ◉ 그대로 ○ 셔플 │ │ +│ │▒ ▒│ │ ▾ 폰트 크기 │ │ +│ │▒ 3. … ▒│ │ ◉ 보통 ○ 큼 │ │ +│ │▒ ▒│ │ │ │ +│ │▒ ▒│ │ │ │ +│ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ │ │ │ +│ └─────────────────────────────┘ └────────────────────────────────┘ │ +│ (좌 60%) (우 40%) │ +│ │ +├────────────────────────────────────────────────────────────────────────┤ +│ [ 결과로 ] [ PDF 다운로드 ↓ ] │ action-bar +└────────────────────────────────────────────────────────────────────────┘ +``` + +### S6 — 다운로드 직후 + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ ← 결과 / PDF 출력 │ +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┃ ✓ PDF 가 다운로드되었습니다. (시험지_중1_2026-05-22.pdf) │ inline-notice- +│ │ pass (left 3px +│ 시험지 미리보기 │ pass-deep) +│ ... │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 부록 A — Mobile 변형 (320~599px) + +좁은 viewport 에서 핵심 변경은 (1) primary-nav 의 nav-links 숨김, (2) entry / result grid 1-up, +(3) book-stack 92vw, (4) action-bar fixed bottom. + +### Landing — mobile + +``` +┌──────────────────────────┐ +│ ◆ OpenMath [ → ] │ primary-nav (links 숨김, +├──────────────────────────┤ brand + CTA 만) +│░░░░░░░░░░░░░░░░░░░░░░░░░░│ +│░ ▢ 검증된 문제 한 세트 ░│ +│░ ░│ +│░ 수학 문제는 ░│ +│░ 그럴듯해서는 ░│ display-hero +│░ 안 된다. ░│ (clamp → 48px) +│░ 맞아야 한다. ░│ +│░ ░│ +│░ ┌──┐┌──┐┌──┐┌──┐ ░│ +│░ │ ││ ││ ││ │ ░│ book-stack 92vw +│░ └──┘└──┘└──┘└──┘ ░│ +│░ ∑ⁿₖ₌₁ k² ░│ floating-token +│░ ░│ (3개로 축소) +│░ [ 무료로 시작하기 ] ░│ +│░ [ 샘플 문제 보기 ] ░│ +│░░░░░░░░░░░░░░░░░░░░░░░░░░│ +├──────────────────────────┤ +│ 01 │ +│ 검증된 답 │ feature-cell +│ SymPy 가 답을 직접 증명 │ 1-up (border- +├──────────────────────────┤ right → +│ 02 │ border-bottom) +│ 두 가지 동형 │ +│ 구조·개념을 분리해 선택 │ +├──────────────────────────┤ +│ 03 │ +│ A4 PDF 출력 │ +│ 학원 시험지 그대로 │ +├──────────────────────────┤ +│ © OpenMath │ +│ build alpha-0.1 │ footline 2-col +└──────────────────────────┘ +``` + +### S1 — mobile + +``` +┌──────────────────────────┐ +│ ◆ OpenMath [ → ] │ +├──────────────────────────┤ +│ ← 새 문제 만들기 (1/4) │ sub-nav (압축, +├──────────────────────────┤ breadcrumb 축약) +│ │ +│ 어느 학년인가요? │ +│ │ +│ ┌──────────────────────┐ │ +│ │ ● 중1 │ │ intent-radio- +│ └━━━━━━━━━━━━━━━━━━━━━━┘ │ card 1-up +│ ┌──────────────────────┐ │ +│ │ ○ 중2 │ │ +│ └──────────────────────┘ │ +│ ┌──────────────────────┐ │ +│ │ ○ 중3 │ │ +│ └──────────────────────┘ │ +│ │ +│ │ +│ ──────────────────── │ +│ ↓ fixed bottom action │ +├──────────────────────────┤ +│ [ 취소 ] [ 다음 → ] │ action-bar +└──────────────────────────┘ (fixed bottom) +``` + +### S4 — mobile + +``` +┌──────────────────────────┐ +│ ← 의도 확인 (4/4) │ +├──────────────────────────┤ +│ 검증하고 있습니다 │ +│ 5~30초 걸려요. │ +│ │ +│ ┌──────────────────────┐ │ +│ │ 1/6 RAG 검색 │ │ +│ │ ✓ 12개 참조 │ │ step-progress- +│ ├──────────────────────┤ │ row 가 +│ │ 2/6 의도 추출 │ │ 그대로 1-up +│ │ ✓ 핵심 제약 │ │ +│ ├──────────────────────┤ │ (텍스트가 줄 +│ ┃ 3/6 문제 생성 │ │ 바꿈 처리, +│ ┃ ⠋ 진행 중... │ │ keep-all) +│ ├──────────────────────┤ │ +│ │ 4/6 산술 검증 │ │ +│ │ · │ │ +│ ├──────────────────────┤ │ +│ │ 5/6 독립 재풀이 │ │ +│ ├──────────────────────┤ │ +│ │ 6/6 학습 목표 매핑 │ │ +│ └──────────────────────┘ │ +│ │ +│ ┌──────────────────────┐ │ +│ │▒ 3x + 5 = 14 ▒│ │ formula-stage +│ └──────────────────────┘ │ (3/6 이후) +│ │ +├──────────────────────────┤ +│ [ 취소 ] │ +└──────────────────────────┘ +``` + +### S5 — mobile + +``` +┌──────────────────────────┐ +│ ← 결과 │ +├──────────────────────────┤ +│ 3개 문항이 준비되었어요 │ +│ │ +│ [전체](구조)(개념)(주의) │ filter-chip +│ ↑ 가로 스크롤 │ (가로 scroll) +│ │ +│ ┌──────────────────────┐ │ +│ │ #1 [ ✓ 구조 ] │ │ +│ │ (★)(↻)(✎)(×) │ │ result-card +│ ├──────────────────────┤ │ 1-up +│ │▒ 3x + 5 = 14 ▒│ │ +│ ├──────────────────────┤ │ +│ │ 답: x = 3 │ │ +│ │ ▾ 풀이 보기 │ │ +│ └──────────────────────┘ │ +│ ┌──────────────────────┐ │ +│ │ #2 [ ✦ 개념 ] │ │ +│ │ ... │ │ +│ └──────────────────────┘ │ +│ │ +├──────────────────────────┤ +│ [ 다시 ] [ PDF → ] │ action-bar +└──────────────────────────┘ (fixed bottom) +``` + +--- + +## 부록 B — 컴포넌트 분해 참조 + +각 mockup 의 영역이 `DESIGN.md` 의 어떤 component entry 에 대응하는지를 한 표로: + +| Mockup 영역 | DESIGN.md 컴포넌트 | Surface | +|---|---|---| +| 상단 검정 띠 (S0) | `hero-tile-productivity` | productivity | +| 워밍 아이보리 hero (Landing) | `eyebrow` + `display-hero` + `book-cover` + `floating-token` + `hint-pill` | editorial | +| 카드 진입 (S0) | `job-entry-card` (× 2) | productivity | +| 학년·단원·모드 카드 (S1·S2·S3-A) | `intent-radio-card` | productivity | +| 체크 항목 (S3-B) | `intent-checkbox` | productivity | +| 검증 6단계 행 (S4) | `step-progress-row` + `spinner-dot` | productivity | +| 결과 카드 본문 수식 (S4·S5·S6) | `formula-stage` (`{colors.soft-cloud}`) | productivity | +| 결과 카드 (S5) | `result-card` / `result-card-failed` + `badge-*` + `button-icon-circular` | productivity | +| 시험지 미리보기 (S6) | `pdf-preview-thumbnail` (A4 1:1.41) | productivity | +| 옵션 행 (S6) | `disclosure-row` | productivity | +| 상단 메뉴 | `utility-bar` + `primary-nav` (brand-mark + nav-links + button-primary) | 공유 | +| 페이지 단위 breadcrumb | `sub-nav` (← + 카운터) | productivity | +| 하단 액션 | `action-bar-sticky` (좌 secondary / 우 primary 또는 ink) | productivity | +| 인라인 알림 | `inline-notice-pass` / `inline-notice-fail` / `inline-notice-warn` | productivity | +| 필터 칩 | `filter-chip` / `filter-chip-active` | productivity | +| 페이지 하단 | `footer` (4-col) + `footline` | 공유 | + +--- + +## 부록 C — Mockup 의 한계 + +ASCII 가 표현하지 못하는 것: + +1. **색 의미** — `{colors.pass}` green / `{colors.fail}` coral / `{colors.concept}` blue / `{colors.warn}` amber 의 차이가 본 문서에는 보이지 않는다. `[ ✓ 구조 ]` `[ ✦ 개념 ]` `[ ⚠ 주의 ]` `[ ✗ 실패 ]` 의 *모양* 만 보임. 시각 결과는 `DESIGN.md` Colors §"Verification semantics" 와 함께 본다. +2. **세리프 vs sans 대비** — Landing 의 Fraunces 96~104px 디스플레이와 본문 Inter 의 시각 격차. ASCII 에서는 모두 동일 monospace. +3. **드롭 섀도우·glow** — Landing 의 책 표지 `0 18px 36px rgba(0,0,0,0.20)` 와 brand-mark `0 4px 14px rgba(59,130,246,0.3)` 의 깊이. ASCII 에서는 같은 1px 박스. +4. **간격 리듬** — `{spacing.section}` (48px) / `{spacing.xxl}` (32px) / `{spacing.lg}` (16px) 의 차이. ASCII 의 줄바꿈은 등간격이라 실제 비례가 어긋남. +5. **Fluid type** — display-hero `clamp(48,8vw,104)px` 의 viewport-비례 스케일. ASCII 는 고정 폭. +6. **Motion** — book flip / parallax / spinner 회전 / step row state 전환. 정적 그림으로 표현 불가. +7. **수식 렌더링** — KaTeX 출력의 글리프 비율·indent. 본 문서는 ASCII 근사 (`3x + 5 = 14`, `∑ⁿₖ₌₁ k²`) 만 사용. + +따라서 본 문서는 spec 의 *대체* 가 아니라 *진입로* 다. 컴포넌트를 빌드할 때는 SCREENS.md 의 상태 표 + DESIGN.md 의 토큰 정의를 함께 본다. + +--- diff --git a/docs/product/SCREENS.md b/docs/product/SCREENS.md new file mode 100644 index 0000000..ba9d8e7 --- /dev/null +++ b/docs/product/SCREENS.md @@ -0,0 +1,555 @@ +# Product Spec — Screens + +| | | +|---|---| +| Status | Draft | +| Last updated | 2026-05-20 | +| Supersedes | — | +| Depends on | `docs/product/USER_FLOW.md` (Draft) · `packages/web/DESIGN.md` (alpha) · `docs/specs/domain.md` (Draft) | + +이 문서는 OpenMath 의 **개별 화면(screen)** 을 정의한다. +"이 화면은 어떤 컴포넌트로 구성되고, 어떤 상태를 가지며, 어떤 데이터를 받고/내며, +키보드·스크린리더 사용자에게 어떻게 노출되는가." +화면 간 흐름·분기는 본 문서가 아닌 [USER_FLOW.md](./USER_FLOW.md) 에서 다룬다. +컴포넌트의 시각 토큰(색·간격·타이포)은 [DESIGN.md](../../packages/web/DESIGN.md) 에서 다룬다. + +각 화면은 다음 4개 섹션으로 명세: +- **화면 구성요소** — 위에서 아래·왼쪽에서 오른쪽 순서. `DESIGN.md` 컴포넌트 토큰 참조. +- **상태 목록** — 화면 전체 또는 핵심 컴포넌트가 가질 수 있는 모든 visual/logical state. +- **데이터** — 화면이 *받는* 입력 (URL param·세션·서버) 과 *남기는* 출력 (도메인 객체·URL 전이). +- **접근성** — 키보드 조작, 스크린리더 노출, motion·contrast 고려. + +공통 접근성 베이스라인 (모든 화면 적용, 화면별 섹션은 *추가* 사항만 기재): +- 모든 인터랙티브 요소 `tabindex` 진입 가능, focus indicator 가시. +- 한국어 본문 `lang="ko"`, `word-break: keep-all; overflow-wrap: break-word;`. +- 검증 시그널은 색 + 아이콘 + 텍스트 3중 표기 (색맹 사용자 대응, `DESIGN.md` Don'ts §"검증 시그널 색을 분위기용으로 사용 금지" 와 짝). +- 터치 타깃 WCAG AAA 44×44px (`DESIGN.md` Responsive §"Touch Targets"). +- `prefers-reduced-motion: reduce` 시 모든 transform·parallax·spinner 회전 무효화. +- 본문 대비 WCAG AA 이상 (`{colors.ink}` on `{colors.canvas-pure}` ≈ 16:1, `{colors.ink-3}` on `{colors.canvas-pure}` ≈ 5.1:1). + +--- + +## Landing — 랜딩 + +**URL:** `/` +**Surface:** Editorial (`{colors.canvas}` 워밍 아이보리) + +### 화면 구성요소 + +| 영역 | 컴포넌트 | 비고 | +|---|---|---| +| Top utility | `{component.utility-bar}` | 우측 "도움말 · 피드백" 두 링크만 (MVP) | +| Primary nav | `{component.primary-nav}` | `{component.brand-mark}` + wordmark + nav-links 4개 + `{component.button-primary}` "문제 생성하기 →" | +| Hero — eyebrow | `{component.eyebrow}` | "검증된 문제 한 세트" — `{typography.caption-sm}` Mono | +| Hero — headline | `{typography.display-hero}` | Fraunces clamp(48,8vw,104)px — 1 페이지 1회 | +| Hero — meta row | `{component.meta-pill}` × N | "· 회원가입 X" / "· 중1–중3 전 단원" | +| Hero — CTA cluster | `{component.button-primary}` "무료로 시작하기" + `{component.button-ghost}` "샘플 문제 보기" | 같은 fold | +| Hero — book stack | `{component.book-cover}` × 4 | 380×500, perspective 1800px / rotateX 28° | +| Hero — floating tokens | `{component.floating-token}` × 6 (desktop) / 3 (mobile) | 수식·식별자, parallax | +| Hero — hint | `{component.hint-pill}` | "마우스를 올리면 표지가 넘어갑니다" — mouse-in 시 fade | +| Feature strip | `{component.feature-cell}` × 3 | num caption → heading-xl → body-sm | +| Footline | `{component.footline}` | copyright · build version | + +**Effect 허용 (editorial 한정):** backdrop-blur, radial-gradient wash (`{colors.primary-50}` 우상단 + `{colors.pass-50}` 좌하단), 64×64 grid pattern overlay (4% opacity, radial mask fade), perspective transform, drop-shadow (책 표지 + brand-mark glow 단 2곳). + +### 상태 목록 + +| 상태 | 트리거 | 표현 | +|---|---|---| +| Default | 페이지 로드 | 책 4권 닫힌 상태, floating-token 정지 | +| Hover-stage | 마우스가 book-stack 영역 진입 | `hint-pill` opacity → 0, 가장 가까운 book-cover 가 168° flip | +| Parallax-active | 마우스 이동 | floating-token data-depth 별 translate (-40px ~ -24px) | +| Reduced-motion | `prefers-reduced-motion: reduce` | 책 flip / parallax 모두 비활성, 정적 표시 | +| Mobile (<600px) | viewport | display-hero clamp → 48px, primary-nav links 숨김, book-stack 92vw, floating-token 3개로 축소 | + +`{component.button-primary}` 자체의 상태는 default / `button-primary-active` (pressed: bg `{colors.primary-deep}` + `translateY(-1px)`) 두 가지. + +### 데이터 + +| 항목 | 방향 | 값 | +|---|---|---| +| 입력 | — | 없음 (정적 페이지) | +| 클라이언트 상태 | — | 없음 (세션 미생성) | +| 출력 (CTA) | 라우팅 | `/app` 으로 전이 | +| 출력 (nav-links) | 라우팅 | 외부 문서 페이지 (MVP 단계 placeholder 가능) | + +### 접근성 + +- **키보드:** `Tab` 순서 — utility links → primary-nav links → `button-primary` (nav) → eyebrow (skip) → hero CTA cluster → feature-cell links → footline. `Enter` 로 CTA 활성. +- **스크린리더:** + - `display-hero` 는 `

` 1개로 한정. feature-cell 의 num caption ("01", "02") 은 `aria-hidden="true"` (장식). + - `book-cover` × 4 는 `role="img"` + `aria-label` "중1 인수분해 · 검증 12문항" 형식의 의미적 라벨. 책 표지 안 문구를 그대로 읽지 않게. + - `floating-token` 은 `aria-hidden="true"` (장식 — 수식이 의미 운반 아님). + - `hint-pill` 은 `aria-hidden="true"` 가 아니라 정보 — "마우스 사용자 안내" 의미는 마우스가 없는 사용자에게 노출되지 않아야 하므로 `