# Prism — M3 웹앱 실행 명세 (Layer 3) | 버전 | 수정일 | 수정자 | 주요 변경 내용 | 비고 | | :--- | :--- | :--- | :--- | :--- | | v0.1.0 | 2026-07-29 | HMCM | 신규 제정 — 웹앱(HTML·대시보드·폼·문서 사이트) 실행 명세. CSS 변수 정본 내보내기 수록, 구조 검증 12항 통과 | `active` 등록 (modules §4.4) | | 메타 | 값 | | :--- | :--- | | 모듈 | **M3 `webapp`** — 웹앱 | | 계층 | Layer 3 (실행 명세 · **자기완결형**) | | 대상 산출물 | HTML/CSS/JS 웹앱 — 대시보드·관리도구·폼·문서 사이트·웹 보고서 | | 상위 의존 | `Design.md` 0.1.1 · foundations 0.1.2 · medium.screen 0.1.1 · components 0.1.1 · tokens.json 0.1.1 | | 접근성 목표 | **WCAG 2.2 AA** (CI 게이트: axe·pa11y·stylelint) | ```yaml module: id: webapp name: 웹앱 version: 0.1.0 status: active brand: saman medium: screen medium_variant: screen.web dual_medium: false format: responsive # 브레이크포인트 5단계 columns: 12 # lg·xl 기준 (BP별 §2) type_set: compact component_count: 20 variant_count: 35 patterns: [w1-shell, w2-dashboard, w3-form, w4-document, w5-list-detail] depends_on: { foundations: 0.1.2, medium.screen: 0.1.1, components: 0.1.1 } ``` --- ## 1. 적용 조건 | 적용 | 제외 | | :--- | :--- | | 웹앱·대시보드·관리도구·폼 | 모바일·태블릿 네이티브 앱 → M4 (`mobile/`) | | 문서·가이드 사이트, 웹 보고서 | 발표 슬라이드 → M2 (`ppt/`) | | 화면 열람용 HTML 산출물 | 인쇄 배포물 → M5 (`A3/`) — 인쇄 스타일시트는 §9 참조 | --- ## 2. 브레이크포인트·그리드 (정본 값) | BP | 범위 (px) | 컬럼 | 마진 | 거터 | 기준 컬럼 폭 | 정합 | | :--- | :--- | :-: | ---: | ---: | ---: | :--- | | `xs` | 0–599 | 4 | 16 | 16 | 70 | 4×70+3×16=328 ✓ | | `sm` | 600–903 | 8 | 24 | 16 | 55 | 8×55+7×16=552 ✓ | | `md` | 904–1239 | 8 | 32 | 24 | 84 | 8×84+7×24=840 ✓ | | `lg` | 1240–1599 | 12 | 32 | 24 | 76 | 12×76+11×24=1176 ✓ | | `xl` | 1600– | 12 | 24 | 24 | 94 (**컨테이너 1440 고정**) | 12×94+11×24=1392 ✓ | | ID | 규칙 | | :--- | :--- | | **WA-1** | 미디어 쿼리는 min-width(mobile-first)·px 단위. 임의 브레이크포인트 금지 (BP-1·BP-3) | | **WA-2** | `xl` 컨테이너 1440px 고정 — 무한 확장은 measure를 파괴한다 (BP-2) | | **WA-3** | **연속 본문 `max-width: 38em` 강제** (MS-1). 컨테이너 폭 ≠ 본문 폭. md 6col=624px(39.0em)·lg 8col=776px(48.5em)·xl 8col=920px(57.5em) 전부 38em 초과 — 본문 블록에는 반드시 max-width 적용 | | **WA-4** | 최상위 레이아웃 영역만 그리드 정렬. 내부는 스페이싱 토큰 (GD-1~3) | --- ## 3. CSS 변수 정본 (`:root` — 이 블록이 곧 토큰 정의부다) > 산출물 CSS에는 아래 변수만 사용한다. **변수 정의부 외 HEX·px 리터럴 0건**이 빌드 게이트다 (W1·W2). ```css :root { /* ── color (light) ─────────────────────────────── */ --prism-bg-canvas: #F4F3F0; /* sand G4 — 순백 전면 금지 */ --prism-bg-card: #FFFFFF; /* white G0 */ --prism-text-body: #474235; /* brown G72 — on sand 9.02 AAA */ --prism-text-heading: #1F524B; /* green G69 — on sand 8.00 AAA */ --prism-text-subtle: #474235; /* 저강조는 색이 아니라 크기·웨이트로 */ --prism-text-accent: #D05C26; /* orange G47 — 대형 전용(24px+/19px+Bold) */ --prism-border-default: #474235; --prism-border-strong: #1F524B; --prism-status-do: #005A4E; --prism-status-do-bg: #DCEAE6; --prism-status-dont: #8F2A11; --prism-status-dont-bg: #F6DFDA; --prism-status-caution: #6B4A00; --prism-status-caution-bg: #F5E7CC; --prism-status-tip: #0A4E86; --prism-status-tip-bg: #DDE8F3; --prism-dv-1: #C08A2E; --prism-dv-2: #6E8F3C; --prism-dv-3: #3C6E9F; --prism-dv-4: #8C3B14; --prism-dv-5: #1B4A43; --prism-dv-6: #5E4370; /* ── space (base 8px = 0.5rem) ──────────────────── */ --prism-space-025: 0.125rem; /* 2px */ --prism-space-050: 0.25rem; /* 4px */ --prism-space-100: 0.5rem; /* 8px */ --prism-space-150: 0.75rem; /* 12px */ --prism-space-200: 1rem; /* 16px — card padding */ --prism-space-300: 1.5rem; /* 24px — block gap */ --prism-space-400: 2rem; /* 32px — zone gap */ --prism-space-600: 3rem; /* 48px */ --prism-space-800: 4rem; /* 64px */ /* ── type (root 16px 미고정 — html font-size 선언 금지) ── */ --prism-type-caption: 0.8125rem; --prism-lh-caption: 1.55; --prism-type-body-sm: 0.875rem; --prism-lh-body: 1.70; --prism-type-body: 1rem; --prism-type-body-lg: 1.125rem; --prism-type-title-sm: 1.25rem; --prism-lh-title: 1.50; --prism-type-title: 1.625rem; --prism-type-title-lg: 2rem; --prism-type-display: 2.875rem; --prism-lh-display: 1.25; --prism-type-display-lg: 4.125rem; /* ── radius · stroke · z · motion ───────────────── */ --prism-radius-sm: 4px; --prism-radius-md: 8px; --prism-radius-lg: 12px; --prism-radius-pill: 9999px; --prism-stroke-hairline: 1px; --prism-stroke-regular: 2px; --prism-stroke-bold: 3px; --prism-z-raised: 100; --prism-z-sticky: 200; --prism-z-dropdown: 300; --prism-z-scrim: 400; --prism-z-modal: 500; --prism-z-popover: 600; --prism-z-toast: 700; --prism-z-tooltip: 800; --prism-scrim: rgba(0, 0, 0, 0.32); --prism-motion-fast: 100ms; --prism-motion-moderate: 200ms; --prism-motion-slow: 300ms; --prism-ease-standard: cubic-bezier(0.2, 0, 0, 1); --prism-ease-enter: cubic-bezier(0, 0, 0, 1); --prism-ease-exit: cubic-bezier(0.3, 0, 1, 1); --prism-focus-ring: 3px; --prism-focus-offset: 2px; --prism-measure-max: 38em; } /* ── dark theme (data-theme + OS 연동) ─────────────── */ :root[data-theme="dark"] { --prism-bg-canvas: #111111; /* obsidian G95 */ --prism-bg-card: #111111; /* 카드는 보더로 분리 */ --prism-text-body: #F4F3F0; /* sand on obsidian 17.02 AAA */ --prism-text-heading: #F4F3F0; /* ⚠ green 텍스트 금지 (2.13:1) — 면·도형만 */ --prism-text-subtle: #D3D3D3; /* subtle 12.61 AAA */ --prism-text-accent: #D05C26; /* 4.71 AA — 대형 권장 */ --prism-border-default: #D3D3D3; --prism-border-strong: #F4F3F0; /* 상태색 다크: container↔on 반전 (tokens v0.1.1, 실측 동일) */ --prism-status-do: #DCEAE6; --prism-status-do-bg: #005A4E; --prism-status-dont: #F6DFDA; --prism-status-dont-bg: #8F2A11; --prism-status-caution: #F5E7CC; --prism-status-caution-bg: #6B4A00; --prism-status-tip: #DDE8F3; --prism-status-tip-bg: #0A4E86; } @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { /* 위 dark 값셋 동일 적용 */ } } ``` | ID | 규칙 | | :--- | :--- | | **CSS-1** | `html { font-size: }` 선언 금지 (UN-2) — 사용자 브라우저 설정 존중 | | **CSS-2** | 타입·간격은 rem, 보더·헤어라인은 px (UN-1·UN-3) | | **CSS-3** | 다크 테마는 `data-theme` 속성 + `prefers-color-scheme` 연동, 사용자 재정의 3택(light/dark/system) 영속 저장 (TC-1~TC-4) | | **CSS-4** | 다크에서 `--prism-text-heading`에 green을 재정의하지 않는다 — **실측 2.13:1 판독 불가** (TM-S3) | | **CSS-5** | 텍스트 색에 알파 금지. 알파는 스크림·오버레이·상태 레이어만 (AL-1·AL-2) | --- ## 4. 인터랙션·접근성 핵심 (요약 정본) | 항목 | 값 · 규칙 | | :--- | :--- | | 상태 표현 | **GRADE 시프트**: hover ±4 · active ±8 · selected ±12 (라이트 +, 다크 −). focus는 색 변화 없이 **링만** | | disabled | 불투명도 금지 — 저강조 토큰 사용, 형태 판별 가능 유지. 가능하면 활성 + 오류 메시지 우선 (DS-1~4) | | 포커스 링 | **3px 이중 링 + 오프셋 2px**, `:focus-visible`, `outline: none` 대체 없는 선언 금지 (FC-1·FC-2) | | 히트 영역 | **최소 44×44px** (권장 48), 인접 간격 ≥ 8px, `pointer: coarse`에서 48px (HT-1~3·PT-1) | | hover | 보강 수단일 뿐 — hover 없이 기능 완전, `@media (hover: hover)` 격리 (HV-3·HV-4) | | 대비 | 본문 ≥ 4.5:1 (AA) 필수 — 등록 페어는 전부 AAA로 여유 충족. 비텍스트 ≥ 3:1 | | 모션 | `transform`·`opacity`만 애니메이션, `prefers-reduced-motion` 필수 구현 (MT-3·RM-1) | | 시맨틱 | 네이티브 요소 우선, 헤딩 레벨 건너뜀 금지, 아이콘 버튼 접근 가능한 이름 (SM-1~5) | | Elevation | **보더 우선**, 그림자는 보조 (`raised` 0 1px 2px / `overlay` 0 4px 12px / `modal` 0 8px 24px + 스크림 32%). 다크에서는 표면 톤 상향으로 대체 | --- ## 5. 화면 패턴 (5종) | ID | 구조 | 구성 | 적용 | | :--- | :--- | :--- | :--- | | **W1 앱 셸** | sticky header + 콘텐츠 | header(메타 4슬롯: 화면ID·권한·갱신시각·환경) + main. lg 이상에서 좌측 내비 레일(`4:8`의 4col 또는 고정 240~280px) | 전 화면 공통 골격 | | **W2 대시보드** | 카드 그리드 | stat·chartframe·table 카드 배치. xs 1열 → sm·md 2열 → lg·xl 3~4열. 존당 stat ≤ 3 | 현황·지표 | | **W3 폼** | 1열 수직 | input 수직 나열 (**다열 폼 금지**), max-width 38em, 레이블 상단, primary 버튼 1개 | 입력·설정 | | **W4 문서** | 목차 + 본문 | md 이상 `4:8`(toc 레일 + 본문 38em), 미만 단일 흐름 + 상단 앵커 toc. 아코디언은 1순위 아님 (AD-1) | 가이드·규정 열람 | | **W5 목록-상세** | 분할 뷰 | lg 이상 `4:8`(목록+상세), 미만 스택 내비게이션. 행 hover는 보강만 | 데이터 관리 | 공통: 결론·핵심 지표는 viewport당 1개 존에 집약 (PD-1). 본문이 있는 패턴은 전부 WA-3(38em) 적용. --- ## 6. 컴포넌트 선별 (20종 / 변형 35종) `eyebrow`(2) · `header`(1) · `rule`(1) · `card`(4) · `table`(4) · `stat`(1) · `chartframe`(1) · `specsheet`(1) · `directive`(3) · `badge`(4) · `scope`(1) · `toc`(1) · `stepper`(1) · `xref`(1) · `mediaframe`(1) · **`button`(4)** · **`input`(1)** · **`modal`(1)** · **`toast`(1)** · **`disclosure`(1)** **미선별**: `hero`(랜딩 제작 시 조건부 추가), `footer`(screen 비적용 — 매트릭스 준수), `stagecard`·`connector`·`principle`(문서 패턴 W4에서 조건부 추가 가능 — 추가 시 카탈로그 규정 준수). 주요 사용 규정: `primary` 버튼 화면당 1개, 레이블 동사 시작 ≤ 8자 · input 레이블 플레이스홀더 대체 금지, 오류 3요소(무엇이·왜·어떻게) + 색·기호·텍스트 3중 · modal 포커스 트랩 + Esc + 중첩 금지 · toast ≥ 5초, 동시 1개, 중요 정보 토스트 단독 전달 금지 · table sticky 헤더 + 가로 스크롤 허용(MS-3 예외) · disclosure 인쇄 시 전체 전개. --- ## 7. 분량 예산 | 슬롯 | 권장 | 최대 | 근거 | | :--- | :-: | :-: | :--- | | 화면 제목 (title) | 12자 | 20자 | 헤더 1행 | | 카드 제목 / 본문 | 12 / 60자 | 20 / 120자 | 카탈로그 승계, 최소 카드 폭(xs 328px) 기준 | | 버튼 레이블 | 4자 | 8자 | "저장"·"제출" — 결과를 말한다 | | input 레이블 / 도움말 / 오류 | 8 / 30 / 40자 | 12 / 40 / 60자 | 1~2행 | | toast 메시지 | 20자 | 40자 | 1~2행, 5초 내 판독 | | 표 셀 | 20자 | 40자 | 초과 시 상세 화면 이관 | | 빈 상태(empty state) 안내 | 30자 | 60자 | 원인 + 다음 행동 | --- ## 8. HTML 골격 (정본 시작점) ```html 화면 제목 — 서비스명
``` ## 9. 인쇄 스타일시트 (`@media print`) 그림자 전부 제거 (EV-4) · 알파 평탄화 (AL-4) · disclosure 전체 전개 (AD-5) · 링크 뒤 목적지 명시 · 색상은 등록 페어 유지, 본문 검정 계열. 본격 인쇄물은 M5로 재조판한다. --- ## 10. 제작 절차 | 단계 | 작업 | 판정 | | :-: | :--- | :--- | | 1 | 화면 목록 작성, 각 화면에 패턴(W1~W5) 배정 | 매핑 완료 | | 2 | §3 CSS 변수 블록을 그대로 탑재 (수정 금지 — 개정은 상위 계층에) | 리터럴 0건 | | 3 | §8 골격에서 시작, 시맨틱 마크업 (SM-1~5) | 랜드마크·제목 | | 4 | 컴포넌트 구현 — §6 선별 + §4 인터랙션 규정 | 카탈로그 외 0건 | | 5 | 반응형 — §2 BP, 본문 38em, 히트 영역 44px | WA-1~4 | | 6 | 다크 테마 — §3 dark 블록 + 전 화면 다크 렌더 확인 | green 텍스트 0건 | | 7 | CI 게이트 — stylelint·axe·pa11y 연결 | 통과 없이 머지 금지 (QG-2) | | 8 | 검증 §11 | 전항 PASS | --- ## 11. 검증 체크리스트 ### 자동 (CI 게이트 — W1~W18 승계 핵심) | # | 항목 | 기준 | | :-: | :--- | :--- | | A1 | 색·치수 리터럴 | 변수 정의부 외 0건 | | A2 | root font-size 고정 | 0건 | | A3 | `outline: none` 대체 없는 선언 | 0건 | | A4 | z-index·양수 tabindex | 토큰 외 0건 / 양수 0건 | | A5 | 그리드 정합 | 전 BP 잔차 0 (§2) | | A6 | axe 색 대비·이름·alt | 위반 0건 | | A7 | 헤딩 레벨 건너뜀 | 0건 | | A8 | `prefers-reduced-motion` | 구현 존재 | | A9 | 히트 영역 | ≥ 44px (인라인 링크 제외) | | A10 | 본문 max-width | ≤ 38em | | A11 | 다크 페어 재계산 | 전 페어 통과, green 텍스트 0건 | | A12 | 텍스트 알파·hover 의존 | 0건 | ### 수동 | # | 항목 | | :-: | :--- | | V1 | 키보드 단독 전 기능 조작 + Tab 순서 = 시각 순서 | | V2 | 스크린리더 주요 흐름 1회 낭독 | | V3 | 200% 확대 시 손실·가로 스크롤 없음 | | V4 | 그레이스케일 판독 (색 단독 의존 검출) | | V5 | 터치 기기 실기 (hover 의존·히트 영역) | | V6 | 인쇄 스타일시트 (§9) 출력 확인 | ### 구조 검증 결과 (modules §8.1 — `active` 등록 근거) 12항 전항 **PASS** (2026-07-29, HMCM): 스키마 충족 · depends_on 기록 · 정량 명시(20종/35변형) · 완화 0건 · 전 슬롯 예산 · 패턴-콘텐츠 매핑 · 그리드 정합(624·920 정정 반영) · 카탈로그 외 0건 · 미사용 0건(조건부 선별 명시) · 역할 중복 0건 · 결론 존 규정 · 겸용 해당 없음 --- *본 모듈은 참조 사양이다 (DC-3). "접근 가능한 컴포넌트로 만들었다"가 접근 가능한 서비스를 보증하지 않는다 — 서비스 단위 검증은 별도로 수행한다 (AT-4).*