Skip to content

Latest commit

 

History

History
802 lines (667 loc) · 145 KB

DESIGN.md

File metadata and controls

802 lines (667 loc) · 145 KB
name 내Law남불
description 판사의 책상 위에서 종이와 서류로 사건을 읽고 판단하는, 차분하고 묵직한 종이 질감의 판결 체험 서비스
colors
navy-ink navy-accent navy-axis navy-mist navy-tint wood-axis bronze-ornament bronze-label bronze-pale paper-bright paper-soft paper-news paper-strong paper-track paper-line paper-line-strong white ink-text ink-muted focus-blue danger danger-line danger-tint caution caution-line caution-tint judge-me judge-ai judge-court factor-heavy factor-light desk-wood desk-felt desk-felt-edge desk-kraft desk-sheet desk-shadow coffee-bg coffee-ink
#101d39
#1f3563
#2f5592
#c9d1e3
#eef1f7
#a8652f
#ba854b
#86581f
#e6d2b5
#fdfcfa
#f7f3ee
#f4efe4
#f1ebe4
#ebe5dc
#e2dad0
#8b8174
#ffffff
#151a24
#5d5952
#2f6fdb
#b3261e
#efb8b3
#fcecea
#9a3d0a
#d4692a
#fff1e7
#61538b
#0b6762
#4f6526
#a03f35
#35704a
#a8652f
#1c2b4f
#14213f
#d6a468
#f7f3ee
#0f0a19
#ead8c0
#4a2c1c
typography
display headline title body label caption
fontFamily fontSize
Noto Serif KR, AppleMyungjo, Batang, serif
34px
fontFamily fontSize fontWeight letterSpacing
Noto Serif KR, AppleMyungjo, Batang, serif
28px
900
-0.02em
fontFamily fontSize
Pretendard, -apple-system, Apple SD Gothic Neo, Malgun Gothic, sans-serif
22px
fontFamily fontSize lineHeight
Pretendard, -apple-system, Apple SD Gothic Neo, Malgun Gothic, sans-serif
15px
1.75
fontFamily fontSize
Pretendard, -apple-system, Apple SD Gothic Neo, Malgun Gothic, sans-serif
13px
fontFamily fontSize
Pretendard, -apple-system, Apple SD Gothic Neo, Malgun Gothic, sans-serif
12px
rounded
xs sm md pad
3px
4px
6px
24px
components
button-primary button-primary-hover button-secondary button-secondary-hover nav-button nav-button-hover mock-tag
backgroundColor textColor rounded padding height
{colors.navy-accent}
{colors.white}
{rounded.sm}
10px 18px
44px
backgroundColor
{colors.navy-ink}
backgroundColor textColor rounded padding height
{colors.white}
{colors.ink-text}
{rounded.sm}
10px 18px
44px
backgroundColor
{colors.paper-soft}
backgroundColor textColor padding height
{colors.navy-ink}
{colors.navy-mist}
10px 12px
44px
textColor
{colors.white}
rounded padding
{rounded.xs}
0 5px

Design System: 내Law남불

FE-76. 새 디자인을 만드는 문서가 아니라 지금 코드(frontend/src)가 하는 일을 적은 문서다. 코드와 다른 곳은 맨 아래 "현재 코드와 차이"에 따로 모았다. 제품의 목적 · 원칙 · 접근성 목표는 PRODUCT.md, 호버 규칙의 결정 배경은 docs/decisions/FE-76.md에 있다. 범위는 프론트 전체(책상 · 신문 지면 · 판결 체험 무대 · 성향 테스트 종이 · 마이페이지 · 커뮤니티 · 관리자 · 로그인). 로그인의 펠트 패드는 책상 계열의 한 사례일 뿐 기준이 아니다.

Overview

Creative North Star: "판사의 책상"

화면은 모두 판사의 집무실에 놓인 물건이다. 메인은 책상 그 자체(그 아래는 집무실 바닥)이고, 신문 지면은 사건 목록, 서류철은 성향 테스트, 커피잔은 마이페이지, 노트북은 커뮤니티다. 판결 체험 무대와 로그인의 펠트 패드도 같은 책상에서 이어진다. 분위기는 차분하고 묵직한 종이 질감이다. 바탕은 크림 종이, 글자와 조작은 짙은 네이비, 장식은 브론즈 한 가지이고, 화면을 꾸미려고 색을 늘리지 않는다. 분위기는 색이 아니라 물건의 은유(종이 · 펠트 · 나무 · 크라프트)가 만든다. 새 시각 요소(표시 · 아이콘 · 장식)도 판사의 집무실에 실제로 있을 물건(책상 위의 도장 · 클립 · 서류 · 명패, 바닥의 의자 · 러그 · 탁자 · 문)으로만 만든다. 집무실 밖 은유는 쓰지 않는다(공개 카드 표시를 왕관으로 만들었다가 결재 도장으로 바꾼 사례). 메인 아래(서비스 소개)는 책상에서 카메라가 그대로 내려간 집무실 바닥이다(FE-87, 같은 탑뷰 · 원근 없음).

무거운 주제를 다루므로 귀엽게 꾸미지 않는다. 캐릭터(내로 · 남불 · 로 합의부)는 판결 체험 무대와 결과처럼 정해진 자리에만 나온다. 피해 · 폭력 장면은 그리지 않고, 화난 표정도 없다. 판결의 방향을 색이나 연출로 미리 알리지 않는다(세 판결은 대등하다). 모바일과 데스크톱을 같은 비중으로 쓴다.

Key Characteristics:

  • 브랜드 색은 종이 · 네이비 · 브론즈 세 가지다. 여기에 상태 색(오류 · 주의), 의미 색(세 판결 · 판단 요소 · 가중/감경 · 역할 이름표)과 책상 계열 · 마이페이지 커피 테마만 예외로 더한다. 빨강은 오류에만 쓴다.
  • 토큰 3층 구조(팔레트 → 역할 → 화면). 화면 CSS에는 색 값을 직접 쓰지 않는다.
  • 깊이는 책상 위 물건의 그림자로, 카드 중첩 없이 선과 면 색으로 구분한다.
  • 연출은 건너뛸 수 있고, 연출 뒤에 정보를 숨기지 않는다.
  • 색만으로 뜻을 전하지 않는다(기호 · 문장과 함께).

Colors

종이(크림)가 바탕, 네이비가 글자와 조작, 브론즈가 장식이다. 토큰은 style.css :root에 3층으로 있다.

층 접두어 규칙
① 팔레트 --palette-* 색 값(hex)은 여기에만 쓴다
② 역할 --color-* 화면 CSS는 이 이름만 쓴다. 특정 역할만 바꾸려면 그 줄만 고친다
(별도) 책상 계열 --palette-wood-* 등 · --color-desk-* · --color-stage-* 3층과 별개인 네임스페이스. 책상 세계의 공용 색으로, style.css에서는 ④ 구역이다(S-01 책상 그림에서 시작해 지금은 여러 화면이 쓴다)

Primary

  • 먹빛 네이비 (#101d39, --color-heading · --color-banner-bg): 로고 · 큰 제목 · 강조 값(형량) · 헤더 띠 · 주 버튼 호버.
  • 서류 네이비 (#1f3563, --color-accent): 주 버튼 · 선택 표시 · 링크. 선택된 항목의 바탕은 안개 네이비 (#eef1f7, --color-accent-tint).

Secondary

  • 놋쇠 장식 (#ba854b, --color-decor): 장식 선 · 로고 "Law". 작은 글자에는 쓰지 않는다.
  • 놋쇠 라벨 (#86581f, --color-label): 섹션 머리 작은 라벨(신문 kicker).
  • 옅은 놋쇠 (#e6d2b5, --color-range-fill): 권고 범위 막대. 어두운 면 위 포커스 링에도 쓴다.

Neutral

  • 밝은 종이 (#fdfcfa, --color-bg): 페이지 바탕. 카드 · 입력칸은 --color-card 흰색, 옅은 면은 부드러운 종이 (#f7f3ee, --color-surface), 한 단계 진한 면은 #f1ebe4 (--color-surface-strong), 신문지는 #f4efe4 (--color-paper), 진행 막대 바탕은 #ebe5dc (--color-track).
  • 종이 괘선 (#e2dad0, --color-border)과 진한 괘선 (#8b8174, --color-border-strong): 구분선 / 입력칸 · 버튼 테두리.
  • 잉크 (#151a24, --color-text)와 옅은 잉크 (#5d5952, --color-muted): 본문 / 보조 글자.
  • 포커스 블루 (#2f6fdb, --color-focus): 키보드 포커스 링 전용.

상태 색 (빨강은 오류에만)

  • 오류: 글자/주색 #b3261e (--color-danger), 테두리 #efb8b3 (--color-danger-border), 바탕 #fcecea (--color-danger-tint), 어두운 면 위 글자 --color-danger-on-dark.
  • 주의(되돌릴 수 없음 · 권고 범위 밖): 글자 #9a3d0a (--color-caution), 테두리 #d4692a, 바탕 #fff1e7.

의미 색

  • 세 판결: 나 #61538b · AI #0b6762 · 실제 재판부 #4f6526 (--color-judge-*). 세 색은 밝기가 같다. 선 · 라벨에만 쓰고 바탕을 칠하지 않는다.
  • 판단 요소: 무겁게 #a03f35 / 가볍게 #35704a (--color-factor-*), 바탕은 각각 -tint.
  • 가중 ↑ · 감경 ↓ (--color-up · --color-down)은 항상 화살표 기호와 함께 쓴다.
  • 4축 막대: 엄격 쪽 남색 #2f5592(--color-axis-strict), 관대 쪽 갈색 #a8652f(--color-axis-lenient). 채도가 비슷한 두 색으로 양쪽을 구분만 하고 좋고 나쁨을 뜻하지 않는다. 갈색은 글자로는 대비가 모자라(3.3:1) 글자에는 한 단계 진한 --color-axis-lenient-text(#8a4f22)를 쓴다.
  • 사건 유형(--color-case-*), 무대 이름표 역할(--color-role-*), 카카오 로그인(--color-kakao, 카카오 가이드 색이라 임의로 바꾸지 않는다)은 같은 방식의 역할 토큰이다.

책상 계열

  • 책상 나무 (#a8652f), 펠트 패드 (#1c2b4f, 안쪽 테두리 #14213f), 크라프트 서류철 (#d6a468), 책상 종이 (#f7f3ee, 부드러운 종이와 같은 값), 그림자 짙은 보라 검정 (#0f0a19) 등 물건 이름으로 짓는다(--color-desk-*).
  • 집무실 바닥(FE-87, --color-room-*): 마루 --color-room-floor · 널 --color-room-plank · 이음매 --color-room-seam, 러그는 책상 패드와 같은 네이비(--color-room-rug · -edge), 맨 아래 문짝은 마루와 갈리는 밝은 나무(--color-room-door = 책상 나무 밝은 쪽, 두께 --color-room-door-edge). 메인에서는 쪽 바탕을 푸터와 같은 갈색으로 두고(아래 오버스크롤), 헤더 위로 남색 띠를 내어 위 오버스크롤도 헤더 색으로 잇는다.
  • 어두운 면(펠트 · 나무) 위 글자는 밝은 종이색(--color-desk-sheet, --color-desk-text)으로 뒤집고, 주 버튼도 같이 뒤집는다. 포커스 링은 옅은 놋쇠.

그래픽 에셋 (B안 이미지, FE-150)

책상 소품 · 배경 · 질감은 CSS 그라디언트 · 인라인 SVG 대신 B안(소프트 매트 렌더) 이미지로 그린다. 파일은 frontend/public/images/ 아래 webp(앱 아이콘만 png)이고, 원본 PNG와 생성 프롬프트는 저장소 밖(로컬 NaeLawNamBul/B-, Claude outputs/frontend/그래픽-에셋-B안-프롬프트_2026-11-02.md)에 둔다.

  • 스타일 한 줄: 평면 셀 셰이딩 + 얕은 음영, 윤곽선 거의 없음(색 면으로 구분), 위 왼쪽 약한 빛, 재질은 나뭇결 · 펠트 · 종이 섬유 · 유약 정도만 은은하게. 귀엽거나 번들거리지 않는다. 빨강 금지(도장도 네이비 잉크).
  • 그림자는 이미지에 굽지 않는다. 놓인 그림자는 지금처럼 CSS drop-shadow가 입히고, 호버 밝기(brightness)도 CSS가 한다. 이미지 안의 글자는 없다 — 글자는 전부 HTML이 올린다(노트북 화면 · 서류철 라벨 · 엽서 · 포스트잇 · 도장 · 우표).
  • 폴더: desk/(나무판 · 패드 질감 · 신문 · 서류철 · 노트북 · 커피잔 · 펜 · 엽서 · 포스트잇 · 날아오는 잔용 큰 커피잔), room/(마루 · 의자 · 러그 · 러그 밑면 · 안내 종이 · 문), stage/(2막 법정 · 1막 신문 배경), mark/(잉크 도장 · 우표 · 앱 아이콘), card/(성향카드 배경 18종 · 이미지 테두리 4종), desk-felt.webp(5개 화면 바탕).
  • 방향: 서류철 그림은 코드 원본 방향(앞표지 왼쪽 · 뒤표지와 종이 오른쪽, 윗변이 오른쪽 위로 올라감)이다. 예전의 scaleX(-1)은 없앴고, 라벨은 앞표지 가운데에서 -11도로 둔다.
  • 크기가 화면마다 변하는 것(패드 · 러그 · 법정 · 막 배경)은 완성된 모양이 아니라 질감 한 장을 cover로 덮고, 모서리 · 테두리 띠 · 술 · 접힘은 CSS가 맡는다. 러그는 직물 위에 러그색 막(80%)을 덮어 대비를 낮추고, 접히는 귀퉁이는 직물 크기를 러그 본체와 맞춘다(--rw × 1.34).
  • 이어 붙이는 질감: AI 생성 질감은 가장자리가 이어지지 않는다. 반복해서 쓰는 마루만 위아래를 거울로 이은 타일(floor-plank-tile, 널 4장 = 가로 448px)로 두었다. 새 질감을 반복해 쓰려면 같은 방식으로 이음매를 먼저 없앤다.
  • CSS로 남긴 것: 신문 모서리 말림(clip-path + .news-curl) · 러그 귀퉁이 접힘 · 안내 종이 귀퉁이 접힘과 줄 칸, 기존 카드 테두리 4종(원목 · 금박 · 법정 네이비 · 바느질), 카드 기본 · 크림 · 클래식 배경, 상태 도장(.status-stamp), 기능 아이콘 · 서명 · 그래프 · 카카오. 말림 · 접힘은 이미지 가장자리가 오려져도 이어 보이도록 그림의 해당 귀퉁이는 평평하고 밝기가 균일해야 한다.
  • 카드 꾸미기 추가(FE-150): 카드 배경 14종(크라프트 서류철 · 장부 종이 · 트레이싱지 · 오래된 문서 · 커피 자국 · 책상 나뭇결 · 압지 · 봉투 안감 · 스탠드 불빛 · 리갈패드 · 네이비 펠트 · 블라인드 햇빛 · 네이비 가죽 · 놋쇠판)과 이미지 테두리 4종(참나무 · 황동 · 법정 · 실 바느질 액자)을 기존 목록(FRAMES · BACKGROUNDS)에 더했다. 배경은 그림 한 장을 카드에 덮고(card/bg-{id}.webp), 어두운 배경(wood · blinds · lamp · felt · leather · bronze)은 글자를 밝은 종이색으로 뒤집는다. 테두리는 정사각 그림(card/frame-{id}.webp)을 9등분(border-image)해 7 : 12 카드에 늘이고, 두께는 카드 폭 5.5cqi(견본 3px)다. 해금은 목 서버 표(mocks/profileDecoMockData.js)에서 기존 도전과제에 나눠 배정했다(첫 판결 · 내 성향 카드 · 단골 손님 · 수집가 · 일주일 출석 · 세 번째 판결 · 열혈 수집가 · 한 달 출석). cases-5 · collect-50은 BE-146 결정(경험치만)대로 건드리지 않았다. 실제 서버 카탈로그(ProfileDecoCatalog)에는 아직 이 id가 없다(후속 BE-158). 그 전에 열 수 없는 칸이 보이지 않게, 화면은 서버 응답(열린 목록 + 도전과제 보상)에 있는 id만 칸 · 분모("카드 배경 n / m")에 쓴다(isServerDeco). 목 서버는 전부 알아서 모두 보이고, BE-158이 머지되면 코드 수정 없이 나타난다.
  • 임시: 앱 아이콘 · 탭 아이콘은 책상 스탠드 시안이다(mark/app-icon-*.png, index.html). 로고가 정해지면 바꾼다. OG 이미지는 mark/og-image.svg(제목 글자 시안, 임시 문구)와 같은 모양의 og-image.jpg(1200 × 630)를 만들어 두었다. index.html 메타(og:image 등, 주소 https://naelawnambul.today)에 연결해 두었다. 문구는 임시이므로 바꾸면 SVG를 다시 렌더링해 jpg를 바꾼다.

스코프 테마 (마이페이지)

  • 화면 하나가 분위기를 통째로 바꿔야 할 때는 그 화면의 최상위 클래스 안에서만 역할 토큰을 덮어쓴다. 의도된 설계이며 규칙이다.
  • 지금 사례: .my-page가 --color-bg · --color-card · --color-accent 등을 커피 팔레트(바탕 #ead8c0, 진한 글자 #4a2c1c)로 덮어쓴다(커피잔이 마이페이지 입구, FE-44). 스코프 테마 안에서는 --palette-*를 직접 써도 되지만 같은 블록에 모아 쓴다.
  • 헤더(#route-header)는 화면 밖이라 토큰을 못 받는다. 라우터가 data-theme="coffee"를 붙이고 .site-header[data-theme="coffee"]가 색을 따로 정한다.

Named Rules

The Role-Only Rule. 화면 CSS(pages/*)는 --color-* 역할 토큰만 쓴다. 새 색은 ①에 팔레트를 먼저 넣고 ②에서 역할 이름을 붙인다. 확인: grep -rnE "#[0-9a-fA-F]{3,8}\b|rgba?\(" frontend/src/pages --include="*.css". (예외: 스코프 테마)

The Name-Fits-Use Rule. 이름과 용도를 맞춘다. 어두운 면 위 글자에 테두리용 토큰을 빌려 쓰지 않고 전용 토큰을 만든다(예: --color-danger-on-dark).

The Red-Means-Error Rule. 빨강은 오류에만 쓴다. 판결이나 판단 요소의 색은 진하지 않은 레드 · 그린이고, 항상 문장이나 기호와 함께 쓴다.

The Quiet Bronze Rule. 브론즈는 장식 선과 로고 "Law" 정도로 드물게 쓴다. 작은 글자에는 쓰지 않는다.

Typography

Display Font: Noto Serif KR (AppleMyungjo, Batang, serif) — 화면 제목 h1 · 로고 Body Font: Pretendard (-apple-system, Apple SD Gothic Neo, Malgun Gothic, sans-serif) — 본문 · 조작 Article Font: Nanum Myeongjo — 신문 기사 본문(S-02 지면). Hand Font: Nanum Pen Script — S-01 메모지 손글씨.

Character: 신문 제호와 판결문의 명조 제목, 읽기 편한 고딕 본문. 제목은 묵직하게, 본문은 또렷하게.

Hierarchy

  • Display (34px, --text-display): 큰 제목.
  • Headline (900, 28px, -0.02em, --text-title-1): 화면 제목 h1(명조). 섹션 · 카드 제목은 본문 글꼴 그대로 둔다.
  • Title (22px, --text-title-2; 18px, --text-title-3): 패널 · 섹션 제목.
  • Body (15px, 행간 1.75, --text-body): 기본 본문. 행간은 1.6 ~ 1.75.
  • Label (13px, --text-small) · Caption (12px, --text-caption): 보조 글자. 헤더 로고는 22px 900 명조, 목데이터 딱지는 11px.

Named Rules

The Korean Wrap Rule. 한국어 줄바꿈은 word-break: keep-all, 긴 제목은 text-wrap: balance, 문장은 text-wrap: pretty, 긴 영문 · 주소는 overflow-wrap: anywhere.

The 16px Input Rule. 입력칸 글자는 16px 이상(그보다 작으면 iOS가 확대한다).

Layout

본문 폭은 --content-width 1180px 가운데 정렬이고, 페이지 틀은 1px solid var(--color-border) 안에 --color-bg다. 읽기 중심 화면(마이페이지 등)은 안쪽 본문을 760px 안팎으로 좁힌다. 간격 눈금은 따로 정해져 있지 않다(아래 "현재 코드와 차이").

  • 공통 헤더: 라우터가 모든 화면 위(#route-header)에 붙이는 네이비 띠. position: sticky; top: 0; z-index: 50. 관리자는 자기 틀을 쓴다. 화면 CSS에 헤더 규칙을 다시 두지 않고, 특정 화면만 다르게 할 때는 body:has(.xx-page) .site-header로 감싸서 추가한다.
  • 헤더 높이는 main.js가 재어 --site-header-h로 넣는다. html { scroll-padding-top }이 이를 써서 포커스 · 스크롤 이동이 헤더에 가리지 않는다.
  • 반응형: ≤ 720px에서 헤더가 두 줄(윗줄 로고 · 로그인, 아랫줄 메뉴 가로 스크롤). 721 ~ 1000px에서는 한 줄을 지키려고 로고(17 ~ 22px) · 메뉴 글자(11.5 ~ 13px) · 간격 · 스위치가 폭에 맞춰 함께 줄어든다(clamp, 로고는 줄바꿈 금지). 좁은 화면의 헤더 좌우 여백은 max(16px, env(safe-area-inset-*)), 721 ~ 1000px 구간도 max(clamp(…), env(safe-area-inset-*))(폰을 가로로 돌린 폭 844px이 이 구간이라 가로 모드 노치를 같이 지킨다). 메뉴 줄은 overscroll-behavior-x: contain.
  • 분기점 기준(FE-151): 모바일 = @media (max-width: 720px), 넓은 화면 = (min-width: 721px). JS matchMedia도 같은 값을 쓴다. 이 밖의 구간은 뜻이 다를 때만 둔다 — 작은 폰 ≤ 380px, 태블릿 ≤ 1100px, 헤더 한 줄 압축 721 ~ 1000px. 모바일 전환에 600 · 680 · 760 · 768을 새로 쓰지 않는다. 기준 화면은 390 × 844(360 · 720 · 721 · 768에서도 깨지지 않게 확인).
  • 모바일 공통 기반(FE-152, style.css · index.html): 탭 번쩍임 제거 · touch-action: manipulation · 컨트롤 글자 선택 막기(본문은 선택 가능) · 가로 모드 글자 키움 막기(text-size-adjust: 100%) · 터치 기기 입력창 글자 16px 이상(iOS 확대 방지, pointer: coarse에서만. 명시도 (0,1,1)이라 화면 CSS의 단일 클래스 규칙보다 이기는 강제다 — 태블릿 터치의 촘촘한 화면 입력창도 16px 이상) · 공통 푸터 아래 env(safe-area-inset-bottom) · 뷰포트 viewport-fit=cover, interactive-widget=resizes-content(안드로이드도 키보드가 뜨면 레이아웃을 줄인다) · 상태 바 색(theme-color)은 라우터가 화면마다 헤더 바탕색으로 맞춘다(마이페이지 = 커피, 관리자 = 처음 값 네이비). 화면 아래 고정 요소(조작 줄 · 고정 버튼 · 시트)는 calc(원래 여백 + env(safe-area-inset-bottom, 0px)). 높이(vh 단독 금지): 앱 틀 · 고정 높이 화면(관리자 · 판결 무대) · 아래 고정 요소 · 대화상자는 dvh(보이는 영역을 따라간다), 첫 화면 배경(로그인 · 성향 테스트 책상)은 svh(주소창이 접히고 펴져도 흔들리지 않는다). 짧은 문서형 페이지 틀의 min-height는 스크롤이 없어 주소창이 접히지 않으므로 dvh · svh가 같아 두 값이 섞여 있어도 둔다 — 새로 쓸 때는 svh. dvh로 둔 틀은 내용 높이가 svh ~ lvh 사이일 때만 스크롤 중 틀 높이가 달라질 수 있다. 줌 막기(user-scalable=no · maximum-scale)는 쓰지 않는다.
  • **분기점 숫자는 CSS px(뷰포트 폭, 스크롤바 포함)**다. 브라우저 창 크기로 잰 값은 창 테두리 · 스크롤바만큼 더 크게 나온다(예: 창 737px ≈ CSS 720px).
  • 터치: 터치 대상은 44px 이상이 기본이다.
  • z-index: 헤더 50, 화면 전환 연출 층 1000.

크기 · 자리 기준 (화면별)

화면마다 "무엇을 기준으로 크기와 자리를 정하는지"가 다르다. 새 요소를 넣을 때는 그 화면 표에서 이웃 요소와 같은 기준을 고른다. 2026-10-23 코드 기준이다.

기준 용어

용어 뜻 예
px 고정 화면이 바뀌어도 같은 px 문짝 두께 44px, 러그 위쪽 여백 56px
비율 (배율) 다른 값의 몇 배 · 몇 % 서류철 = 러그 폭 × 0.48, 종이 = W × 1.2
기준 좌표계 정해 둔 그림판(px) 안에 놓고 그림판째 줄임 책상 그림 1672 × 941
상대 위치 다른 요소 · 부모 기준 자리(흐름, %, cqw, 단위 W) 러그 가운데 정렬, 종이 자리 --x · --y(W 단위)
절대 위치 position: absolute로 부모 상자 안 고정 좌표 러그 밑 서류철(러그 오른쪽 아래 모서리 기준)
화면 고정 position: fixed · sticky(스크롤과 무관) 헤더, 전환 연출 층
자체 단위 화면이 정한 한 칸 길이(--u · cqw · cqi)에 맞춰 모든 크기를 곱함 6막 지면 --u, 무대 cqw, 마이페이지 카드 cqi

공통 틀

요소 크기 자리
페이지 틀(판결 체험 문서 화면 · 마이페이지 · 관리자) px 상한: --content-width 1180px, 그보다 좁으면 100% 상대: 가운데 정렬
공통 헤더 높이는 내용대로(main.js가 재어 --site-header-h), 721 ~ 1000px에서 글자 · 간격 clamp 축소 화면 고정: sticky 맨 위, z 50(성향 테스트만 fixed)
책상 펠트 바탕(사건 목록 · 사건 기사 · 성향 테스트 · 2 ~ 6막) 화면 전체 화면 고정: fixed; inset: 0; z-index: -1(배경 이미지 cover)
화면 전환 연출 층 화면 전체 화면 고정: fixed; inset: 0; z-index: 1000
대화상자 · 엽서 창 px 상한 + 100%(min(640px, 100%) 등), 높이 100dvh − 32px까지 화면 고정 층 가운데
사진 · 그림 비율 고정(aspect-ratio) + 폭 100% 칸 안 흐름
터치 대상 px 고정: 최소 44px –

메인 (S-01)

메인은 위(첫 화면 = 책상)와 아래(서비스 소개 = 집무실 바닥)가 크기를 정하는 방식이 다르다.

너비 구간 (메인 기준, 연출 · 배치가 바뀌는 지점)

구간 판정 첫 화면
넓은 너비 책상이 높이에 맞춰 줄어듦(폭 / 1672 ≥ 높이 / 941) 책상, 신문 폭 그대로
중간 너비 721px 이상이고 책상이 폭에 맞춰 줄어듦(창 높이 800 기준 약 1320px 아래) 책상, 신문 폭이 화면 폭 따라 줄어듦
좁은 너비 ≤ 720px (landing.css 미디어 쿼리) 책상 대신 메뉴 카드

첫 화면 (책상)

요소 크기 자리
책상 장면 높이 = 화면 높이 − 헤더(100svh), 최소 360px 화면 맨 위
책상 그림 전체 기준 좌표계 1672 × 941을 --desk-scale(= min(폭 / 1672, 높이 / 941))로 통째로 줄임 그림판 안 px(절대 위치), 남는 폭은 --dx로 가운데
신문 596 × 712 기준, 남는 높이로 최대 1.2배까지 아래로 길어짐(--ng ≤ 142) 기준 좌표계 절대 위치
작은 소품(엽서 · 서류철 · 커피 · 포스트잇 · 캐릭터) 배율이 줄수록 비율로 키움(--k, 배율 0.85 → 0.5에서 최대). 포스트잇은 기본 1배(FE-99, 옛 1.15배)로 줄여 첫 화면의 주인공이 되지 않게 함 기준 좌표계 절대 위치 + 여유 높이(--r) 비율로 이동
신문 바람 펄럭임(.news-sheets span, FE-99) 맨 앞 지면의 오른쪽 아래 모서리(접힌 변 최대 118px, 지면 폭의 약 20%)가 휙 들려 곡선으로 말렸다가 넘어갈 듯 말 듯 망설이고, 한 번 더 들썩인 뒤 떨어져 살짝 튀고 가라앉음.맨 앞 장만 들리고 밑 종이 3장은 그대로.(들린 모서리 밑으로 빈 종이 겹만 보임). @property --d 하나만 애니메이션하고, 지면 오려내기(clip-path)와 뒷면(.news-curl, 접힌 선 어둡고 → 밝고 → 끝에서 약간 어두운 그라디언트)이 같은 점을 쓴다. 원근 없는 2D 6.3초 주기로 되풀이(약 45%는 쉼), 종이마다 .09초씩 늦게 신문 오른쪽 · 위로 종이 끝이 삐져나옴. 이름표를 늘리지 않고(FE-50 유지) 움직임으로 "눌러 볼 물건"을 알린다. 신문을 집어 들면 멈춤, 동작 줄이기 · 연출 끄기면 없음

아래 (집무실 바닥) — 방 폭(cqw, .room이 container)과 책상 배율(--desk-px)을 기준으로 한다

러그 위 종이 크기(--W)는 Safari가 탭 복귀 뒤 cqw를 갱신하지 않는 문제(FE-90) 때문에 var(--cqw, 1cqw)로 쓴다. --cqw는 방 폭의 1%(px)이고 landingPage.js가 ResizeObserver · 탭 복귀 때 넣는다. 새로 이 계산에 단위를 더할 때도 같은 변수를 쓴다.

요소 크기 자리
의자 비율: 높이 기준 --ch = 신문 폭 596 × 책상 배율, 가로 --cw = --ch × 1.6(최대 92vw) 상대: 방 폭의 62%(--chair-x), 안내문과 최소 10px 떨어지게 오른쪽으로 밀림. 책상 밑으로 넣어 위쪽이 가림
안내문(이렇게 체험해요) 비율: --ch × 0.64 (140 ~ 520px) 상대: 왼쪽 여백 clamp(25px, 6cqw, 100px), 책상 가장자리에서 최대 100px
러그 폭: min(1180px, 방 폭 − 96px)(좁은 너비: 방 폭 − 32px), 높이: 종이 묶음 W × 4.25 + 여백 상대: 가운데 정렬, −0.6도
러그 위 종이 비율: 기준 폭 --W(= min(420px, 방 폭 24%, …)) × 종이별 배수, 비율(세로 : 가로) 고정. 종이 안 작은 글자는 하한(11 ~ 12px, max())을 둔다(FE-99). 좁은 너비(≤ 720px)는 라벨(머리기사 라벨 · 주문 · 누구의 판결 · 형량 눈금 · 목데이터 · 법률 자문 아님) 하한이 12px이고, 판단 요소 표 머리만 칸 폭(50u) 때문에 11px(FE-153). 좁은 너비는 종이 높이를 풀고 원래 비율 높이를 min-height로만 둬, 글자가 넘치면 종이가 아래로 길어진다(360px에서 설명이 종이 밖으로 넘치던 것, FE-153) 상대: W 단위 --x · --y, 묶음은 러그 가운데. 좁은 너비(≤ 720px)는 가로로 겹치지 않고 한 장씩 세로로 쌓아 왼쪽 · 오른쪽을 번갈아 놓는다(W = (방 폭 − 90px) / 1.2, 글자가 5px대로 줄던 것을 막음, FE-99)
맨 아래 신문 끝자락 등장(.room-news.is-out, FE-99) 처음엔 벽(문) 밑에 숨음(translate로 보이는 높이 + 24px 내림) 화면에 60% 들어오고 1초 뒤 .9초 동안 슥 밀려 나옴(한 번). 움직임 줄이기 · 키보드 포커스면 바로 보임
좁은 너비 첫 화면 메뉴 카드 위쪽 정렬(옛 세로 가운데), 카드 밑 빈 곳은 내로가 고개만 내민 장식(.desk-menu-peek, 96 ~ 190px) 흐름: 제목 → 카드 → 내로
종이 안 글자 · 간격 비율: --u = W / 300 종이 안 흐름
러그 밑 서류철 비율: 러그 폭 × 0.48, 443 : 521 절대: 러그 오른쪽 아래 모서리에서 서류철 폭 × 0.06(가로) · 0.07(세로)만큼 밖으로(FE-150에서 절반쯤으로 줄임. 860px 이하 0.08 · 0.09, 좁은 너비 0.06 · 0.2). 이름표는 러그를 접으면 드러나는 삼각형 안(서류철 폭의 47% · 높이의 72%, 폭 30%)에 둔다 이름표 글자는 서류철 폭의 5%(390px에서 약 8.6px)로 장식이다 — FE-153에서 12px 하한을 걸었으나 FE-150이 서류철 · 이름표 판을 작게 다시 그리면서(54 × 34px) 12px 세 줄이 판에 들어가지 않아 하한을 뺐다. 읽는 글이 아니라 접힌 귀퉁이를 누르는 입구 그림이다
접히는 귀퉁이 비율: 러그 폭 × 0.36(직각삼각형 한 변) 절대: 러그 오른쪽 아래
신문 끝자락 비율: 596 × 712 지면의 윗부분 20%만 보임, 좁은 너비는 0.55배(--news-zoom) 상대: 가운데, −1.5도. 아래는 문 밑으로 들어감
문(맨 아래 띠) px 고정: 두께 44px(윗면 + 앞면 12px) 상대: 집무실 맨 아래
들썩임 여유 px 고정: 신문 끝자락 칸 위쪽 안쪽 여백 48px –

사건 목록 (S-02) · 사건 기사 (S-20)

신문 한 장이 화면 폭을 따라 넓어지고, 안쪽은 비율 격자다. 1면 · 기사 면 모두 caseList.css 지면을 쓴다.

요소 크기 자리
지면 px 상한 + 화면 비율: min(1280px, 100vw − 48px), 720px 이하 100%(여백 0 · 안쪽 18px) 상대: 가운데, 위 32px
제호 줄 비율 격자: 귀 190px · 가운데 1fr · 귀 190px(1100px 이하 150px, 720px 이하 한 줄). 6막 읽기 지면 · 내 결과 지면의 귀도 같은 폭(FE-120). 제호 글자는 92px, 1100px 이하 min(72px, (100vw − 494px) / 4.4)(좁은 태블릿에서 가운데 칸을 넘쳐 포스트잇에 덮이지 않게, FE-154), 720px 이하 50px 흐름
머리기사 단 비율 격자 2.6 : 1(1100px 이하 2 : 1, 720px 이하 한 단). 머리기사 배너 글자는 720px 이하에서 모든 면 30px(오피니언 28px, 성향 테스트 면 포함, FE-154) 흐름
아래 기사 · 색인 비율 격자: 아래 기사 2열, 색인 6열 → 3열(1100px 이하) → 2열(720px 이하) 흐름
기사 사진 비율 고정: 추천 16 : 9, 색인 3 : 2, 폭 100% 흐름
면 탭 (FE-102 · FE-119) px 고정 최소: 72 × 44px. 탭 하나가 신문 한 면이다: 1면 → 살인 · 사기 · 상해 · 강도 · 절도(사건이 0건이면 숨김, 보던 면은 남김) → 색인 → 성향 테스트 → 오피니언. 탭 글자 옆에 건수(.tab-count, 12px, 1면은 9건까지, 색인 · 성향 테스트 · 오피니언은 건수 없음)만 보인다("상해 3건 · 전체 11건" 같은 요약 줄은 없앴다, FE-119). 720px 이하는 면이 아홉이라 탭 줄을 가로로 밀어 고르고(가로 스크롤 컨테이너), 면을 그릴 때마다 지금 면 탭이 줄 가운데로 온다(FE-154). 고른 면은 주소 ?type=에 남는다 흐름
1면 · 유형 면 기사 수 (FE-119) 머리기사 1 + 추천 2 + 다른 사건 6 = 9건. 1면은 9건만(나머지는 유형 면 · 색인에서 본다), 유형 면은 9건 뒤를 "이 면 더 보기"(점선 44px 줄, 펼침)로 연다. 신문 최소 높이 = max(100dvh + 120px, 신문 폭 × 1.4)(신문 폭이 넓은 화면에서 신문이 짧아 보이지 않게 하한을 둔다)이라 짧은 면은 법적 고지가 아래로 밀리고 펼침은 대개 화면 밖에서 일어난다(S-20 기사 지면은 제외) 흐름
잠긴 사건 표시 (FE-149 · BE-146) 사건 목록의 기사 분류명 줄 · 색인 줄에 진행 상태 도장과 같은 자리 · 크기의 점선 도장 "잠김"(.status-locked, 이중 선 대신 점선 3px, 글자 · 선 --color-muted/--color-border-strong, -4도). 체험한 사건은 잠기지 않으므로 진행 중 · 완료 도장과 겹치지 않는다. 잠긴 사건은 머리기사 · 추천 칸에서 뒤로 밀리고(준비 중과 같은 취급), 1면 포스트잇에 "사건 N건을 더 마치면 새 기사가 열려요."가 붙는다. 기사를 눌러도 막지 않고 사건 정보(S-20)에서 잠김과 이유를 알린다 흐름
색인면 (FE-119 · FE-126) 검색칸 최대 420 × 44px(720px 이하 글자 16px, iOS 확대 방지), 그 아래 사건 종류 버튼(.face-kind, 최소 높이 40px, 720px 이하 44px: 전체 + 면 이름 다섯, 색깔 네모 · 이름 · 건수, aria-pressed. 기본은 전체, 누르면 그 종류만 남고 검색과 함께 거른다. 이름은 면 이름을 쓴다 — 분류명 "재산범죄"는 강도 · 절도가 겹친다). 제목 목록 2단(720px 이하 1단), 줄 최소 높이 48px(색깔 네모 · 제목 한 줄(말줄임, 마우스를 올리면 전체) · 진행 상태 도장(줄 오른쪽, 글자 위로 겹침)). 분류 글자는 줄에서 뺐다(네모와 종류 버튼이 말한다). 쪽 번호(.face-pager, 이전 · 숫자 40 × 40px(720px 이하 44 × 44px) · 다음): 걸러진 사건이 한 장(20건)을 넘을 때만 보이고 검색 · 종류가 바뀌면 1쪽으로 돌아간다. 세로 여백이 남으면 다른 면과 같이 읽을거리 줄로 채운다 흐름
성향 테스트 면 (FE-126) 신문 한 면: 배너 → 위 두 단(1.7 : 1: 왼쪽 소개 기사(사진 높이 190px + 2단 본문) · 오른쪽 가치관 4축 저울 + 안내) → 열여섯 유형 갤러리(8열, 엄격 → 관대 순서, 칸마다 그림 120px · 관용 눈금(8px 네모) · 이름 · 한 줄). 이름 · 한 줄 · 그림은 서버 시드와 같은 og/personality-types.json이라 목 데이터가 아니다. 내 유형은 응답을 받은 뒤 칸에 "내 유형" 도장을 찍고 광고 칸을 "내 결과 다시 보기"로 바꾼다(못 받으면 그대로). 광고 칸 전체가 눌린다. 이 면은 광고를 본문에 이미 싣기 때문에 아래 여백 칸에는 광고 없이 법정 상식 · 사진만 둔다(광고는 면당 한 번) 흐름
오피니언 면 (FE-126) 신문 한 면: 사설 배너 → 위 두 단(사설 본문(그림 · 둘째 문단 뒤 인용구 · 바이라인 "사설 n/5") | 오른쪽 남불의 한마디 · 지난 사설 다섯 편 목록) → 독자 투고(커뮤니티 공감 많은 글 3건 + "편집부에 보내기" 칸). 오늘 사설은 날마다 차례로 돌아가고 지난 사설을 누르면 그 편이 같은 자리에 실린다. 독자 투고는 서버에서 받은 뒤 채우고(받기 전에는 보내기 칸만, 실제 모드처럼 API가 없으면 칸 숨김), 목 데이터일 때는 "목데이터" 표기를 단다. 면을 떠났거나 다른 면으로 넘겼으면 늦게 온 값은 버린다 흐름
지면의 호버 상자 (FE-126) 눌러서 어딘가로 가는 칸(기사 · 유형 · 투고 · 광고)은 칸의 글 · 사진에서 바깥으로 10px인 같은 상자를 띄운다. 칸마다 단 사이 padding이 달라 outline으로는 쏠려서, 마우스를 올릴 때 그 칸의 padding을 재 --pick-inset으로 상자(.is-pickable::after) 자리를 정한다(hoverBox.js). 칸 전체 클릭은 마우스용이고 키보드 · 화면 낭독기는 칸 안 버튼으로 간다. 동작 줄이기에서는 전환 없음. 신문 기사 칸(.is-pickable)은 호버가 없는 기기에서 누르는 동안(:active) 호버와 같은 상자를 보인다(FE-154) 흐름(호버 7패턴에는 아직 넣지 않았다 — 기존 기사 호버 상자와 같은 모양)
면 넘김 화살표 (FE-118, FE-119에서 종이 귀만 빼고 남김) px 고정: 40 × 88px, 모서리 6px 화면 고정: 세로 가운데, 지면 바깥 여백 max(8px, 여백 − 52px)(여백이 좁으면 지면 안쪽 여백 위에 걸친다). 720px 이하 · 지면이 들어오는 중 · 사건으로 가는 중에는 숨긴다
빈 칸 채움 지면 (FE-118 · FE-119) 기사 칸 안: 추천 사건 반쪽(세 개 세로), "이번 호 다른 사건" 마지막 줄 빈 칸(emptySlots) — 들어간 칸 크기 그대로. 면 아래 세로 여백: 최소 높이보다 기사가 짧으면 남는 높이를 읽을거리 줄로 채운다. 칸 폭 최소 360px(1200px 폭에서 3칸, 최대 4칸), 줄 최소 120px · 최대 2줄, 줄 높이 = 남는 높이를 줄 수로 나눈 값이라 빈틈이 없다. 190px 이상인 높은 줄은 사진 지면(공용 재연 컷 5분류 + 탐구 · 대화 컷 6장, 세 번에 한 번이 탐구 컷, + 망점)을 대각선으로 엇갈려 섞고, 낮은 줄은 광고 · 법정 상식으로 채운다. 광고는 한 면에 한 번만 흐름(본문이 남는 높이를 받고 .face-fill이 그 안에서 남은 만큼 차지)
남불 포스트잇 px 고정: 184px 정사각(aspect-ratio: 1). 문구는 지금 체험할 수 있는 사건 수에 맞춘다(전부 열렸으면 "눌러 보세요", 일부면 "지금 체험할 수 있는 기사는 N건이에요", 없으면 "체험은 곧 열려요", FE-102). 건수를 받기 전에는 감춰 두었다가 한 번에 나타난다. 1면에만 붙어 있다(FE-126): 떼지 않았으면 다른 면으로 넘길 때 1면과 함께 넘어가고 1면으로 돌아오면 함께 돌아온다(넘기는 장이 지면을 본뜨므로 has-tip이 지면에 있다) 절대: 지면 오른쪽 위(위 −16px · 오른쪽 22px, 3도). 오른쪽 귀 위에 놓이므로 떼기 전에는 귀를 감추고(visibility) 떼면 귀(건수)가 드러난다. 720px 이하는 흐름 안 가로 띠이고, 얼굴 + 한두 줄(약 64px)로 줄였다(FE-102)
오려 낸 기사(S-20) px 상한 + 100%: min(1040px, 100%) 상대: 가운데
체험 시작 전 "시작하기" 도장 (FE-119) px 고정: 최소 폭 156px, 이중 테두리 4px(서류 네이비), 글자 24px 명조 굵게 + 위 "사건 접수" 11px(장식, 낭독 안 함). 지면 위에 찍힌 도장이라 실제 <button>이어도 화면을 따라오지 않는다 흐름(격자): 제목 줄(.story-top)의 오른쪽 칸, 사건 메모 바로 밑 가운데에 -5도로 기울어 놓이고 왼쪽 빈 자리 블록과 세로 가운데를 맞춰(살짝 아래로, 위 여백 22px) 놓여 노트북 화면에서도 스크롤 없이 보인다. 제목 칸은 두 줄 높이를 차지해 왼쪽에 빈 자리가 생긴다. 720px 이하 한 단에서는 메모 밑에서 칸을 꽉 채우고 똑바로 펴진다(FE-155, 전에는 768px). 준비 중은 -3도로 살짝 틀어진 점선 도장(눌러도 아무 일 없음). 대본 점검 중(FE-145)은 같은 점선 도장에 글자만 "점검 중". **순차 해금으로 아직 열리지 않은 사건(locked, FE-149 · BE-146)은 같은 점선 도장에 글자만 "잠김"**이고, 도장 밑에 이유 한 줄("다른 사건을 마치면 새 사건이 차례로 열려요.", .paper-start-lock, 글자 --text-small 차분한 색, 상자 없음 — 사건별 열리는 시점은 서버가 주지 않아 건수를 약속하지 않는다)을 둔다. 잠긴 사건은 대본을 받지 않고 지면만 읽는다
제목 칸 아래 왼쪽 빈 자리 (FE-119) 비율: 제목 칸 폭을 반반 두 단(720px 이하 한 단, FE-155), 이중선 머리 라벨 + 본문 13px 명조. 왼쪽 "알아두기"(범죄 유형별 일반 법률 설명, content/typeNotes.js. 안내 대상 민감 플래그가 있는 사건은 그 아래에 점선으로 나눠 굵은 한 줄 "이 사건에는 …과 관련된 내용이 나와요." — FE-129, content/sensitiveNotice.js), 오른쪽 "같은 유형의 다른 사건" 둘(제목 버튼 — 한 줄만, 길면 …, 누르면 그 사건 정보. 다른 사건이 없으면 비운다) 흐름(격자 2행 왼쪽 칸, 도장과 같은 행)
민감 사건 안내 (FE-129) 2막 첫 대사보다 먼저 나오는 안내만의 장면 하나(이어 보기로 ③ · ④에서 열면 그 시작 장면 앞, 빈 무대, 캐릭터 · 대사 없음, 가운데 시스템 지문 .sysline). 사건 상세의 "알아두기" 칸에서는 일반 설명을 대신해 같은 한 줄만 보인다(둘 중 하나). 굵게, 색은 기존 지문과 같다. 안내 대상 플래그가 없으면 나오지 않는다. 스크린 리더는 대사 없는 장면이라 무대의 알림 영역이 안내를 읽는다(지문 영역에는 aria-live를 두지 않는다) 무대 안 오버레이 · 알아두기 칸
엽서 내려놓기 (FE-140) 1막 첫인상 엽서(신문 지면 무대의 입력 카드)가 지면을 가려 사건 정보를 못 보던 문제. 누르면 엽서만 숨기고(쓰던 내용은 그대로) 뒤 어둠 · 지면 비활성(inert)이 풀려 지면을 읽고 굴릴 수 있다. 엽서가 새로 뜰 때는 늘 펼친 채 시작하고, 다시 펼치면 엽서에 포커스가 돌아온다. 엽서의 최대 높이는 100dvh − 190px. 모바일(732px 이하): 엽서 아래 가운데 알약 버튼 "엽서 내려놓고 사건 보기 ▾" / "엽서 다시 펼치기 ▴"(.postcard-rest, aria-expanded, 조작 줄 바로 위 bottom: 66px. 상태 문구가 있어 조작 줄이 두 줄이면 90px로 올려 문구를 덮지 않는다, FE-155). 넓은 화면(733px 이상, 엽서 폭 700px + 양옆 16px): 열려 있을 땐 엽서 왼쪽 위 ✕ 박스(.postcard-close, 30px, 진한 바탕 --color-heading + 흰 글씨라 작아도 눈에 띄고 "첫인상" 제목을 가리지 않는다)로 내려놓고, 엽서 바깥(어두운 배경, 이 자리만 손 모양 커서)을 눌러도 내려놓는다. 내려놓은 뒤에는 엽서를 던진 남불 곁의 작은 엽서(.postcard-mini, 84×57px, 항공우편 줄무늬, 글자 "엽서"는 낭독기용 aria-label 겸 시각 라벨)가 다시 펼친다. 작은 엽서는 남불이 선 쪽의 바깥(오른쪽에 서면 왼쪽, 왼쪽에 서면 오른쪽)에 붙고, 높이는 남불 그림 높이의 38%이며, 창 크기가 바뀌어도 프레임마다 남불 그림을 따라간다(남불 그림 밖 무대 바닥에 두고 z-index: 29로 하단 조작 줄 28보다 위라, 낮은 화면에서 줄과 겹치면 엽서가 줄을 가린다). 엽서를 던지는 인물은 배역과 상관없이 늘 남불이다. 내려놓은 동안 → · Enter는 다음 장면이 아니라 엽서를 다시 펼친다(다음 버튼은 비활성). 엽서에는 role="dialog"만 두고 aria-modal은 뺐다(낭독기가 모달 밖의 내려놓기 버튼에 닿게). 지면 무대(.scene)의 빈 곳은 눌러도 넘어가지 않으니 손 모양 커서를 두지 않는다 고정: 모바일은 화면 아래 가운데 알약, 넓은 화면은 엽서 왼쪽 위 ✕ · 남불 곁 작은 엽서
지면 굴리기 (FE-140) 1막 지면에서 대사를 넘길 때 기사 쪽으로 화면을 굴리는 움직임. 브라우저 기본 behavior: 'smooth'는 거리와 상관없이 짧고 빨라(특히 사파리) "휙휙" 움직였다. 거리에 비례한 시간(420 ~ 1000ms, 거리 × 0.55 + 300)과 처음 · 끝이 느린 곡선(easeInOutCubic)으로 직접 굴린다. 키보드로 넘기면 70% 길이, 도중에 넘기면 지금 자리에서 새 목표로 이어 굴리고(줄 서지 않는다), 휠 · 터치로 굴리면 바로 멈춘다. 움직임 줄이기면 바로 옮긴다 stage.js createGlider
서명하고 제출 스크롤 (FE-140) 3막 판결 확정 장에서 하단 "서명하고 제출"을 누르면 서명란이 카드 · 화면 아래(조작 줄 90px 포함)에 가려져 있을 때 서명란을 가운데로 굴려(최대 0.8초) 서명이 쓰이는 모습이 보이게 한 뒤 서명한다. 이미 보이면 기다리지 않는다. 움직임 줄이기면 바로 옮긴다 verdictPage.js

판결 체험 무대 (1 ~ 6막)

무대는 16 : 9 상자 하나가 기준 좌표계이고, 안쪽은 전부 무대 기준 %(자리)와 cqw(무대 폭의 1%, 글자 · 테두리 · 여백)다. 화면이 바뀌어도 무대 안 비율이 그대로다.

요소 크기 자리
무대(2 ~ 5막) 비율 고정 16 : 9, 폭 100%(문서 틀 최대 1120px · 좌우 20px) 상대: 문서 틀 안 흐름. container-type: inline-size
무대 안 법정 · 판 · 보드 비율: 무대 %(예: 보드 좌우 24% · 위 5% · 아래 27%, 대본 카드 좌우 28% · 높이 38%) 절대: 무대 % 좌표
무대 안 글자 · 테두리 자체 단위: cqw, 글자는 max(최소 px, N cqw)로 하한을 둠(예: max(13px, 1.5cqw)). 읽는 글자의 하한은 12px 이상이다(FE-103: 11px · 10px 하한을 12px로 올림) –
캐릭터 비율: 무대 높이의 62%(판이 뜨면 50%), 4막 판사 셋은 칸 20% · 키 비율 100 / 94.4 / 88.9% 절대: 가운데 기준 --l 16% · --r 84%(판이 뜨면 10% · 90%), 바닥 25%
캐릭터 자리 · 꼬리 (FE-128) 자리는 묶음 기준: 대본 cast(묶음 A 판사 · 검사 = prosecutor, 묶음 B 변호인 · 피고인 = defense)에서 A는 --l, B는 --r. 역할 의상(검사 · 판사 · 변호인)이면 의상 자리, 피고인 역 · 평상복 · 볼따구는 그 캐릭터의 묶음 쪽(data-seat). 말풍선 꼬리 · 이름 딱지 · 지면 다가서기 · 남불 엽서 건네기 방향도 캐릭터 이름이 아니라 서 있는 쪽(data-side · data-seat). 볼따구 가로 자리는 A 40% · B 62%, 높이는 캐릭터 그림마다. 바라보는 방향: 원본 그림은 내로가 오른쪽 · 남불이 왼쪽을 본다(이미지 규칙). 서 있는 곳에서 가운데를 보도록 그림만 좌우로 뒤집는다(data-flip, flipFor) — 왼쪽에 서면 오른쪽을, 오른쪽에 서면 왼쪽을 보고, 슬롯 30은 원본 방향, 피고인석(stand)은 왼쪽에 선 검사 쪽을 보게 한다 — 원본이 오른쪽을 보는 내로는 뒤집혀 꼬리가 오른쪽으로 간다(FE-164, placeFor). 판사 · 볼따구 포함, 이름 딱지는 뒤집지 않는다. 물음표가 든 볼따구 컷(cheek:idle-thinking · cheek-slim:idle-thinking, COMMON-73)은 CSS로 뒤집으면 물음표가 거울 글씨가 되므로 뒤집지 않고 반대 방향으로 따로 그린 그림(*-mirror.webp, facedImage · MIRRORED_LOOKS)을 쓴다. cast가 없으면 기본 배역(내로 A · 남불 B) 절대: 무대 % 좌표
6막 1면 법정 사진 (FE-141) 5막 마지막 법정 그대로: 대본 배역(script.cast)의 묶음 A가 판사로 왼쪽(.sheet-photo-a, 16% · 모바일 25%), 묶음 B가 피고인 역으로 오른쪽(.sheet-photo-b, 84% · 모바일 75%). 무대처럼 가운데를 보게 뒤집는다(img[data-flip], courtPhotoCast). 예전에는 "판사 = 내로 · 피고인 = 남불"로 고정이라 배역이 바뀐 사건에서 5막과 달랐다. 내 결과 지면(FE-75)은 대본이 없어 기본 배역(서버 대본 연동 FE-139 뒤 배역을 넘긴다) 절대: 사진 안 % 좌표
1막 신문 지면 무대(layout paper) 지면은 S-02와 같음 대사창 · 조작 줄 · 캐릭터는 화면 고정(아래). 대사창 폭 --dock-w = min(860px, 100vw − 340px)(양옆 캐릭터 자리 170px씩), 720px 이하 100vw − 20px. 721 ~ 1000px은 버튼 칸이 좁아 버튼은 줄바꿈하지 않고, 상태 문구는 모바일처럼 버튼 위 한 줄로 올리며 개발용 "이름표 끄기"는 뺀다(6막 조작 줄도 같음, FE-155)
1막 독자 엽서 팝업 px 상한 + 화면 비율 화면 고정: 가운데(46%)
사건 기사 지면 머리 · 목차 (S-20 · 1막, FE-155) 머리(귀 · 제호) · 제목 칸 · 본문 단은 720px 이하 한 단(전에는 768px, S-02 지면과 같은 분기점). 신문 목차(pageIndex, 사건 정보 · 1막 · 신문 성향 테스트 공용)는 가로로 굴러가는 줄이라, 그릴 때 지금 면을 줄 가운데로 가로만 굴린다(scrollLeft, 세로 이동 없음 — scrollIntoView는 쓰지 않는다). 줄이 넘치지 않으면 그대로 흐름
6막 다음 날 신문(화면 전체 카메라) 무대 = 헤더 아래 화면 전체(fixed), 지면은 자체 단위 --u = 무대 1cqw, 지면 폭 118u 지면은 카메라 변환(절대 위치 + transform)
6막 읽기 지면 · 성향 신문지 · 내 결과 지면 자체 단위: --u = min(10px, (100vw − 32px) / 118), 지면 폭 118u(최대 1180px). 성향 신문지 720px 이하 --u 6px · 6막 읽기 지면 720px 이하 폭 100vw − 16px. 내 결과 글자만 --u × 1.12 상대: 가운데. 넘긴 뒷면이 같은 폭 · 자리에 이어진다
낮은 화면 무대 폭 (2 ~ 5막, FE-97) 상대: 폭 = min(100%, (100dvh − 270px) × 16 / 9)(--stage-w), 조작 줄도 같은 폭. 270px = 사이트 헤더 + 사건 상단 줄 + 막 탭(약 195px) + 조작 줄 흐름: 가운데. 1366 × 768에서 무대 약 891px, 1440 × 900 이상은 그대로 1120px
모바일 볼따구 (2 ~ 5막, ≤ 720px, FE-155) 자체 단위: 높이 50cqw(장면 폭 기준). 무대 높이 %로 잡으면 보드 길이만큼 무대가 늘어 볼따구가 커지고 보드 글자를 덮었다 절대: 장면(폭 × 3/4) 기준 — 보드가 없으면 대사창 위에 걸치고(top: 25cqw + 34px), 보드가 있으면 장면 바닥에 선다(25cqw + 6px). 남불은 1.2cqw 아래
모바일 대사창 · 조작 줄 (2 ~ 5막, ≤ 720px, FE-97 · FE-155) 상대: 폭 100%, 조작 줄 높이 약 52px(상태 문구가 있으면 한 줄 더, 약 82px) 화면 고정에 가까운 sticky: 조작 줄 bottom: 6px, 대사창은 그 바로 위(62px · 상태 문구가 있으면 96px). 보드가 그 밑으로 지나가고 막 끝에서는 원래 자리로 돌아온다. 6막 신문의 대사창은 신문 초점과 겹쳐 흐름에 둔다
모바일 1막 엽서 (≤ 720px, FE-97 · FE-155) 비율: 윗변은 사이트 헤더(약 113px) + 8px 아래, 높이는 100dvh − 윗변 − 98px(조작 줄 위까지, 상태 문구가 있어 조작 줄이 두 줄이면 122px — 알약 버튼과 함께 24px 올린다), 안쪽만 스크롤. 고른 형량 칸은 메모지색 바탕 + --color-heading 글자 화면 고정: 가운데 정렬(top: 46%) 대신 헤더 아래에서 시작
6막 모바일 신문 (≤ 720px, FE-97 · FE-155) 자체 단위: --u = 1.3cqw(데스크톱 1cqw), 카메라 확대 하한 없음(기사 폭에 맞춤). 글자 하한 12px(제호 정보 · 날짜 줄 · 표 머리 · 등장인물 이름, 전에는 10 · 11px) 카메라: 기사가 화면 폭에 들어온다. 예전에는 1.3배로 키워 기사 오른쪽이 화면 밖으로 잘렸다

판결 체험 무대 조작 보조 (FE-103)

  • 장면 순서 표시: 대사창 오른쪽 아래 안내가 "4 / 11 · ▶ 클릭 · Space"처럼 이 화면에서 지금 몇 번째 장면인지 함께 보인다(stage.js hint). 조작 줄의 상태 문구(.sp)에 넣지 않는다 — 비어 있지 않으면 모바일 조작 줄이 두 줄이 된다.
  • 막힌 이유: 다음으로 못 넘어가는 이유(3막 "확정 장으로 가려면 …")는 .sp.is-blocked로 주의 색(--color-caution) 14px 굵게 보인다. 입력 카드가 stageNext.blocked()로 알린다.
  • 3막 감경: 형벌을 고르기 전에는 "형벌 종류를 먼저 고르면 감경 여부를 정할 수 있어요."로 이유를 밝히고, 감경 후 형벌 라디오는 "감경해서 선고하기"를 체크하기 전까지 감춘다(회색 빈 칸이 고장 난 조작처럼 읽혔다).
  • 숫자 칸 Enter: 년 · 개월 · 벌금 칸에서 Enter는 무대 "다음 장"과 같다(개월은 12개월 넘김을 먼저 적용). 무대는 입력칸 위의 키를 받지 않는다.
  • 남불 빼꼼 마스코트(.peek.b, 3막): 높이 64px. 84px일 때는 머리가 위 "사건 기록 다시 보기" 링크를 가렸다.
  • 엽서 팝업: 열리면 팝업 자체가 아니라 내용이 준비된 뒤 첫 입력칸(별점)으로 포커스가 간다. 보낸 엽서처럼 입력칸이 없으면 팝업에 그대로 둔다.

판결 체험 문서 화면 (사건 검토 · 판결 · AI 판결 · 재판부 · 비교 · 개요)

요소 크기 자리
문서 틀 px 상한 1180px(무대가 있으면 안쪽 1120px) 상대: 가운데
2단 · 3단 격자 비율 격자 + px 고정 옆 칸: 검토 1fr · 260px, AI 판결 300px · 1fr, 주장 · 판단 요소 2열, 세 판결 3열. 760px 이하 모두 한 단 흐름
판결 파일철 말린 모서리 px 고정 64px(공용 종이 귀, 글자 없는 귀) 절대: 종이 오른쪽 아래
진행 책갈피 px 고정 폭 28px, 길이만 화면별(판결 60px · 성향 36px, 공용 folderParts.css) 상대: 탭 줄 끝에 끼움(음수 여백으로 철 위에 걸침)

신문 성향 테스트 (/test/paper, FE-55 · FE-122)

신문 10면 성향테스트(오피니언 면). 6막 · 사건 목록 · 사건 정보 · 지면 목차의 "성향 테스트" 입구가 모두 이 면으로 온다. 껍데기는 사건 목록 · 사건 정보 신문과 같다(루트 case-list-page, 지면 paper inner-page, 새 색 없음).

요소 크기 자리
책상 · 종이 · 안쪽 면 사건 목록과 같다: 지면 min(1280px, 100vw − 48px), 위 32px, 안쪽 여백 40px, 종이 --color-paper, 펠트 책상 위, 왼쪽 제본 그늘(.inner-page). 최소 높이는 사건 목록 신문과 같다(max(100dvh + 120px, 신문 폭 × 1.4), FE-126 — 면을 넘길 때 두 지면 높이가 달라 보이지 않게 맞췄다. 옛 "화면 높이 × 1.2"는 폐기) 상대: 가운데
머리글 · 지면 목차 사건 정보와 같은 함수(innerPageHead · pageIndex): 왼쪽 귀 "← 이전으로", 오른쪽 귀 "메인으로", 작은 제호 + "오피니언면 · 오늘 날짜 · 10면", 목차는 10면 성향테스트가 지금 면(밑줄, 누르지 않음). 목차는 1면 · 3면 사회 · 8면 색인 · 10면 성향테스트 · 12면 오피니언(FE-126)이고, 이 면에서는 3면 사회가 갈 곳이 없어 글자만 둔다. 다른 면으로 갈 때 지면을 넘긴다(아래 "신문 면 넘기기") 흐름
본문 글자 단위 --u = min(10px, (100vw − 128px) / 120)(지면 안쪽 폭이 줄면 같이 줄어든다), 720px 이하 6px 흐름
법적 고지 지면 맨 아래 줄(다른 신문 지면과 같다, 라우터의 바깥 푸터는 ownsFooter로 끔) 흐름
  • 책상 배경(.desk, position: fixed)이 잘리지 않게 루트에 perspective · overflow: clip을 두지 않는다. 신문을 넘겨 들어오는 연출(tp-turn-in)의 원근은 지면 자신의 변환에 넣고, 끝난 뒤 변환을 남기지 않는다(animation-fill-mode: backwards — both면 남은 perspective가 좁은 화면에서 쪽을 가로로 늘렸다, FE-156).
  • 좁은 화면(≤ 720px, FE-156): 지면 목차는 가로로 미는 줄이다(들어올 때 지금 면(10면)을 줄 가운데로 미는 것은 공용 pageIndex가 한다, FE-155). 결과 지면의 보조 버튼(공유 · 마이페이지 · 다시 하기)은 서류철 결과지처럼 한 줄씩 쌓는다. 문항 화면은 한 화면에 들어오게 진행자 한마디를 12px로 줄인 그대로 둔다.
  • 문항 문구(FE-126): 이 지면만 "이번 주 사건사고 제보" 단신 문구로 싣는다(content/paperQuestions.js). 문항 id · 축 · A/B 방향은 그대로라 점수 뜻이 같고, 서버에는 문항 id로 저장된다. 일반 테스트(/test)는 원래 문구 그대로다.
  • 결과 지면(/test/paper/result, FE-126): 신문 테스트를 마치면 온다. 내용은 /test/result와 같고(부품 공용) 모양만 신문이다: 큰 제목 = 유형 이름 → 캐릭터 사진 | 4축 · 관용 지수 → 두 단 해설 + 다른 캐릭터 · 안내 → 다음 행동. 지면 목차 · 이전으로도 같은 넘김을 쓴다. 900px대 이하 한 단. 일반 테스트의 서류철 결과(/test/result)는 그대로 남는다.
  • 문항 화면 본문(찬반 토론 A | B · 독자 설문 응모권 · 진행 칸)은 기존 구조 그대로이고 색 토큰만 신문 지면색(--color-paper · --color-text · --color-muted)으로 맞췄다(옛 6막 지면색 --color-desk-news* 제거).

성향 테스트 (S-15 · S-16 · 공유)

요소 크기 자리
서류철 721px 이상 px 고정(실제 서류철처럼): 세운 철 840px · 눕힌 철 1040px(넘치면 100%). 눕힌 철 최소 높이는 A4 가로 비율(폭 × 210 / 297). 720px 이하 min(980px, 100%) 상대: 가운데. 페이지 여백 64px 24px
안내 캐릭터 컷 px 고정 높이 180px, 비율 1122 : 1402 화면 고정: sticky(위 24px)
색인 탭 px 고정 최소 76 × 40px 상대: 철 위 가장자리, 10px 겹침
표지 글 px 상한 620px 상대: 가운데

마이페이지 (S-17)

요소 크기 자리
본문 px 상한 760px(내 정보 수정 640px, 입력칸 320px) 상대: 가운데
위쪽 카드 줄 격자: 카드 200px · 가운데 1fr · 카드 200px 흐름(좌우 대칭)
프로필 · 성향 카드 px 고정 폭 200px + 비율 7 : 14. 카드 안 글자 · 책갈피는 자체 단위 cqi(카드 안쪽 폭의 1%) 흐름
겹친 성향카드 px 고정: 카드 200px, 높이 400px, 어긋남 --fan-step 56px(380px 이하 36px) 절대: x만 어긋나게 겹침
컬렉션 카드 격자 minmax(150px, 1fr)(720px 이하 2열), 비율 7 : 12. "새로 얻음" 알약은 그림 칸 왼쪽 위 8px 흐름 · 알약은 절대
성향 변경 카드 세 장(초심 → 재심 → 최근) 721px 이상 min(200px, (100vw − 168px) / 3)(768px 이상은 200px, 721 ~ 768px만 줄어 가로로 넘치지 않는다, FE-157). 720px 이하는 세로로 쌓고 160px 흐름(가로 줄 · 좁으면 세로)
대화상자(꾸미기 · 컬렉션 확대) 높이 상한 92dvh(확대 그림 70dvh), 모바일 브라우저 툴바에 아래가 잘리지 않게 화면 고정 층 가운데
작은 조작(뒤로 · 카드 꾸미기 · 꾸미기 알약 · 표로 보기 · 사건 제목 링크) 터치(pointer: coarse)에서 누르는 높이만 44px. 보이는 모양 · 자리는 그대로(늘린 만큼 바깥 여백을 줄이거나 음수 여백으로 상쇄, FE-157) –

커뮤니티 (S-18)

요소 크기 자리
화면 틀 격자: 220px · 1fr, px 상한 1400px(1023px 이하 배치 변경) 상대: 가운데
왼쪽 거르기 칸 px 고정 220px 화면 고정: sticky(헤더 높이 + 14px)
거르기 칸 중간 화면 (FE-165) 721 ~ 1023px: 말머리 칩은 왼쪽, 사건 검색창(.cm-case-search)은 같은 줄 오른쪽(폭 280px 상한 · 200px 하한, 검색창 쪽이 줄어든다). "사건" 글자는 화면에서만 감추고(label은 낭독용으로 남김) placeholder가 말한다. 검색 결과 목록은 줄 높이를 밀지 않게 검색창 아래로 띄우고(position: absolute, 그림자), 사건을 고르면 선택 표시(.cm-case-chosen)가 같은 자리에 들어온다. 1024px 이상(사이드바)과 720px 이하(모바일, 위아래 두 줄)는 그대로 상대: 칩 줄 1fr · 검색창 280px
글 사진 px 상한 560px(한 장 크게 680px), 비율 4 : 3 contain, 한 장은 원래 비율 · 높이 480px(크게 70vh)까지 흐름
글 본문 · 판결 카드 글자 수 상한(본문 68ch) · px 상한(판결 카드 460px) 흐름
메인 슬라이드 (FE-89) 격자: 한 장이 가운데 칸 폭 100%, 기사 조각 1.05fr · 글 1fr(1279px 이하 한 열로 쌓음) 흐름: 장마다 가로 스냅
오려 낸 기사 조각 상대: 내용 길이만큼(옆 글 높이에 맞춰 늘이지 않음), 사진 비율 16 : 9(1023px 이하 높이 240px) 흐름: 장 왼쪽, 기울기 ±.5deg
시상대 카드 (1 ~ 3위) px 상한 168px, 칸 폭이 좁으면 cqi로 줄어듦(1위는 112%), 모바일은 칸 3등분 흐름: 2 · 1 · 3위 순서로 놓음
시상대 단 px 고정 높이 1위 100px · 2위 76px · 3위 60px 흐름: 카드 아래
4 ~ 10위 카드 상대 하한: max(110px, 시상대 카드 폭의 절반). 이보다 작아질 자리면 줄이지 않고 좌우로 넘김 흐름: 화살표 34px 칸 · 카드 줄 · 화살표 칸(카드와 겹치지 않음)
시상대 카드 글자 (FE-93) 상대 하한: 닉네임 · 성향 이름 max(11px, 9.1cqi)(카드가 작아져도 11px 아래로 내려가지 않음). 단 닉네임은 넘치면 11px 아래로도 줄여 다 보인다(FE-120 "닉네임 표시") 흐름
추천 사건 격자: 3단(720px 이하 1단), 단 사이 세로선 흐름
모바일 터치 대상 (FE-158) px 고정: ≤ 720px에서 조작은 모두 44px 이상(필터 · 정렬 칩, 좋아요 · 댓글, 목록으로, 말머리 고르기, 사진 붙이기, 사건 선택 · 검색칸, 판결하기, "그래도 보기" 등). 화살표 .cm-arrow(34px) · 사건 지우기 · 사진 빼기(28px)는 보이는 크기 그대로 ::before로 누르는 영역만 44px(테두리 안쪽 기준이라 테두리 두께만큼 더 뺀다). 글 · 사건 제목과 "모두 보기" 링크는 위아래 padding을 키우고 같은 만큼 음수 margin으로 상쇄해 글자 자리는 그대로 둔다 흐름
카드 안 작은 글자 (FE-158 결정) 시상대 · 4 ~ 10위 · 명함 카드 안 레벨(최소 약 6 ~ 10px) · 성향 한마디(약 9px)는 12px 하한의 예외로 둔다. 카드는 마이페이지에서 꾸민 카드의 축소 복제본이고, 레벨은 카드 밖(단 · 순위 줄 · 명함 정보 줄)에 12px 이상으로 다시 보인다 흐름

로그인 (S-12) · 리뷰 엽서 (S-09) · 관리자 (S-19)

요소 크기 자리
로그인 책상 px 상한 + 100%: min(1080px, 100%), 펠트 패드 모서리 24px 상대: 가운데
404 책상 (S-14, FE-104) px 상한 + 100%: min(560px, 100%), 서류 한 장 모서리 4px · 위 가장자리 크라프트 6px · rotate: -.6deg(≤ 720px는 0) 상대: 가운데, 펜은 서류 오른쪽
리뷰 화면 본문 px 상한 + 100%: min(680px, 100%) 상대: 가운데
날아오는 엽서 창 px 상한 + 100%: min(640px, 100%), 높이 100dvh − 32px까지 화면 고정 층 가운데
관리자 px 상한 1180px, 메뉴 200px · 본문 1fr, 카드는 auto-fit 격자 minmax(140px, 1fr)(넓은 화면 6장 한 줄) · 차트는 minmax(260px, 1fr). 720px 이하는 메뉴가 옆으로 미는 한 줄(grid-template-rows: auto 1fr) 상대: 가운데
관리자 작업 완료 알림줄 px 상한: min(560px, 100vw − 32px), 6초 뒤 사라짐 화면 고정: 아래 24px + env(safe-area-inset-bottom) 가운데(노치 위, FE-159)

관리자 화면 규칙 (S-19, FE-105)

운영 도구라 책상 은유는 쓰지 않고 역할 토큰 · 선 · 면으로만 나눈다.

  • 확인 기준 하나: 되돌릴 수 없거나, 사용자가 보던 것을 거두거나(숨김 · 삭제 · 정지 · 비활성화 · 비공개), 사용자에게 새로 공개하는 작업만 confirmChange(무엇을, 결과)로 묻는다. 확인 문구에는 대상 이름(#번호 · 제목 · 닉네임)을 넣는다. 되돌리는 작업(복구 · 정지 해제 · 다시 사용)과 운영 기록만 바뀌는 작업(기각 · 고정 · 축 확정 · 내용 저장)은 묻지 않는다.
  • 특별 모드 경고 확인(FE-163): 특별 모드로 적재한 사건(공개 조건 SPECIAL_MODE_UNREVIEWED)은 공개 준비 사유 목록 아래에 주의 상자(admin-special-mode, 주의 토큰 면 · 선, role="group")를 둔다. 안에 특별 모드 배지(admin-badge is-pending) · 확인 순서 3단계(ol.admin-special-steps) · 경고를 확인했어요 버튼. 공개 불가 사유 문구와 겹치지 않게 상자에는 행동 안내만 둔다. 운영 기록만 바뀌는 작업이라 confirmChange는 묻지 않고(위 확인 기준), 완료 알림 · 포커스는 공개 제목으로 돌아간다. AI 후보의 검증 경고 목록은 [특별 모드]로 시작하는 줄을 굵게(.is-special) 보여 준다.
  • 완료 알림: 작업이 끝나면 틀의 알림줄(ctx.notify, role="status", 네이비 바탕)에 결과를 한 줄로 띄운다. 화면 맨 위 문구 대신 쓴다(스크롤 위치와 상관없이 보이게).
  • 포커스: 화면에 들어오면 제목(h1), 목록에서 상세를 열면 상세 제목(h2), 사건 상세에서 상태를 바꾸면 다시 그린 뒤 누른 자리(후보 이름 · 판단 요소 줄 · 공개 제목 · 저장 버튼), 신고 처리 뒤에는 다음 신고의 "처리"로 옮긴다. 맨 앞에 "본문으로 건너뛰기"가 있고, 누르면 화면 제목(h1)으로 포커스가 간다(제목이 아직 없으면 본문).
  • 같은 이름 버튼에는 대상: 승인 · 반려는 버튼 글자에 #번호, 처리 · 기각 · 숨기기 · 공개 전환 · 비활성화는 aria-label에 대상을 넣는다.
  • 크기: 입력칸 글자 16px(iOS 확대 방지), 입력칸 도움말(.admin-field small)은 모든 폭에서 12px(--text-caption). 720px 이하 · 손가락 화면(pointer: coarse)에서 버튼 · 고르기 · 메뉴 44px, 체크박스 줄(.admin-checklist label · .admin-check) · 접힘 제목(summary)도 44px 안팎이고, 표 안 제목 링크 · 예시 PDF 링크는 누르는 범위만 넓힌다. 넓은 화면 마우스 버튼은 36px 그대로(FE-105 · FE-159).
  • 높이(모바일): 틀은 100dvh. 720px 이하 · 손가락 화면에서는 틀을 flex 세로(머리줄 + 본문 flex: 1)로 두어, 44px 버튼으로 높아진 머리줄 때문에 짧은 화면이 화면 높이를 넘지 않게 한다. 표는 .admin-table-wrap 안에서 옆으로 밀어 보고, 조작 칸(처리 · 수정)도 가로 스크롤로 닿는다(열 순서 · 카드형 전환은 하지 않음, FE-159).
  • 호버 · 눌림: 호버는 @media (hover: hover) and (pointer: fine) 안에서만. 터치 눌림은 메뉴 · 보기 바꾸기 줄 · 바로 가기는 틴트, 대시보드 카드(button.admin-card)는 테두리 강조, 버튼은 전역 scale .96(FE-159).
  • 보기 바꾸기 줄(tabBar)은 role="group" + aria-pressed(누르면 목록을 다시 불러오는 버튼 묶음이라 탭 역할을 쓰지 않는다). 표는 caption(화면에는 숨김) · th scope="col".
  • 목 모드: 머리줄 로고 옆에 점선 "목데이터" 꼬리표. 실제 모드에서 서버가 없는 메뉴는 점선 "준비 중" 꼬리표.
  • 강조 상자(.admin-warn · .admin-verdict)는 왼쪽 굵은 띠 대신 1px 테두리 + 모서리 4px.
  • 사진 미리보기(.admin-photo img)는 3:2 칸을 고정해 그림이 바뀌어도 아래 내용이 튀지 않게 한다. 대사 줄(.admin-script-line)은 1px 테두리 상자로 묶고, 대사 본문은 본문 색 그대로 두되 옆의 판단 요소 문구는 흐리게 둔다 — 읽을 것과 참고할 것을 나눈다(FE-106).

Elevation & Depth

물건이 놓인 그림자. 깊이는 책상 위 물건이 만든다. 책상 계열(펠트 패드 · 서류철 · 편지 · 책상 물건)은 2단 그림자(가까운 짧은 것 + 먼 부드러운 것)로 놓이고, 일반 화면의 카드와 영역은 그림자 대신 **선(--color-border)과 면 색(--color-surface)**으로 나눈다. 그림자 색은 --color-desk-shadow를 color-mix로 투명하게 만든다.

Shadow Vocabulary

  • 패드 그림자 (inset 0 0 0 3px var(--color-desk-pad-edge), 2px 4px 6px <shadow 35%>, 6px 18px 28px <shadow 30%>): 로그인 펠트 패드. 안쪽 테두리가 펠트 가장자리를 만든다.
  • 카드 들어올림 (translateY(-3px)): 마이페이지 카드. 그림자가 아니라 위치로 띄운다.
  • 그 밖의 그림자는 화면마다 71종 가까이 흩어져 있다(눈금 미정).

Named Rules

The Placed-Object Rule. 그림자는 "물건이 책상 위에 놓였다"는 뜻이다. 일반 카드를 떠 있는 것처럼 보이게 하려고 쓰지 않는다.

The No-Nested-Cards Rule. 카드 안에 카드를 두지 않는다. 구분은 선과 면 색으로 한다.

Shapes

버튼 · 입력칸은 모서리를 작게(4px) 둔다. 헤더 안 계정 버튼은 6px, 목데이터 딱지는 3px(점선 테두리)이다. 책상 위 물건과 펠트 패드는 크게(로그인 패드 24px) 둥글려 물건 느낌을 낸다. 테두리는 1px 괘선이 기본이고, 장식 선은 브론즈다. 모서리 값은 34가지가 쓰이고 있어 눈금이 필요하다(아래 "현재 코드와 차이").

Components

Buttons

공통 버튼은 style.css의 .primary-button · .secondary-button이 기준이고, 화면 CSS 9곳에도 같은 값이 일부러 중복 정의돼 있다(라우터 · 404처럼 화면 CSS를 빌리지 않는 곳에서 쓰려고 style.css에도 있다).

  • Shape: 작게 둥근 모서리(4px), 최소 높이 44px, 패딩 10px 18px, 글자 굵기 700.
  • Primary: 서류 네이비 바탕 + 흰 글자. 호버는 먹빛 네이비.
  • Secondary: 흰 바탕 + 잉크 글자 + 진한 괘선 테두리. 호버는 부드러운 종이. 단 종이(책상 종이 = 부드러운 종이와 같은 색) 위에서는 호버 색이 바탕과 같아 면이 사라져 보이므로, 호버에서 테두리 --color-heading + kraft 16% 틴트로 바꾼다(성향 테스트 .personality-page에 적용. 다른 종이 위 화면은 아직 전역 규칙 그대로).
  • 어두운 면(펠트) 위: 주 버튼은 종이색 바탕 + 펠트색 글자로 뒤집히고, 보조 버튼은 투명 바탕 + 종이색 글자 · 테두리가 된다.
  • 눌림: 전역 scale .96 (.12s, cubic-bezier(.2, .8, .2, 1)).
  • 비활성: opacity .6, 호버 반응 없음.

Navigation (헤더)

  • 먹빛 네이비 띠 위에 로고(22px 900 명조, "Law"는 옅은 놋쇠)와 메뉴. 메뉴 버튼은 평소 안개 네이비 글자, 지금 화면(aria-current="page")은 흰 글자 + 아래 2px 밑줄. 준비 중은 aria-disabled="true" + opacity .6.
  • 로그인 버튼은 글자색 45% 테두리로 계정 동작임을 구분한다. 보이는 테두리는 36px이고, 누르는 범위는 ::after로 위아래 4px씩 넓혀 44px다(FE-99). 작은 화면(720px 이하)에서는 두 줄이 되고 로그인이 윗줄 로고 옆으로 올라간다. Tab 순서가 시각 순서를 따르도록 이때 로그인을 메뉴보다 앞 DOM 자리로 옮기고, 넓은 화면에서는 메뉴 뒤로 되돌린다(keepTabOrderWithLayout, FE-103).
  • 헤더 버튼의 포커스 링은 3px 브론즈(--color-range-fill, offset 2px)로 로그인 패드 · 메인 책상과 같다(FE-104, 기본 1px 링은 네이비 띠에서 얇다).
  • 본문으로 건너뛰기(.skip-link, FE-99): 헤더의 첫 Tab 자리. 평소에는 보이지 않고 포커스가 닿으면 헤더 왼쪽 위에 나타난다. 누르면 주소를 바꾸지 않고 본문(#route-view 안의 <main>, 없으면 #route-view)으로 포커스만 옮기고, 키보드로 눌렀으면 <main> 안쪽 가장자리에 윤곽이 보인다(다른 tabindex="-1" 포커스 자리는 윤곽 없음). 메인은 책상 · 메뉴 카드 · 서비스 소개를 <main>으로 감싼다(푸터는 밖). 책상의 Tab 순서는 신문 → 서류철 → 노트북 → 커피잔 → 펜 → 포스트잇이고, 겹침 순서는 DOM이 아니라 landing.css의 z-index가 정한다.
  • "연출" 스위치(utils/motion.js): 끄면 <html data-motion="off">가 되고 모든 CSS 애니메이션 · 전환 길이가 0이 된다.
  • 마이페이지는 같은 헤더에 커피색만 덮어쓴 밝은 헤더를 쓴다.
  • 헤더의 "연출" 스위치는 환경 설정의 스위치(아래 "스위치")와 같은 규칙이다. 36 × 20 트랙에 손잡이 14, 꺼짐 = 옅은 트랙 + 글자색 윤곽, 켜짐 = 채움 + 손잡이 오른쪽(16px 이동). 켜짐 색은 헤더 바탕에 따라 다르다. 커피 헤더(밝은 면)는 강조색(커피 강조색) 채움 + 밝은 손잡이이고, 네이비 헤더(어두운 면)는 "어두운 면 위" 규칙대로 뒤집어 종이색 채움 + 네이비 손잡이다(꺼짐의 손잡이는 옅은 안개 네이비). 놋쇠(--color-decor)는 장식 선 · 로고 전용이라 스위치에 쓰지 않는다.

글자 링크

호버에서 밑줄(text-underline-offset: 3px), 평소 밑줄 없음, 글자색은 그대로. 임시 확정.

호버 · 눌림 (결정 7패턴)

기준은 "밝아짐"이 아니라 요소 종류별 패턴이다. 비교 페이지: docs/decisions/FE-76-hover-compare.html.

패턴 규칙 대표 요소
P1 버튼 배경색만 한 단계 변함. 크기 · 위치 그대로. 방향은 배경에서 멀어지는 쪽(흰 면 위는 어둡게, 펠트 · 어두운 면 위는 밝게). 눌림은 전역 scale .96(전역 목록에 없는 카카오 버튼 · 좁은 화면 메인 메뉴 카드는 화면 CSS에서 같은 값, FE-153) 공통 버튼 · 로그인 박스 · 카카오 버튼 · 신문 면 넘김 화살표 .face-edge(종이색 → kraft 16% 틴트)
P2 면 단위 항목 틴트 kraft 16%(color-mix(in srgb, var(--color-desk-kraft) 16%, transparent)). 테두리 · 글자 그대로 .cm-filter · .cm-action · .cm-attach · .my-row · .admin-menu-item · .is-pickable · 지면 귀
P3 선택지 테두리 --color-heading + 틴트 kraft 16%, 둘이 함께 바뀜. 성향 테스트 선택지(.answer-cell · .test-option · .tp-option)는 터치에서 누르는 동안(:active) 같은 모양을 보인다(FE-156) .range-option · .answer-cell · .test-option · .tp-option · button.admin-card
P4 글자 링크 (임시) 호버에서 밑줄(3px), 평소 밑줄 없음 .paper-link · .case-review-back · .cm-post-title a 등
P5 헤더 메뉴 글자색만 변함(--color-banner-text). 마이페이지 커피 헤더는 --color-text(스코프 테마 예외) .nav-button
P6 비활성 호버 반응 없음, 커서 그대로. opacity .6 통일을 제안 .is-soon · [aria-disabled] · :disabled
P7 예외 연출 의도된 예외. 아래 표

공통 규칙: 틴트는 뒤에 선이나 그림이 비치면 안 되는 요소(예: 성향 테스트의 .test-dot, 뒤에 가로선)에서는 투명(transparent) 대신 바탕색 위에 섞는다(color-mix(…, var(--color-desk-sheet))). 호버는 @media (hover: hover) and (pointer: fine) 안에서만 쓴다. 예외 연출은 prefers-reduced-motion: reduce에서 끈다. 전환 길이는 .12s ~ .2s, ease-out이 기본이고 예외 연출만 cubic-bezier(.2, .8, .2, 1).

P7 예외 연출 (물건의 은유를 지키려는 의도된 예외)

연출 동작 위치
책상 물건 brightness 1.06 ~ 1.13 + 그림자 변화 landing.css .desk-object:hover > *
펜 → 엽서 연동 펜에 올리면 엽서도 반응. 키보드용 :focus-visible 짝 있음 landing.css
카드 들어올림 translateY(-3px), 눌리면 1px. 미리보기 · 상세에서는 끔 myPage.css .my-card
6막 엽서(옛 8막 편지) translate -0.4u + 그림자 stage.css
폴더 탭 눌림 translateY(1px). 성향 결과 색인 탭은 터치에서 누르는 동안(:active)도 같다(FE-156) personality.css · verdict.css
별 확대 scale 1.12 caseReview.css
판결 입력 하단 버튼 무대의 "다음" 버튼이 장을 넘기고(다음 장 ▶) 마지막 장에서는 서명하고 제출 ▶로 바뀐다. 서명 = 제출. 카드가 layout.stageNext로 라벨 · 안내 · 활성 여부를 넘기고 stagenextchange 이벤트로 알린다 stage.js · verdictPage.js
겹친 카드 앞뒤 바꾸기(성향 공유) 같은 크기 카드 두 장(내로 앞 · 남불 뒤)을 겹쳐 놓고, 뒤 카드는 오른쪽 아래(--peek-x 50px · --peek-y 22px)로 비스듬히(5deg) 삐져나온다. 앞 · 뒤 어느 카드를 눌러도 앞뒤가 바뀐다(0.36s, z-index도 전환). 호버는 뒤 카드만 6px 더 나온다. 키보드 · 낭독기용 투명 버튼이 뒤 카드를 덮는다 personality.css .result-portrait-set · personalityParts.js portraitPair
철 돌리기 성향 테스트 검사지(가로) ↔ 결과지(세로). 제출 후 결과지는 눕힌 철을 돌려 세우고, 공유 화면의 "나도 해 보기"는 세운 철을 눕힌다. 떠나던 철의 자리 · 크기 · 스크롤 위치를 기억해 눈에 보이던 자리에서 시작한다(folder-turn, 1s). 모바일(≤ 720px)과 연출 끄기 · 동작 줄이기에서는 건너뛴다 personality.css · personalityParts.js canTurnFolder
서류철 올라오기 (FE-50) 메인 책상의 서류철(좁은 화면은 메뉴의 "판결 성향 테스트")을 누르면 검사지 철이 화면 아래에서 세워진 채(90°) 올라오며 눕고(folder-rise 1.15s), 뒤에 남긴 책상은 흐려진다. 헤더 · 서비스 소개로 오면 연출 없이 바로. 모바일(≤ 720px)에서도 돈다(철 돌리기와 달리 건너뛰지 않기로 확정, FE-156). 쪽은 .is-test의 overflow: clip으로 가로로 늘지 않는다. 동작 줄이기에서는 끈다 personality.css · personalityParts.js renderFolder
선고 도장(4 · 5막, FE-97) 판결문이 놓이고 .45s 뒤 주문의 형량(4막 .vp-penalty · 5막 .cp-penalty, 같은 크기 max(22px, 3cqw))이 도장처럼 찍힌다(penalty-stamp .55s: 1.35배 · -3deg → .96 → 제자리). AI · 실제 판결이 같은 무게로 공개되게 같은 움직임. 4막 합의 대기 장면("AI 판사들이 의견을 맞추고 있어요")은 1.1초 stage.css · aiPage.js
판결 접수 도장(3막, FE-97) 서명 획(.9s) → 확정이 되면 서명 끝에 "판결 접수" 도장(겹줄 원 · 강조색, 6막 엽서 "접수 완료" 소인과 같은 모양)이 찍히고(filed-stamp .42s) .9초 뒤 AI 판결로 넘어간다. 화면 읽기 프로그램에는 "판결이 접수되었어요"를 알린다 verdict.css · verdictPage.js
결과 공개(성향 결과, FE-95) 검사지를 막 제출했을 때만: 철이 선 뒤(--reveal-at, 철을 안 돌리면 .15s) 카드가 꽂히고 → 유형 이름이 도장처럼 찍힌다(+.35s, name-stamp .55s: 1.3배 · -3deg에서 내려와 .97로 눌렸다 제자리) → 한 줄 설명(+.8s) → 나머지(+1s, reveal-up 8px) → 4축 막대를 가운데 눈금에서 양쪽으로 긋는다(+1.15s, 줄마다 90ms, axis-draw clip-path: inset(0 50%) → inset(0 0); none으로 끝내면 보간되지 않는다). 결과 주소로 바로 오거나 탭을 오가면 다시 하지 않는다(다 끝나면 .is-revealing을 뗀다). 모바일은 철 돌리기 없이 곧바로, 동작 줄이기 · 연출 끄기에서는 끔. 공개 중(.is-revealing)에는 루트에 overflow-x: clip을 둬 1.3배 도장이 좁은 화면을 가로로 밀지 않게 한다(FE-156) personality.css · personalityResultPage.js
종이 귀 들썩임(판결 체험 말린 모서리 · 성향 결과 장 넘김, FE-96) 공용 부품 하나(paperFold). 메인 신문 모서리 넘김(FE-99)과 같은 방식으로 접힌 변 길이 하나만 움직인다(@property --fold-lift): 잘려 나간 자리(서류철 크라프트)와 뒤집힌 뒷면이 같은 점을 써서 귀를 통째로 키우거나 돌리지 않고 종이가 실제로 더 접혔다 펴진다. 접힌 변은 모서리 쪽으로 볼록한 곡선, 뒷면은 접힌 선에서 어둡고 → 밝고 → 끝에서 약간 어두운 띠. 3.2초마다 휙 들렸다(1.4배) 망설이고 한 번 더 들썩인 뒤 떨어져 살짝 튀고 가라앉는다(약 45% 쉼). 호버 · 포커스에서 더 접히고(--fold-hand 1.1, .2s 전환), 누르면 꾹 눌려 납작해진다(.85). 들썩임과 곱해져 서로 끊지 않는다. 앞 장 귀(.is-flipped)는 그림을 왼쪽 아래 꼭짓점에 붙여 좌우로 뒤집는다. 신문 면 넘김 귀(FE-118)는 들린 밑으로 서류철이 아니라 다음 장 신문이 비치므로 잘린 자리를 --color-desk-news-sheet-1로 바꾸고, 호버에서 글자에 밑줄이 생긴다. 움직임 줄이기에서는 쉬는 모양 그대로(호버 반응은 바로). @property를 모르는 브라우저(사파리 16.4 미만 등)도 쉬는 모양 그대로(main.js가 <html>에 단 .has-css-property가 있을 때만 들썩임 · 전환을 켠다, FE-110) folderParts.css .paper-fold
이 면 더 보기 펼침 (FE-119) 점선 줄을 누르면 같은 종이가 아래로 길어진다(grid-template-rows 0fr → 1fr .55s cubic-bezier(.22, 1, .36, 1)). 남은 기사는 .12s 뒤 .4s에 걸쳐 떠오르며 나타나고(opacity + 14px), 화살표가 180도 돈다. 닫힌 동안은 visibility: hidden이라 키보드 포커스를 받지 않는다. 움직임 줄이기에서는 바로 caseList.css .more-fold
시작하기 도장 (FE-119) 평소 -5도로 비스듬히 찍혀 있고, 호버(마우스)에서 기울기는 그대로(-5도)이고 바탕만 서류 네이비로 바뀐다(.18s, P1). 눌림은 전역 scale .96. 준비 중 도장은 호버 반응 없음(P6) paperStage.css .stamp-button
신문 면 넘기기 (FE-32 · FE-118) 지면을 본뜬 한 장이 왼쪽 가장자리를 축으로 넘어간다(면 넘김 0.5s). 방향이 있다: 뒤 면으로는 오른쪽 끝이 들려 왼쪽으로 넘어가고, 앞 면으로는 앞 면이 왼쪽에서 넘어와 지금 면을 덮는다(거울 곡선). 탭 · 양옆 화살표 · ← → · 손가락 밀기가 모두 같은 넘김이고(종이 귀는 FE-119에서 뺐다), 탭이 아닌 조작으로 넘기면 새 면은 맨 위부터. 모바일(≤ 720px) 면 탭 줄(가로 스크롤)에서 시작한 밀기는 면을 넘기지 않고 줄만 움직인다(FE-154). 양옆 화살표는 721px 이상에서만 보인다. 움직임 줄이기면 지금 면이 0.2s 흐려지며 바뀐다. 다른 화면으로 넘기기(FE-126, turnPageAcross): 신문 성향 테스트 지면(문항 · 결과)과 사건 정보 지면(S-20 · 1막 무대, caseNewspaper)에서 목차 · "← 이전으로" · "← 다른 사건 보기"로 다른 신문 면으로 갈 때도 같은 넘김(0.5s)을 쓴다(3면에서 10면 성향 테스트로 가는 것도 뒤 쪽 면이라 지금 지면이 넘어가며 10면이 드러난다. 이때는 도착 지면이 스스로 넘겨 들어오지 않게 from을 뺀다). 뒤 쪽 면(12면 오피니언)은 지금 지면이 넘어가며 밑에 갈 화면이 그려지고, 앞 쪽 면(1면 · 8면 · 이전으로)은 누르자마자 한 장이 왼쪽에서 뒷면(괘선 종이)부터 넘어오는 동안 갈 지면을 불러와 그리고, 넘김 40% 지점(앞면이 비치기 시작할 때)에 그 지면을 본떠 앞면에 붙인다(그때까지 못 그렸으면 그 자리에서 멈췄다 이어 넘김, 사진은 미리 해독해 프레임이 끊기지 않게). 갈 곳이 신문 지면이 아니거나 2.5초 안에 못 그리면 넘기지 않고 흐려지며 걷힌다. 넘기는 동안 다시 눌러도 한 번만 간다. 넘김 덮개는 공통 헤더 아래만 덮어(clip-path, --site-header-h) 내비가 가려지지 않고, 사건 목록의 면 넘기기도 같다 pageTurn.js(leaveByTurn) · caseListPage.js
기사 → 사건 제목 확대 (FE-118) 누른 기사의 제목이 사건 정보(S-20) 큰 제목 자리로 커지며 날아간다(공유 요소 전환, .62s cubic-bezier(.65, 0, .35, 1)). 날아가는 글자는 새 제목과 같은 글꼴 · 폭 · 줄바꿈이라 내려앉는 순간 진짜 제목과 바뀌어도 티가 안 나고, 처음 18%는 원래 제목과 겹쳐 바꾼다(줄바꿈이 달라서). 본뜬 신문의 내용이 .4s 다 사라진 뒤 새 지면의 내용이 .4s 페이드인한다(겹쳐 보이면 눈이 아프다는 피드백). 그 사이에는 책상이 아니라 새 지면의 빈 종이가 보인다(종이는 처음부터 놓여 있고 내용만 나타난다). 제목은 그 위에서 계속 날아간다. 연출 층은 공통 헤더 아래만 덮어 내비가 가려지지 않는다. 화면 이동과 함께 맨 위로 스크롤되고, 가운데 정렬 배너는 가운데끼리 맞춘다. 면 넘김(넘기기)과 사건 진입(확대)을 다른 움직임으로 나눈다. 움직임 줄이기면 없음 articleZoom.js · caseDetailPage.js
고리 강조 테두리 고리 box-shadow courtSketch.css
바닥 안내문 들썩임 · 치우기 종이 왼쪽 위 귀퉁이가 늘 들썩이고(호버 시 더 크게), 누르면 아래로 끌려가 치워져 밑의 남불이 드러남 landing.css .room-note
신문 끝자락 들썩임 호버 시 벽선 왼쪽 끝을 축으로 오른쪽 귀퉁이를 1.5도 틀어 올렸다 내림(1.5s 반복, 올라갈 때 빠르게) landing.css tail-tug
러그 귀퉁이 들썩임 · 접기 호버(귀퉁이 · 삐져나온 서류철) 시 대각선 축으로 55도 들렸다 내려앉음. 누르면 0.6s에 걸쳐 180도 접혀 밑의 서류철이 드러나고 다시 누르면 덮음(들썩이던 각도에서 이어 감, 원근 없음). 접기 전에는 서류철을 눌러도 접힘, 접힌 뒤 서류철은 성향 테스트 landing.css .rug-flap · landingPage.js
좋아요 눌림 아이콘 scale .86 community.css
내 글 삭제 (FE-165) 글 상세 좋아요 줄 오른쪽 끝에 내 글(post.mine)일 때만 "삭제"(.cm-action, 휴지통 아이콘). 누르면 바로 지우지 않고 같은 자리에서 확인 상자(.cm-delete-confirm: "이 글과 댓글, 좋아요가 모두 사라져요. 되돌릴 수 없어요." + 삭제하기 · 취소)로 바뀌고 **포커스는 안전한 "취소"**에 둔다(취소 · Esc는 삭제 버튼으로 복귀). 삭제하기는 --color-danger 윤곽선 버튼, 호버 · :active는 danger 채움. 성공하면 목록으로 기록을 교체해 이동(뒤로 가기로 지운 글에 돌아오지 않는다), 실패는 role="alert" 한 줄로 이유를 알리고 확인 상자를 닫는다. 모바일은 44px 이상 communityParts.js deleteControl · community.css
전역 버튼 눌림 scale .96. 신문 면 탭 · 색인 종류 · 쪽 번호 · 이 면 더 보기도 같은 눌림(FE-154) style.css · caseList.css
차트 점 툴팁 JS pointerenter(마우스) · pointerdown(터치) myPage.js
브라우저 기본 툴팁 title 속성 3곳 recommendedRangeBar.js 2 · stage.js 1

포커스

키보드 포커스 링은 outline: 3px solid var(--color-focus)(37곳, 사실상 통일). 어두운 면 위에서는 옅은 놋쇠(--color-range-fill). 스크롤 영역(.scroll-body)도 키보드로 포커스되며 링이 보인다.

스크롤 영역

모든 스크롤 영역은 components/scrollArea.js를 쓴다. 기본 막대는 숨기고, 글 위에 겹치는 반투명 막대(5px)를 스크롤하는 동안만 보여 준다.

불러오는 중 · 오류 · 빈 상태

.message-panel(패딩 44px 28px, 본문 15px/1.75). 오류에는 다시 시도하는 버튼을 둔다. 404(S-14, FE-104)는 로그인과 같은 나뭇결 책상(loginDesk) 위의 "찾지 못한 서류" 한 장이다. 내로 · 남불이 서류 뒤에서 고개를 내미는데, 없는 사건 주소는 미안한 얼굴(cheek/idle-sorry), 그 밖의 잘못된 주소는 놀란 얼굴(cheek/idle-surprised)이다. 제목 · 안내 · 찾은 주소(크라프트 꼬리표) · 버튼 두 개(사건 목록으로 · 처음으로)를 둔다. 없는 사건(/cases/…, /me/cases/…)은 "사건 기록을 찾지 못했어요", 그 밖의 주소는 "이 주소의 서류가 없어요"로 나눈다. 화면 전체가 바뀌는 것이라 role=alert를 두지 않고, 콜백처럼 상태가 바뀌는 곳은 안내 한 줄에만 role=status · alert를 둔다(카드 전체에 두면 버튼까지 낭독된다). AI 판결 로딩 문구는 "AI 판결을 기다리는 중"처럼 생성 · 불러오기 양쪽으로 읽히게 쓴다(생성 중 · 계산 중처럼 단정하지 않는다).

성향 기준점 용어 (마이페이지)

초심(첫 성향 테스트) · 재심(성향카드 다시 정하기, 1회) · 최근(최근 체험 3건 평균). 그래프 가로축에 들어가므로 두 글자로 맞췄다. 버튼 이름은 "재심 청구하기".

결재 도장 (.my-stamp, myPage.css)

고른 카드에 "공개"를 찍는다. 잉크는 네이비(--color-accent 80%)이고 빨간 인주는 쓰지 않는다. 모양은 잉크 도장 이미지(mark/stamp-ink-flat.webp, 이중선 · 번진 잉크 질감, FE-150)를 늘여 쓰고, rotate: -12deg, 명조 900 · --text-title-3, 카드 오른쪽 아래. 메인 러그 위 판결문의 "확정" 도장(.sheet-seal)도 같은 그림이다. 장식이라 aria-hidden이고, 공개 여부는 버튼 이름(aria-label · aria-pressed)이 읽어 준다.

점선 빈 칸 버튼 (.my-retake-slot)

아직 없는 카드 자리를 카드와 같은 크기의 점선 칸으로 두고, 칸 전체를 "다음 행동" 버튼으로 쓴다(첫 테스트 하기 · 재심 청구 · 판결 체험하러 가기). 2px dashed --color-border-strong, 모서리 14px(카드와 같음), 가운데에 + 아이콘 · 제목 · 보조 문구. 호버는 P1(배경 --color-surface), 비활성은 opacity .6. 720px 이하에서 세로로 쌓이면 카드 비율(7:14)을 따르지 않고 가로로 눕혀(min(100%, 280px), 높이 약 125px) 칸 하나가 화면 한 장을 차지하지 않게 한다(FE-103).

성향카드 없음 (.edit-fan-empty, 내 정보 수정)

성향카드가 하나도 없으면 겹친 카드 자리에 카드 크기(너비 200px, 높이 100%)의 점선 칸을 둔다. 2px dashed --color-border-strong, 모서리 14px, 안내 글 "아직 성향카드가 없어요. 첫 성향 테스트를 하면 여기에 생겨요." 누르는 칸이 아니라서 호버는 없다.

로그인 전 마이페이지 (myGuest.js, FE-138)

로그인하지 않았으면 마이페이지는 서버를 부르지 않고 첫 화면의 틀만 보인다. 레벨 · 직업 · 닉네임 · 수치 · 아래 항목 줄(만난 모습 · 내가 체험한 사건 · 내 정보 수정 · 환경 설정)은 없고, 항목 화면 주소로 와도 같은 화면이다.

  • 위쪽 줄은 로그인 뒤와 같은 격자(카드 200px · 가운데 1fr · 카드 200px, 720px 이하 2열 + 가운데가 맨 위)다.
  • 왼쪽 샘플 프로필카드(button.my-guest-slot, guestCards.js): 대표 이미지 · 테두리 · 배경 · 책갈피가 서로 다른 지어낸 카드 10종을 0.6초마다 무작위로(바로 앞 카드는 건너뛰고) 갈아 끼운다. 페이드 없이 바로 바뀐다. 카드는 로그인 뒤 프로필카드와 같은 profileFace라 같은 자리에 그대로 들어선다. 누르면 로그인 화면으로 간다: 호버(위로 3px) · 눌림(1px 아래) · 포커스(3px 아웃라인)는 일반 카드(.my-card)와 같고, 안의 카드는 따로 움직이지 않는다(pointer-events: none). 읽는 이름은 버튼의 "프로필카드 자리. 누르면 로그인 화면으로 가요"이고 안의 바뀌는 카드는 aria-hidden이다. 그림은 미리 받아 둬 바뀔 때 비지 않는다. 자리가 화면에서 떨어지면(다른 화면으로 이동) 스스로 멈추고, 탭이 가려져 있는 동안에는 바꾸지 않는다. 움직임 줄이기에서는 한 장만 고정한다.
  • 오른쪽 성향카드 자리(button.my-card.is-type.my-guest-card): 성향카드의 기본 디자인(타로카드: 어두운 바탕 · 이중 테두리 · 아치 창, 꾸미기 없음 DEFAULT_TYPE_DECO)을 그대로 쓴 "?" 카드다. 위 · 아래 칸에는 "카드를 눌러" / "로그인 하세요"를 같은 글자(명조 900 9.1cqi)로 쓰고, 각자 칸에서 안쪽 이중 테두리와 창 사이 높이 가운데에 맞춘다(아래 구분선은 없앤다). 창에는 큰 "?"(44cqi)가 올라가고, 창 안에는 성향 카드 그림이 뿌옇게 비친다(.my-guest-haze): 성향 그림 32장 전부(guestCards.js GUEST_TYPE_IMAGES)를 내로(01 ~ 16) · 남불(17 ~ 32) 번갈아 순서대로(01, 17, 02, 18, …) 1.8초마다, 다음 그림을 새로 만들어 불투명도만 1.2초 ease-in-out으로 올리고(앞 그림은 내린 뒤 뗀다) 겹쳐 보인다. 그림은 다른 카드와 같은 맞춤(fitPortraitIn, center: true · side: 1)으로 창을 가로 · 세로 꽉 채우고 가운데에 두며(캐릭터의 투명 여백을 걷어 그림마다 크기가 같다), blur(4px)에 불투명도 .4다(필터는 고정, 움직이는 값은 불투명도뿐). 그림은 다음 한 장씩 미리 받고, 자리가 떨어지면 멈추며, 탭이 가려져 있으면 바꾸지 않고, 움직임 줄이기에서는 한 장만 비춘다. 장식이라 aria-hidden. 누르고 싶게 하는 장치: ① 4.2초마다 한 번 대각선으로 지나가는 빛줄기(.my-guest-card::after, translateX만 움직임, 카드 overflow: hidden), ② 호버에서 금빛 테두리(--frame-ring에 --deco-gold 3px)가 둘러지고 "?"가 1.08배로 커진다(.15s). 호버 · 눌림 · 포커스는 일반 카드와 같다(호버 시 위로 3px). 움직임 줄이기에서는 빛줄기를 끈다. 누르면 로그인 화면으로 간다. 읽는 이름은 "성향카드 자리. 카드를 눌러 로그인 하세요 누르면 로그인 화면으로 가요".
  • 가운데: 굵은 제목 "판사가 되어 당신만의 판결을 내려보세요."(임시 문구, 명조 900 · --text-title-1, 720px 이하 --text-title-2, word-break: keep-all · text-wrap: balance) → 로그인하면 되는 것 세 줄(목록) → 숫자 두 줄 "공개된 사건 N개" / "일러스트 N장"(.my-guest-stats, 작은 흐린 글자 · 숫자만 굵게 · tabular-nums). 둘 다 서버에서 받는다: 사건 수는 공개 사건 목록(API 1 summary.total), 일러스트 수는 일러스트 총 개수 API(I-1 total, 사건 컷 · 캐릭터 아이콘 포함 전부, BE-142). 둘을 함께 받고, 받기 전이나 한쪽이라도 못 받았을 때는 숫자 줄을 통째로 숨긴다(틀린 숫자를 보이지 않는다). 버튼은 없다(로그인은 두 카드와 헤더의 로그인에서, 가입은 로그인 화면의 "회원가입"에서)
  • 아래 안내 한 단락(.my-note, 62ch): 로그인 없이도 사건 체험 · 성향 테스트는 되고, 처음 로그인할 때 이 브라우저의 체험 기록 · 첫인상 엽서는 계정으로 가져올 수 있으며 성향 테스트 결과는 가져오지 않는다는 것.
  • 로그인한 채 불러오다 로그인이 풀리면(401) 같은 화면으로 바뀐다.
  • 성향 테스트 결과(서류철 결과지 · 신문 결과)의 "결과 안내"에는 로그인 전이면 "로그인하지 않고 한 결과예요. 처음 로그인할 때 성향 테스트 결과는 계정으로 옮겨지지 않으니, 로그인한 뒤 새로 하면 돼요." 한 줄을 더한다(공유 주소 화면에는 붙이지 않는다).

첫 로그인 가져오기 확인 창 (anonymousImport.js, FE-138)

로그인 · 가입 · 카카오 콜백이 끝난 직후(메인 · 보던 화면으로 가기 전) 서버가 이 브라우저의 체험 기록 · 첫인상 엽서 후보를 알려 주면(M-9) 모달을 띄운다. 후보가 없거나 서버에 아직 없거나(404 · 204) 조회가 실패하면 창 없이 지나간다.

  • 내용: 오른쪽 위 닫기 ×(44px, aria-label "닫기: 로그인하지 않고 돌아가기") · 제목 "이 브라우저의 기록을 가져올까요?" → "이 브라우저의 체험 기록과 첫인상 엽서 기록을 이 계정으로 가져오게 됩니다." → 후보 수 "체험 기록 N건 · 첫인상 엽서 M건"(굵게, 0건은 뺀다) → "본인이 하지 않았다면 초기화하고 로그인하세요."(--color-caution) → [가져오기](주, 처음 포커스) · 초기화하고 로그인. 문구 · 모양은 임시(만든 뒤 조정).
  • 닫기 = 로그인하지 않기: × 버튼과 Esc는 로그아웃(M-2, 리프레시 쿠키 삭제)하고 로그인 표시를 지운 뒤 창을 닫는다. 로그인 · 가입 화면에 그대로 남고(버튼은 다시 눌릴 수 있다), 카카오 콜백은 메인으로 간다. 이 브라우저의 익명 기록은 건드리지 않는다. 브라우저가 직접 닫는 경우(취소를 막은 뒤 Esc를 한 번 더)에는 고르기 전이면 showModal()로 다시 연다. 바깥 누름으로는 닫히지 않는다. 가져오기 · 초기화 · 닫기 중 하나를 골라야 로그인이 이어진다.
  • 처리 중에는 세 버튼이 aria-disabled, 실패하면 role="alert"로 "처리하지 못했어요. 잠시 뒤 다시 눌러 주세요."를 띄우고 다시 고르게 한다. 크기는 min(92vw, 460px), 모양은 재심 확인 창(.my-retake-dialog)과 같다.

로그인 패드 구성 (loginPage.js, FE-104)

  • 순서: 카카오 로그인 → "또는" → 아이디 · 비밀번호 → 로그인 → "처음이신가요? 회원가입" → "로그인 없이 사건 보러 가기". 링크는 모두 밑줄 글자 버튼(.login-link, 44px 높이)이다.
  • 아이디 · 비밀번호 폼은 목 · 실제 모드 모두 펼쳐 둔다(일반 회원가입이 생길 예정이라 접지 않는다). 지금 실제 모드에서는 관리자 계정만 이 폼으로 들어간다. 일반 로그인이 없는 실제 모드는 폼만 보인다.
  • 요청 중에는 누른 버튼만 글자가 "로그인하는 중…"이 되고 둘 다 aria-disabled(포커스 유지)다.
  • 오류 문구는 누른 버튼 가까이(카카오는 버튼 아래, 폼은 로그인 버튼 위)에 둔다. 원인별로 나눈다: 연결 실패 "서버에 연결하지 못했어요. 잠시 뒤 다시 시도해 주세요." · 틀린 입력 "아이디와 비밀번호를 확인해 주세요." · 그 밖 "로그인하지 못했어요…".
  • 개발용 "넘어가기" 버튼 줄은 개발 서버(import.meta.env.DEV) + 목 모드에서만 붙는다. 720px 이하에서는 떠 있지 않고 패드 아래에 놓인다.

회원가입 패드 (signupPage.js, /signup, FE-104)

  • 로그인과 같은 책상 · 펠트 패드 · 400px 단을 쓴다. 순서: 제목 "회원가입" · 안내 → 카카오로 시작하기 → "또는 아이디로 가입" → 아이디 · 비밀번호 · 비밀번호 확인 · 닉네임 → 필수 동의 → 가입하기 → "이미 계정이 있나요? 로그인" → "가입 없이 사건 보러 가기".
  • 칸 아래 도움말(.signup-hint, 작은 흐린 글자)을 두고, 틀리면 그 아래에 칸 안내(.signup-field-error, --color-danger-on-dark)가 붙는다. 입력칸은 aria-invalid + 빨간 테두리, 도움말 · 안내는 aria-describedby로 잇는다. 빈칸 안내는 가입하기를 눌렀을 때만, 값이 있는 칸은 칸을 떠날 때 바로 검사한다. 제출하면 첫 틀린 칸으로 포커스를 옮긴다.
  • 규칙(signupRule.js): 아이디 영문 소문자 · 숫자 · 밑줄 4~20자, 비밀번호 영문 + 숫자 8자 이상, 비밀번호 확인 일치, 닉네임 2자 이상(한글이 있으면 8자, 영문 · 숫자만이면 12자까지, utils/nickname.js와 같은 규칙, 입력칸은 한도를 넘게 쳐지지 않게 잠근다), 이용약관 · 개인정보처리방침 동의(필수). 서버 API가 생기면 서버 규칙과 맞춘다.
  • 동의: 20px 체크박스(accent-color: --color-range-fill) + 굵은 문구, 줄 높이 44px. 아래 "이용약관 보기 · 개인정보처리방침 보기"는 입력값이 사라지지 않게 새 탭으로 연다(44px 높이 밑줄 링크).
  • 로그인 ↔ 회원가입 이동은 기록 항목을 바꿔(replace) 가입 · 로그인 뒤 뒤로 가기가 그 전에 보던 화면으로 간다.
  • 실제 모드는 회원가입 API가 없어 입력 검사 뒤 "아이디 회원가입은 아직 준비 중이에요. 지금은 카카오로 시작할 수 있어요."를 가입하기 위에 띄운다(role="alert"). 이미 로그인한 채로 오면 "이미 로그인했어요" + 마이페이지 · 사건 목록.

문서 페이지 (legalPage.js, 이용약관 · 개인정보처리방침)

읽는 글 한 장이다. 폭 760px, 카드 안에 제목 · 요약 · "초안" 딱지(점선) · 목차 · 제N조 조항 순서로 둔다. 위에 두 문서를 오가는 탭 줄이 있고(현재 문서는 aria-current만, disabled는 두지 않아 키보드로 닿는다), 목차를 누르면 그 조항으로 내려가며 움직임 줄이기에서는 바로 간다. 목차는 접이식(details, 제목 "목차 · N개 조항")이라 680px 이하에서는 접힌 채 시작하고 넓은 화면은 펼쳐 둔다. 조항 끝마다 "↑ 목차로"(13px, 44px 높이)가 있어 목차를 펼치고 그 자리로 돌아간다(FE-104). 조항 본문은 legalPage.js의 DOCUMENTS 배열에서만 고친다. 비어 있으면 "이 조항은 준비 중이에요."가 나온다. 색은 역할 토큰만 쓴다(legalPage.css).

카드 고르기 (.my-shown-pick)

누르면 하나만 선택된다(aria-pressed). 호버는 P3(카드 테두리가 --color-heading으로).

성향 변경폭 막대 (마이페이지, changeRow)

영역 다툼 문법이다(경계선은 1px, 막대를 위아래로 쭉 가로지르고 경계 정중앙에 걸쳐 절반은 막대 · 절반은 빗금 몸통에 걸친다. 두 선이 겹치거나 붙으면 가운데 하나만 둔다). 화살표 · 빗금 띠에는 테두리가 없고(경계선만 남긴다) 막대가 비운 칸 안에 들어가 막대를 침범하지 않는다. 막대는 "지금" 함유량(왼쪽 엄격 남색 | 오른쪽 관대 갈색)이고, 처음 · 지금 경계에 같은 검정 세로선을 긋고, 그 사이(늘어난 몫)는 늘어난 쪽 색 빗금으로 칠한다(그 뒤 막대는 비운다). 비중 글자는 처음 → 지금이고 늘어난 쪽만 그 색 · 굵게다.

  • 5%p 이상: 머리가 있는 빗금 화살표(머리 끝이 지금 경계) + 오른쪽에 "+N%p 쪽 이름".
  • 5%p 미만("거의 그대로"): 글자는 "거의 그대로"로 두고, 처음과 지금이 실제로 다르면 머리 없는 빗금 띠를 그린다(steadyBand). 폭은 두 경계 사이에 비례하고, 표시 비중이 같은데 실제 값이 다르면 최소 1% 폭을 표시 경계 가운데에 두고, 띠의 양 끝에 경계선 둘을 긋는다(0.2%p만 달라도 표시 차이 1%p일 때와 같은 모양). 실제로 같으면(부동소수 오차 포함) 띠가 없고 글자는 **"변화 없음"**이다. 숫자 · 글자는 바꾸지 않고 그리는 폭에만 적용하므로 반반 특별법(lenientPercent, 정확히 반반이 아니면 50을 보여 주지 않는다)과 충돌하지 않는다. 방향으로 늘리지 않는 이유는 49 → 49에서 관대 쪽으로 1%p를 늘리면 끝이 50%에 닿기 때문이다.
  • 1%는 데스크톱에서 약 5px, 모바일에서 약 2.4px이라 1% 띠는 윤곽선 안에 사선 하나가 보이는 작은 표식이다(2%p부터 빗금으로 읽힌다).

내 정보 수정 칸 묶음 (.edit-field, myProfileEdit.css)

  • 한 칸은 제목 → 조작 → 설명 순이다. 제목은 .edit-label(700), 조작은 입력칸이나 버튼 줄(.edit-actions, 간격 8px), 설명은 .edit-hint(13px, 옅은 잉크)로 조작 아래에 둔다. 설명은 "~요."로 끝나는 문장으로 쓴다.
  • 칸 안 간격은 6px, 칸 사이는 14 ~ 20px로 칸 안보다 넓다.
  • 지금 할 수 없는 버튼(준비 중 · 이미 끝남)은 숨기지 않고 비활성(opacity .6, 호버 없음)으로 두고, 이유는 버튼 이름에 괄호로 붙이지 않고 설명에 쓴다. 끝난 동작은 비활성 버튼이 아니라 상태 글(.edit-status, "✓ 첫 성향 테스트 완료" · "✓ 재심 완료", FE-103)로 보인다. 아직 못 누르는 "재심 청구하기"만 비활성 버튼으로 둔다.
  • 다른 화면으로 가는 버튼은 "○○에서 보기 · 꾸미기"처럼 갈 곳을 이름에 넣는다.
  • 프로필 칸 왼쪽 열은 프로필카드(200px)와 그 아래 "프로필카드에서 보기" 버튼(.edit-card-col)이다. 오른쪽에는 닉네임 · 한 줄 소개 · 서명 미리보기(.edit-signature)가 차례로 놓이고, 닉네임을 입력하는 대로 서명이 새로 그려진다(서명 씨앗이 닉네임이다).
  • 폼 맨 아래 버튼 줄은 오른쪽 정렬, 주 버튼(저장)이 오른쪽 끝이고 취소는 그 왼쪽이다.

스위치 (.set-switch, mySettings.css)

켜고 끄는 항목 하나에 쓴다. 트랙 44 × 24(완전 둥근 모서리) 안에 손잡이 18이 들어 있다. 꺼짐은 옅은 트랙(--color-track) + 진한 괘선 안쪽 1px이고, 켜짐은 강조색(--color-accent) 채움 + 손잡이가 오른쪽(20px 이동)이다. 손잡이는 카드색에 옅은 그림자다. 켜짐은 색만이 아니라 손잡이 위치로도 드러난다. 누르는 칸은 높이 44px를 지키고 눈에 보이는 트랙만 작게 둔다. role="switch" + aria-checked이고 이름은 같은 줄의 제목이 읽는다. 전환은 .15s ease-out이다. 헤더의 "연출" 스위치(style.css .site-header .switch-track)가 같은 규칙을 크기만 줄여(36 × 20) 쓰고, 커피 헤더에서는 색만 커피로 바꾼다(myPage.css .site-header[data-theme="coffee"]).

환경 설정 반영 규칙 (FE-100, mySettings.js)

  • 화면 연출은 헤더 "연출"과 같은 스위치라 누르는 즉시 적용된다(저장하기를 기다리지 않는다). 설명에 "누르면 바로 적용돼요"를 쓴다.
  • "곧 적용" 항목(앱 동작에 아직 연결되지 않음)은 눌러도 아무 일이 없으므로 비활성(opacity .6)으로 두고, 설명 아래 "아직 준비 중이라 바꿀 수 없어요"를 쓴다.
  • 바꿀 수 있는 항목이 하나도 없으면 "설정 초기화 · 저장하기" 줄을 두지 않는다.

내 결과 지면 구성 (FE-100, myResultPage.js)

  • 헤드라인은 내 판결이다("○○의 판결은 징역 15년", 최종 판결 값 그대로). 부제(deck)는 첫인상 구간. 예전 "…의 판결, 다시 펼치다"는 판결이 사진 아래 작은 글씨로 밀렸다.
  • 제호 왼쪽 귀는 들어온 곳으로 돌아가는 자리(← 마이페이지 · 내가 체험한 사건으로), 오른쪽 귀는 사건 번호 · "판결 확정"(6막과 같다. 6막의 "고민 중" 메모는 FE-120에서 없앴다).
  • 아래 광고 단은 6막과 같은 세 칸 모양을 지킨다: 성향 테스트를 했으면 "내 성향카드"(마이페이지 성향카드로), 안 했으면 "판결 성향 테스트" · "오늘의 지면"(눌러서 사건 목록) · "마이페이지로"(눌러서 내가 체험한 사건 목록, 6막의 "첫인상 엽서 모집" 자리). 세 칸은 폭 · 테두리(3px 실선) · 제목 크기가 모두 같고, 6막도 같은 규칙이다(stage.css, FE-120 — 전에는 6막 첫 칸만 1.3배 · 3px · 큰 제목이었다). 광고 칸처럼 생긴 것은 모두 누를 수 있다. 마이페이지를 못 받아 성향 테스트 여부를 모를 때는 첫 칸을 빼고 두 칸만 둔다(테스트를 이미 한 사람에게 테스트를 권하지 않으려는 것이고, 720px 이하는 한 열 그대로, FE-110).
  • 좁은 화면(720px 이하, FE-155)은 한 단으로 1면 · 판결 성향 → 흐름 그래프 → 광고 단 순서다. 이 분기는 6막 읽기 지면(stage.css · courtSketch.css)의 모바일 분기와 같아야 한다(myResult.css가 그 격자를 덮어쓴다).
  • 마이페이지로 돌아가는 길은 제호 왼쪽 귀("← 마이페이지")와 아래 광고 단의 "마이페이지로" 칸이다. 6막처럼 화면에 떠 있는 버튼은 두지 않는다(좁은 화면에서 그래프 · 푸터를 가렸고, 어두운 책상 바닥 위의 남색 버튼은 묻혔다). 화면 전체는 <main>이다.
  • 마이페이지 · 내 결과의 내 이름은 서버 닉네임 → 로그인 세션 닉네임 → "익명의 판사" 순(myNickname, 헤더와 같은 이름).

대표 이미지 고르기 (FE-100, 프로필카드 화면)

열린 종류는 펼쳐 두고, 잠긴 종류는 "잠긴 종류 (n)" 아래에 한 줄씩(details, 이름 · 개수 · 여는 도전과제, 44px) 접는다. 줄을 누르면 잠긴 그림을 미리 볼 수 있다. 좁은 화면의 열린 개수 표(.my-profile-counts)는 한 칸으로 쌓는다(두 칸이면 이름이 한 글자씩 쪼개졌다).

닉네임 표시 (utils/fitText.js, FE-120)

닉네임은 한글 8자 · 영문 12자까지 쓸 수 있고, 말줄임(...)으로 자르지 않는다. 한 줄(white-space: nowrap)에 두고, 칸에 넘칠 때만 글자 크기를 줄여 맞춘다(fitOneLine). 칸 폭이 바뀌거나(카드 크기 · 창 크기) 글자가 바뀌면 다시 잰다. 원래 크기는 CSS가 정하고, 줄일 때만 인라인 font-size로 덮는다. 쓰는 곳: 프로필카드 위 닉네임(.my-card.is-profile .my-card-name) · 마이페이지 가운데 칸 닉네임(.my-nickname). 헤더 로그인 이름(.account-name)은 줄이지 않고 폭만 내용만큼 둔다. 가장 좁은 곳은 카드 책갈피(굵은 띠) 사이다: 한글 8자가 카드 폭 200px에서 약 14px, 모바일 약 12px까지 줄어든다.

경험치 페이지 (myLevel.js · myLevel.css, FE-120)

마이페이지 첫 화면의 경험치 막대(.my-level-progress, 이제 버튼)를 누르면 view=level("경험치")이 열린다. 호버는 P2. 구성: ① 지금 레벨 · 직업 · 큰 진행 막대(16px) · 다음 직업까지 남은 XP ② 직업이 열리는 레벨 표(레벨 · 직업 · 필요 누적 XP · 상태: 열림 / 지금 / N XP 남음, 지금 직업 행은 --color-surface 바탕) ③ 경험치 얻는 방법 표(API 명세 11장 수급처 표를 옮긴 고정 문구). 경험치 획득 내역(로그)은 서버에 없어 보여 주지 않는다. 값은 서버 L-1(data.level)이고 목 모드에서는 "목데이터" 딱지가 붙는다.

카드 꾸미기 창 스크롤 (FE-120)

창이 열려 있는 동안 뒤 화면 스크롤은 잠긴다(scrollLock.js, body.deco-open). 넓은 화면에서는 오른쪽 고르기 칸만 스크롤되므로, 왼쪽 미리 보기 카드 위에서 휠을 굴려도 고르기 칸이 스크롤된다(휠 전달). 좁은 화면은 창 전체가 스크롤된다.

6막 읽기 지면 끝 ("여기 남아있기", FE-120)

  • 귀: 두 귀는 같은 크기로 고정한다. 폭은 S-02 · S-20 제호 줄과 같은 190px(1100px 이하 150px, 720px 이하 두 칸 같은 폭), 높이는 64px(720px 이하 44px, 보조 글자는 숨김). 글자는 줄바꿈하지 않는다. 왼쪽 귀 보조 글자는 "오늘의 지면으로". 왼쪽 귀 "← 다른 사건 보기"는 눌리는 칸이라 진한 면(--color-heading) + 종이색 글자(--color-on-accent) + 이중 테두리이고 호버는 --color-accent로 밝아진다(P1). 오른쪽 귀는 누르지 않는 사건 번호 칸이라 3px 이중 --color-border-strong, 바탕 없음.
  • 광고 단: 첫 칸 "판결 성향 테스트"(성향 테스트 면으로), 가운데 "공유하기"(링크 공유, 결과 문구는 칸 안 설명 자리에 나온다), 오른쪽 "메인으로"(신문을 책상에 내려놓고 메인으로, 사건 목록으로 보내지 않는다 — 목록을 거듭 보는 피로감, 왼쪽 귀와 같은 진한 면 .sheet-ad.is-main). 읽는 상태가 된 뒤에만 눌린다. 떠 있던 우하단 "메인으로" 알약은 없앴다. 광고 칸 모양은 "내 결과 지면 구성"과 같다(stage.css).
  • 독자 의견 엽서: 질문 · 내 첫인상 · 그래프 · 편집부 의견만 둔다("기사만 읽고 보낸 첫인상 엽서" 줄과 "엽서를 눌러 … 쓰기" 안내는 없앴다. 엽서를 누르면 독자 엽서가 열리는 동작은 그대로). 손글씨 줄 대신 "이 사건 독자 N명의 첫인상" 막대그래프: 칸은 구간 이름 50% · 막대 · 숫자 3.2em, 내 구간은 막대 서류 네이비 + 구간 이름 · 숫자 굵게(글자 표시 없음, 낭독기만 "내 선택"). 좁은 화면은 이름 · 숫자 한 줄, 막대 아래 줄. 지금은 목 데이터("목데이터" 딱지, preToFinal.crowd — API 14에 아직 없고, 없으면 예전 손글씨 줄을 둔다). 엽서 글자는 FE-120에서 키웠다(질문 2.4u · 답 3.3u · 편집부 의견 1.6u, 좁은 화면 질문 22px).

하나만 고르는 버튼 (.edit-choice)

보조 버튼 모양 그대로 나란히 두고, 고른 것은 aria-pressed="true" + 서류 네이비 테두리(안쪽 1px 더) + 안개 네이비 바탕이다(카드 꾸미기의 알약 선택과 같은 표시). 호버는 P3. 고를 카드가 없는 칸은 비활성이다. 내 정보 수정의 초심 · 재심 · 최근(공개 카드)이 쓴다.

겹친 성향카드 (.edit-fan, 내 정보 수정)

같은 크기 카드를 같은 높이에 두고 x만 --fan-step(56px, 380px 이하 36px)씩 어긋나게 겹친다. 고른 카드가 맨 왼쪽 · 맨 앞이고 결재 도장이 찍힌다. 뒤 카드의 보이는 부분을 눌러도 고를 수 있고, 호버는 P7 카드 들어올림(3px)이다. 겹친 카드 아래에 "성향카드에서 보기" 버튼(.edit-fan-main, 폭 174px로 프로필카드 아래 버튼과 같고, 맨 앞 카드 한 장 아래 가운데에 놓여 프로필카드 아래 버튼과 가로 위치가 같다)을 둔다. 카드가 한 장도 없으면 이 버튼은 점선 테두리 비활성이다.

성향 변화 흐름 차트 (마이페이지)

  • 키보드(FE-103): 그래프는 role=group이고 점마다 투명한 role=img 원(.trend-hit, 이름 = 점 값)이 있다(누를 일이 없어 button이 아니다). 점이 많아 Tab 자리는 그래프당 하나(roving tabindex)이고 방향키(Home · End는 처음 · 끝 점)로 점을 옮기면 말풍선이 따라 보인다. 포커스 링은 3px --color-focus. 눈금 · 보조 글자는 12px 이상.
  • 보이는 칸: 최근 체험 10건까지. 테스트가 있으면 앞에 초심 칸을 하나 더 둔다. 테스트가 없으면 체험 10건만이다(첫 체험을 따로 붙이지 않는다).
  • 기준점(초심 · 재심 · 최근): 한 번의 판결이 아니라서 체험 선과 잇지 않는다. 마름모와 세로 점선으로 따로 찍고, 초심 · 재심은 속이 빈 마름모, 최근은 커피색이 찬 마름모다. 보이는 크기는 최근(칠 + 흰 윤곽이 안쪽을 먹음)과 초심 · 재심(속 빈 마름모의 진한 윤곽이 바깥으로 나옴)이 지름 약 9로 같게, 반대각선 절반을 최근 6 · 초심 · 재심 3.5(윤곽 1.5px)로 둔다(trendGraph.js CURRENT_R · BASE_R).
  • 재심 위치: 가로 칸을 만들지 않고 두 칸 사이 가운데에 얹는다(사람마다 시점이 달라도 체험 간격 · 기울기가 같게). "재심" 글자는 점선 위쪽, 그래프 위에 둔다(가로축 글자와 겹치지 않게).
  • 간격 예외: 재심이 초심 바로 뒤(체험 0건 뒤)이거나 최근 바로 앞(맨 끝)일 때만 그 사이를 0.7칸 더 띄운다(CHART.anchorGap). 나머지는 모두 같은 간격이다.
  • 눈금선: 가로 눈금선은 75 · 50 · 75(가운데 세 줄)만 점선(1px, 진한 괘선색 75%, 대시 4 3)으로 긋는다. 100(위 · 아래 끝)은 그래프 끝이 이미 보여 선을 두지 않는다(눈금 글자는 둔다).
  • 점수가 없는 축: 체험은 있는데 이 축의 점수가 하나도 없으면(체험이 0건이면 그래프 전체가 "사건을 하나 마치면 첫 점이 찍혀요." 한 줄이다) 축 이름(제목)은 그대로 두고 위아래 끝 표시(↑ ↓)만 뜻이 없어 감추며(visibility: hidden, 자리는 남겨 다른 그래프와 같은 크기), "체험 자료가 아직 없어요"를 박스 정중앙에 둔다(.trend-chart.is-empty).

오버스크롤 색은 맞추지 않는다 (10/28 결정)

쪽 끝에서 더 당겨 드러나는 자리(고무줄 스크롤)의 색은 맞추려 하지 않는다. 사파리는 그 자리를 페이지가 아니라 브라우저가 html 배경색 하나로 칠해서, 위 · 아래를 헤더 · 푸터 색으로 따로 이을 수 없다.

  • 시도했다가 버린 것: 헤더에 붙인 띠 · 쪽 위 바깥 판(그려지지 않음), html 배경을 상시 덮기(평소 바탕까지 바뀜), 당기는 동안 배경색 바꾸기(깜박임 · 단색), 고무줄 스크롤 끄기(조작감이 어색함).
  • 오버스크롤 때문에 html · body 배경을 바꾸거나 스크롤 이벤트로 색을 바꾸지 않는다.

되돌릴 수 없는 동작 · 확인 상자

회원 탈퇴 · 기록 초기화 · 재심 1회 안내처럼 되돌릴 수 없는 동작은 주의 색(--color-caution · --color-caution-border)을 쓴다(빨강 금지). 버튼을 누르면 동의 체크가 있는 확인 상자가 열리고, 체크해야 실행 버튼이 켜진다(dangerAction, myProfileEdit.js). 입력칸은 글자 16px · 모서리 4px, 체크 줄 높이는 44px 이상이다.

컬렉션 · "새로 얻음" · "새로 열림" (FE-132, myPage.js · myPage.css)

  • 무엇을 보여 주나: 서버 수집 기록(P-5 collection)을 그린다. 화면은 사건을 마칠 때(6막) 얻은 것을 보내고(P-9), 마이페이지는 받은 목록만 그린다. 묶음은 "만난 모습(n)"(역할 의상) 한 묶음 + "사건 일러스트(n)" 아래 사건별 소제목(최근에 얻은 사건부터, 사건 안은 재연 · 발단 · 결론 순). 사건 일러스트 카드 글자는 자리 이름(재연 · 발단 · 결론)만이고, 크게 보기 · 스크린리더 이름은 "사건 제목 · 자리"다.
  • 획득 날짜: 카드 이름 아래 한 줄 "2026. 10. 14. 획득"(.collection-date, 12px · --color-muted · 숫자 고정폭). 도전과제는 달성 상태 칸에 "달성 · 2026. 10. 14."(unlockedAt). 날짜는 한국 시간이다.
  • 새로 표시(.my-new-badge): 주 버튼과 같은 색(--color-accent 바탕 · --color-on-accent 글자)의 작은 알약(12px 굵게, 위아래 1px · 좌우 8px, 완전 둥근 모서리). 색만으로 알리지 않고 글자로 쓴다. 쓰는 자리 네 곳: 컬렉션 카드 그림 왼쪽 위("새로 얻음") · 첫 화면 "만난 모습" 줄 제목 옆("새로 얻음 N") · 도전과제 제목 옆("새로 열림") · 프로필카드 화면 "도전과제 보기" 버튼 글자 옆("새로 열림 N").
  • 언제 사라지나: 컬렉션 화면 · 도전과제 화면을 열면 곧바로 "봤음"을 보낸다(P-10 종류별 · P-11). 그 화면을 보는 동안은 표시를 남겨 두고(새로 받은 응답으로 다시 그려도 지우지 않는다), 다른 화면으로 갔다 오면 사라진다. 기존 사용자 백필(수집 기록이 생기기 전에 마친 사건)은 처음부터 "본 것"이라 표시가 없다.
  • 첫 화면 줄 문구: "사건에서 모은 모습과 일러스트 N개"(N = 서버 collected, 일러스트 + 의상 — 위 수치 "모은 일러스트"와 도전과제 collected가 같은 값), 없으면 "체험을 마치면 사건에서 만난 모습과 일러스트가 모여요". 미리 보기 네 장은 최근에 얻은 순.
  • 목 모드 개발 서버의 "이미지 전체 미리 보기"(FE-67)는 컬렉션 맨 아래 접힌 칸(details, 점선 위 구분)으로 옮겼다. 옛 실사풍 대표 사진(case-N.webp)은 파일을 지웠고, 이름 붙은 종류에 들지 않는 이미지(기타)와 잘라 만든 얼굴 아이콘은 이 미리 보기에서 뺐다. 사건 이미지는 사건 번호를 자릿수대로(2 다음 3, 10은 그 뒤) 정렬한다.

빈 상태 안내 (옆 카드가 더 높을 때)

옆 카드 높이만큼 빈 자리가 생기면 안내 문구를 그 자리 가운데에 둔다(성향카드 화면의 판결 성향, .my-detail.is-empty).

목데이터 딱지 (.mock-tag)

목 값 옆에 붙는 점선 테두리 딱지(11px, 주변 글자색을 따라간다). 목 수치를 숨기지 않고 "목데이터"라고 표기한다. 흐리게(opacity) 두지 않는다: 11px라 흐리면 대비가 4.5 : 1 아래로 떨어진다(FE-93). 참여 수처럼 목 모드에서만 가짜인 값은 API_MODE === 'mock'일 때만 붙인다(사건 목록 · 커뮤니티 공통).

색인 탭과 진행 책갈피 (성향 결과지)

  • 색인 탭(결과 · 풀이): 파일철의 색인 탭처럼 윗변이 좁은 사다리꼴(perspective(18px) rotateX(2.2deg), 위 모서리 10px)이고, 두 탭이 10px 겹쳐 꽂힌다. 글자는 사다리꼴과 따로 그려 일그러지지 않는다. 지금 탭은 철과 같은 색(--color-desk-kraft)으로 이어져 앞으로 나오고(위 하이라이트, 아래 1px이 철 테두리를 덮는다), 나머지는 한 겹 뒤(--color-desk-kraft-back, 아래쪽 그늘, 4px 낮게)다. 탭 줄은 철 위 가장자리에 붙어 틈이 없고, z-index를 두지 않아 뒤쪽 탭은 철 앞면 밑으로 들어간다. 호버는 P7 "폴더 탭 눌림"(뒤쪽 탭이 3px 올라옴).
  • 진행 책갈피(.folder-bookmark, 공용 folderParts.js folderBookmark): 판결 체험 파일철과 같은 부품으로, 펼쳐 본 가장 뒤 장의 탭 뒤에 끼워 둔 리본 + 얼굴이다. 마이페이지 "책갈피 설정"에서 고른 리본 색(.bookmark-color-*)과 캐릭터 얼굴(selectedBookmarkFace)을 쓴다. 잠금이 아니라 진행 표시라 앞 장으로 돌아와도 자리를 지키고, 옮겨질 때 0.24s 내려오며 나타난다(동작 줄이기에서는 바로 옮김). 장식이라 aria-hidden.
  • 모양: 폭 28px. 윗모서리는 위로 길쭉한 둥근 머리(border-radius: 6px 6px 0 0 / 11px 11px 0 0)이고, 아래는 4px V자 홈(clip-path)이다. 얼굴 아이콘은 지름 24px, 리본 위에서 3px 아래. 탭과 양쪽 8px 겹친다.
  • 길이만 화면별(--bookmark-length · --bookmark-hang): 성향 결과지는 36px(서류철 상단보다 6px만 솟고 종이 위까지 30px 걸침, 기본값), 판결 체험은 60px(종이 위까지 34px 걸침).

4축 막대 (components/axisRow.js)

성향 결과(S-16)와 마이페이지(S-17)가 함께 쓴다. 한 줄은 **[축 이름 | 양쪽 비중 글자 / 막대]**로, 축 이름은 막대 왼쪽(7em)이고 비중 글자는 막대 위다. 막대 줄은 20px로 묶어 막대(10px)가 축 이름과 같은 높이 가운데에 오고, 가운데 눈금(반반)은 위 · 아래로 4px씩 튀어나온다. 비중 글자는 평소 옅은 잉크 · 보통 굵기이고 더 기운 쪽만 그 쪽 색 · 굵게(700)다. 좁으면 양쪽 비중이 두 줄로 나뉘고 오른쪽 것은 오른쪽 끝에 붙는다. 체험 자료가 없는 축은 막대 대신 "체험 자료가 아직 없어요"를 두고 눈금도 뺀다. 줄 높이는 막대가 있는 줄과 같게 둔다: 양쪽 비중 글자 줄이 빠지면 줄 첫 칸이 0이 되어 줄이 짧아지고 축 이름이 위로 붙으므로, 그 줄만큼의 보이지 않는 자리(aria-hidden)를 채운다(appendEmptyAxis). 4축 막대와 마이페이지 변경폭 줄이 같은 도우미를 쓴다. 결과 페이지의 4축과 관용 지수는 점선(1px dashed)으로 나뉜 별개 섹션이다(섹션 위아래 여백 32px). 남색 | 갈색 경계에는 틈이나 선 없이 두 색이 바로 맞닿고, 가운데 반반 눈금만 변경폭 경계선과 같은 검정 1px 선(막대 위아래로 4px 튀어나옴)이다(결과지 · 마이페이지 공통. 예전에는 결과지용 종이색 2px 틈이 있었으나 커피색 바탕에서 흰 줄로 튀어 뺐다).

성향 결과지 구성 (S-16 · 공유 화면)

내 결과는 결과 · 풀이 두 장(탭)이고, 두 장 모두 맨 위에 같은 모양의 캐릭터 카드를 둔다. 결과 장은 유형의 기본 캐릭터, 풀이 장은 같은 유형의 다른 캐릭터(이미지 번호 ±16)를 보여 준다. 결과 장 = 카드 · 4축 · 관용 지수 (판결하러 가기 · 공유 버튼은 풀이 장 끝에만 둔다 — 결과 장 끝에 두면 풀이를 건너뛰고 떠나므로 뺐다, FE-95), 풀이 장 = 카드 · 일상에서는 · 판결을 볼 때는 · 안내 · 버튼 줄(종이 아래에 고정, 데스크톱). 두 장을 오가는 종이 귀가 모서리에 있다(다음 장은 오른쪽 아래 "풀이 보기 →", 이전 장은 왼쪽 아래 "← 결과 보기"). 귀를 누르면 맨 위로 스크롤한다. 4축 · 관용 지수 · 설명은 각각 점선(1px dashed)으로 나뉜 섹션이다(위아래 여백 32px). 공유 화면(/test/types/{shareId})은 개인 점수가 없어 내용이 짧으므로 탭 없이 한 장으로 이어서 보여 주고, 버튼("나도 해 보기")은 오른쪽 아래에 둔다. 카드는 위 P7의 "겹친 카드"다.

관용 지수 눈금

components/toleranceScale.js. 숫자 없이 "엄격함 ↔ 관대함" 양 끝 눈금 막대 위의 점. 높을수록 좋은 게 아니라는 안내를 함께 둔다.

커뮤니티 메인 (S-18, FE-89)

메인 책상 위 노트북의 첫 화면이다. 모두 집무실 물건의 은유로 만든다.

  • 오려 낸 기사 조각 (.cm-hot-clipping): 신문에서 오려 낸 종이 + 종이 클립(.cm-clip). 가장자리는 clip-path로 찢은 듯 깎고, 장마다 기울기를 번갈아 준다(.is-alt). 기사 바탕 · 글자색은 신문 팔레트(--palette-news*)다.
  • 시상대 (.cm-podium · .cm-step): 받침대 대신 종이 위 괘선 단(위 가로선 2px)으로 높이만 순위를 보인다. 1위가 가운데 · 가장 높고, 읽는 순서(1 · 2 · 3위)와 보이는 자리(2 · 1 · 3위)는 order로만 다르게 한다. 카드는 프로필카드 그대로이고 아래 칸만 레벨 대신 공개한 성향 이름이다. 레벨은 성향과 무관하다(FE-89 결정 기록).
  • 끝없이 도는 줄 (슬라이드 · 4 ~ 10위): 진짜 항목 앞뒤에 복제본을 한 벌씩 두고(aria-hidden · inert), 멈춘 뒤(140ms) 복제본에 와 있으면 한 벌 폭만큼 behavior: 'instant'로 되돌린다. 넘기는 중에 되돌리면 튀므로 멈춘 뒤에만 한다. 항목이 하나뿐이거나 다 들어가면 복제본 · 화살표를 뺀다. 막대 스크롤바는 숨기고 화살표 · 손가락 밀기로 넘긴다.
  • 가로 스크롤 칸 안 여백 주의 (FE-158): 슬라이드 · 4 ~ 10위 줄 안의 요소에 음수 아래 여백을 두지 않는다. 칸 밖으로 넘쳐 세로 넘침이 생기면 손가락 세로 밀기를 슬라이드가 먹어 페이지가 스크롤되지 않는다.
  • 끝 흐림 (.cm-rest.more-left · .more-right): mask-image로 양 끝 40 · 56px을 흐리게 하고, 첫 카드는 흐림 칸을 비켜 46px 안쪽에서 시작한다. 화살표는 카드 줄 밖의 제 칸에 둔다.
  • 화살표 버튼 (.cm-arrow): 34px 정사각, 흰 면 위 P1(배경만 --color-surface로 어둡게). 비활성은 P6(opacity .6). 눌림은 전역 버튼과 같은 scale .96(판결하기 버튼 · 정렬 칩도 같다). 모바일(≤ 720px)은 모양 그대로 누르는 영역만 44px(::before, FE-158).
  • 판결하기 버튼 (.cm-hot-try): 진한 네이비 면 위 P1 방향대로 호버에서 밝아진다.
  • 추천 사건 제목 (.cm-reco-title-link): 사건 정보(/cases/{id})로 가는 링크. 평소 밑줄 없이 제목처럼 두고, 호버((hover: hover))에서 밑줄(오프셋 3px).
  • 글쓴이 명함 작은 창 (③ .cm-pop, FE-88): 닉네임 · 작은 명함을 누르면 열리는 popover. 넓은 화면은 누른 자리 옆에 뜨고 바깥 누르기 · Esc · 스크롤로 닫는다. 모바일(≤ 720px, JS SHEET와 같은 경계)은 화면 아래에서 올라오는 판(위 모서리만 둥글게, 높이 85dvh까지, 아래 여백에 env(safe-area-inset-bottom, 0px))이고, 오른쪽 위 닫기 버튼(.cm-pop-close, 44px ✕, aria-label "명함 닫기", 눌림은 --color-surface 바탕)을 둔다(FE-158). 닫는 방법은 ✕ · 바깥 누르기 · 스크롤 세 가지이고, 끌어서 닫기는 없다 — 그래서 끌 수 있어 보이는 손잡이도 두지 않는다. 넓은 화면에서 닫기 버튼은 display: none.
  • 터치 눌림 (FE-158): 호버는 모두 (hover: hover) and (pointer: fine) 안에만 있다. 터치에서는 P2 항목(.cm-filter · .cm-action · .cm-case-pick · .cm-attach)이 :active에서 호버와 같은 틴트가 되고, 버튼(P1)은 scale .96이다.
  • 판결 카드 접기 (.cm-verdict.is-folded, FE-93): 보는 사람이 그 사건 판결을 확정하지 않았으면 남의 판결 카드 대신 점선 테두리 · --color-surface 바탕의 접힌 칸("이 사건을 판결하면 글쓴이 판결이 보여요" + "그래도 보기")을 둔다. 펼치면 그 자리에 카드가 들어오고 포커스가 카드로 간다. 남의 글 카드 라벨은 "글쓴이 판결", 작성창 미리보기만 "내 판결"이다.
  • 판결 카드 속 미니 성향카드 (.cm-verdict-clip): 장식이라 aria-hidden. 성향 이름은 카드 안 글자("판결한 사람의 성향 …")로 읽힌다.
  • 빈 상태 (FE-93): 이야기가 모인 사건이 없으면 슬라이드 자리에 빈 스크랩 칸(.cm-slot, 점선 테두리 + --color-surface 면, "아직 오려 둔 기사가 없어요" + "사건 목록 보기")을 둔다. 시상대는 랭킹이 비었거나 셋보다 적으면 빈 자리를 점선 카드(.cm-rank-empty, 카드 비율 7 : 14, aria-hidden)로 남기고 위에 안내 한 줄을 둔다. 꾸민 카드를 올릴 자리가 비어 있다는 것을 보여 동기를 준다. 점선 + 옅은 면은 접힌 판결 칸과 같은 말투다.
  • 로고 (.cm-logo, 임시): 책상 위 스탠드 + 초승달(밤). currentColor를 따라 테마가 바뀌어도 어울리고, 붉은색은 쓰지 않는다. 이름은 COMMUNITY_NAME 한 곳, 최종 이미지 로고가 나오면 logoMark만 바꾼다. 이름 · 로고는 확정 전이다.

서명 (components/signatureInk.js · utils/signature.js, FE-94)

닉네임으로 만든 읽히지 않는 서명 한 획이다. 커뮤니티 글쓰기 · 글 상세 · 판결 체험 3막 서명란이 같은 부품을 쓴다. 인장은 커뮤니티의 기존 인장 시스템 그대로이고 서명 옆에 도장을 더하지 않는다.

  • 모양: 글자 하나가 획 하나다. 한글은 초성 · 중성 조합이 12가지 획(고리 · 겹고리 · 물결 · 큰 호 · 코일 · 8자 · 솟는 획 등) 중 하나를 고르고 받침이 끝 갈고리를 붙인다. 영문 · 숫자는 글자 코드로 정한다. 해시는 기울기 · 높이 흔들림 · 밑줄 유무만 정한다. 같은 닉네임은 늘 같은 서명이고, 서명이 계정이 아니라 닉네임에 묶여 닉네임을 바꾸면 서명도 바뀐다.
  • 한 획: 모든 획이 기준선에서 시작해 기준선으로 끝나 끊기지 않는다(M은 한 번뿐, 테스트로 확인).
  • 쓰는 연출: 처음부터 끝까지 한 번에 이어서 0.8초(짧은 서명) ~ 1.2초(긴 서명). 점선(stroke-dasharray)이나 pathLength를 쓰지 않고, JS가 좌표를 앞에서부터 길이 순서로 이어 붙여 그린다(pathAt). 사파리는 긴 선에서 대시가 조각나 여러 토막이 동시에 그려졌다. 속도는 선 길이에 대해 일정하다. 움직임을 줄인 환경에서는 완성본이 바로 나온다(reducedMotion()).
  • 굵기 · 색: vector-effect: non-scaling-stroke로 화면 픽셀 굵기(기본 2.2px, 3막 3px)를 쓴다(대시를 쓰지 않아 안전). 색은 쓰는 곳이 color로 정한다: 커뮤니티 --color-accent, 3막 --color-desk-note-pen.
  • 계산: 로그인할 때(saveUser) 한 번 만들어 두고, 남의 서명은 처음 볼 때 만들어 닉네임별로 메모리에 캐시한다(signatureOf). 로그인하지 않은 3막 사용자는 브라우저에 저장한 임시 식별값(anonSeed)을 씨앗으로 쓴다.
  • 접근성: 장식이라 aria-hidden, 글쓴이 서명만 role="img" + "○○의 서명". 서명하는 동안 버튼은 잠긴다.
  • 글쓰기 페이지 (/community/write, communityWritePage.js): 커뮤니티 화면은 2열(왼쪽 제목 · 거르기 · 많이 본 글 220px / 가운데 본문)이고, 글쓰기는 오른쪽 칸이 아니라 왼쪽 열 맨 아래 "글쓰기" 버튼(.cm-compose-open, 좁은 화면은 그 열 오른쪽 위)으로 가는 이 별도 화면이다(글쓰기 페이지에는 이 버튼이 없다)(좁은 화면의 덮는 창도 같은 페이지로 통일). 많이 본 글은 좁은 화면(1023px 이하)에서 숨는다. 올리면 글 목록으로 가서 새 글을 보여 준다. 폭은 고정하지 않고 가운데 열 폭을 그대로 쓴다. 커뮤니티 메인에는 "모든 글 보기" 버튼이 없고(말머리 "전체"가 그 역할), 거르기에 눌린 표시도 두지 않는다.
  • 글쓰기 칸 편지화 (.cm-compose): 입력 항목과 기능은 그대로 두고 위 가장자리 장식(--color-decor 점선 띠), 받는 사람 한 줄(.cm-recipient, 고른 사건을 따라 "받는 사람 ○○ 사건을 본 사람들 / 커뮤니티 모두에게"), 줄 쳐진 내용칸, 맨 아래 서명란(.cm-sign)만 더했다. 제목은 "편지 쓰기", 버튼은 "서명하고 올리기"(누르면 서명이 쓰인 뒤 올라가고, 올리지 못하면 서명을 지운다). 목록 · 상세의 말투(글 · 댓글)는 바꾸지 않았다(전면 편지 은유는 비직관적이라 취소, docs/decisions/FE-94.md).
  • 글 상세 글쓴이 서명 (.cm-signoff): 본문 끝에 완성본 서명을 둔다(연출 없음).
  • 3막 서명란 (.sign-line .sig-ink): 고정 획 대신 서명을 쓴다. 서명이 다 쓰인 뒤에 확정을 보낸다(writeSignature 약속이 이행될 때).

판결 체험 입력 · 공개 규칙 (FE-98)

세 판결은 대등하고 판단은 사용자의 것이라는 원칙(PRODUCT.md 원칙 1)을 화면 규칙으로 옮긴 것이다.

  • 판단 요소 버튼 위계: 고른 "무겁게 · 가볍게"는 방향 색(--color-factor-heavy · -light)으로 채우고 글자는 --color-on-accent. 기본 "고려 안 함"은 윤곽선만(--color-border-strong, 켜져 있으면 글자만 진하게). "아무것도 안 고려함"이 가장 눈에 띄는 상태로 읽히지 않게 한다. 머리에 "고른 요소 n / N"을 보인다(몇 개를 골라야 한다는 뜻이 아니다).
  • 요소 0개 서명: 막지 않는다. 서명 장에 "판단 요소를 하나도 고르지 않았어요. 그대로 서명할까요?"를 한 줄 둔다(.sign-notice, 주의색 왼쪽 줄).
  • 비교 문구: 실제 판결이나 AI를 잣대로 삼지 않는다. 6막 편집부 의견은 첫인상과 내 최종 판결의 비교("처음 생각보다 무겁게 판결했어요", 서버 preToFinal.direction)이고, 판단 요소 행렬의 분류 이름은 "AI · 재판부만 고려한 요소"(내부 값 ONLY_ME_MISSED는 API 계약이라 유지), 차이 문장은 "…를 고려했고, 내 판결에는 들어가지 않았어요."로 사용자를 놓친 쪽으로 틀지 않는다. 차이 대사는 내 → AI → 실제 순서.
  • 독자 엽서 순서: 평균 별점과 다른 독자의 엽서는 내가 엽서를 보낸 뒤에 보인다. 보내기 전에는 "엽서를 보내면 평균 별점과 다른 독자들의 엽서가 보여요."만 둔다.
  • 2막 맺음: 마지막 변호인 대사 뒤에 "재판은 여기까지예요. 이제 재판장, 당신이 판결할 차례예요." 한 줄을 두고 판결(3막)로 넘긴다.

연출 (모션)

기본 전환은 짧게(.12s ~ .2s, ease-out). 긴 연출(책상 → 신문, 커피잔 다이브, 페이지 넘김 등)은 해당 화면 CSS / JS에 있다. data-motion="off"는 길이를 0으로 만들되 끝 상태는 유지하고 animationend · transitionend도 바로 오게 한다. 기기의 "동작 줄이기"는 각 화면 CSS의 prefers-reduced-motion이 맡는다. 화면을 통째로 돌리거나 옮기는 큰 연출은 모바일(≤ 720px)에서 건너뛰는 것이 기본이다(철 돌리기가 사례, 위 P7 표). 커피잔 다이브(utils/coffeeDive.js)는 화면을 돌리지 않고 위에 잔이 날아오는 덮개 연출이라 모바일에서도 그대로 한다(FE-157 확정, 잔 크기는 짧은 변의 95%).

신문 끝자락 → 사건 목록 (utils/tailSpread.js): 누른 순간의 끝자락(기울기 · 들썩임 포함)을 맨 위 층에 본떠 바로 움직인다. 한 장처럼 보이는 둘(본뜬 끝자락 · 그 밑의 사건 목록 지면)이 같은 경로로 움직이고, 구간을 이어 붙이지 않고 동시에 겹친다.

갈래 곡선 진행 구간
올라감 iOS 서랍 .32, .72, 0, 1 0 ~ 100%
커짐 · 기울기 풀림 ease-in-out .65, 0, .35, 1 0 ~ 100%
벽선 열림 smoothstep 30 ~ 80%
지면 교체(흐림 최대 3px) smoothstep 29 ~ 67%
메인 흐려짐 선형 30 ~ 100%

전체 길이는 모든 너비 1.1s(두 지면 배치 차이가 커 바뀌는 과정을 보여 줌. 중간 너비 기준으로 맞춤, 사용자). 지면 교체 직전까지 사건 목록이 덜 그려졌으면 그 자리에서 기다린다(최대 1.2s). 화면 전환이라 일반 UI 규칙(300ms 이하)보다 길다.

Do's and Don'ts

Do:

  • Do 화면 CSS는 --color-* 역할 토큰만 쓴다. 그림자 · 투명도는 color-mix(in srgb, var(--토큰) N%, transparent)로 만든다.
  • Do 어두운 면 위 글자 · 주 버튼 · 포커스 링은 뒤집어 쓴다(종이색 글자, 옅은 놋쇠 포커스 링).
  • Do 호버는 요소 종류별 패턴(P1 ~ P6)을 따르고, 예외는 P7 표에 이유와 함께 더한다.
  • Do 색 뜻은 기호나 문장과 함께 전한다(가중 ↑, 감경 ↓, 오류 문구, 세 판결 라벨).
  • Do 연출에는 건너뛰기 · 키보드 진행 · prefers-reduced-motion을 지키고, 연출을 건너뛰어도 판단에 필요한 정보는 모두 읽히게 한다.
  • Do 화면 하나의 분위기를 바꿀 때는 그 화면의 최상위 클래스 안에서만 역할 토큰을 덮어쓴다.
  • Do 새 표시 · 아이콘 · 장식은 책상 위에 있을 물건(도장 · 클립 · 서류 · 명패)으로 만든다.
  • Do 되돌릴 수 없는 동작은 주의 색(--color-caution)으로 알리고, 확인 상자에서 동의를 체크해야 실행 버튼이 켜지게 한다.

Don't:

  • Don't 판결의 방향을 색이나 연출로 암시하지 않는다. 세 판결의 색은 선 · 라벨에만 쓰고 바탕을 칠하지 않는다.
  • Don't 빨강을 오류 아닌 곳에 쓰지 않는다.
  • Don't 브론즈(--color-decor)를 작은 글자에 쓰지 않는다.
  • Don't 카드 안에 카드를 두지 않는다. 일반 카드에 책상 물건 같은 그림자를 입히지 않는다.
  • Don't 이름과 용도가 다른 토큰을 빌려 쓰지 않는다.
  • Don't 집무실 밖 은유(왕관 등)로 표시를 만들지 않는다. 빨간 인주도 쓰지 않는다(빨강은 오류에만).
  • Don't 피해 · 폭력 장면이나 화난 표정을 그리지 않는다. 사건 · 피해자를 희화화하지 않는다.
  • Don't 호버를 @media (hover: hover) and (pointer: fine) 밖에 쓰지 않는다(터치에서 달라붙는다).
  • Don't 한 규칙에 translate와 transform을 같이 쓰지 않는다. 배포 빌드(Vite 8의 lightningcss 압축)가 translate를 지운다. 기울임은 rotate:, 크기는 scale:로 나눠 쓴다(FE-91).

현재 코드와 차이

규칙(위)과 코드가 다른 곳이다. 2026-10-22, develop 7b97ceb 기준 정적 점검이며 수치는 ±몇 개 오차가 있을 수 있다. 정리는 마감 직전 일괄로 한다(여러 팀원이 같은 시기에 화면 CSS를 고쳐 충돌 위험이 크다).

번호 항목 현황 상태
1 호버 가드 호버 규칙 59개 중 @media (hover: hover) 없는 것 22개. 공통 버튼(.primary-button · .secondary-button)은 FE-152에서 감쌌고, 화면별은 FE-153 ~ 159에서 정리 FE-151 진행 중
2 P4 글자 링크 평소 밑줄 있는 5개(login-link · footer-link · admin-link · cm-text-button · stage-skip)가 아직 옛 방식 임시 확정, 하나씩 확인
3 호버 틴트 kraft 16% 외에 4 ~ 5종이 흩어짐 미정리
4 글자 크기 토큰 122곳 대비 px 직접 207곳, 눈금(34/28/22/18/15/13/12)에 없는 14 · 11 · 10 · 16 · 17 · 24px 다수 미정리
5 간격 눈금 없음(안 쓰던 --space-md는 FE-76에서 삭제) 눈금 결정 필요
6 border-radius 34형태 눈금 결정 필요
7 box-shadow 71종 눈금 결정 필요
8 easing 8종 미정리
9 z-index 22값 미정리
10 모바일 분기점 5종(600 · 680 · 720 · 760 · 768) → 기준을 720 / 721로 정함(위 Layout "분기점 기준"). 공통(style.css · 약관/개인정보)은 FE-152에서 맞췄고, 화면별 CSS는 FE-153 ~ 159에서 맞춘다 FE-151 진행 중
11 버튼 최소 높이 44 · 40 · 38 · 36 · 34 · 32px 6종(44px이 다수) 미정
12 비활성 opacity .6 · .55 · .5 · .45 혼재, :disabled와 aria-disabled 혼용. 판결 is-off는 .55 + 흑백 + not-allowed .6 통일 제안, is-off 별도 확인
13 outline-offset 8종(2px 21곳, 4px 10곳 등) 미정리
14 버튼 중복 정의 .primary-button · .secondary-button을 9개 파일이 각자 정의 구조 결정 필요
15 직접 쓴 색값 6개, 대부분 마스크용 #000(myPage.css · pageTurn.css). myPage.css의 --axis-strict #2f5592, --axis-lenient #a8652f도 직접 값이다. 같은 값이 --color-axis-* 토큰으로 생겼고(성향 결과가 사용 중) 마이페이지는 아직 옛 값을 쓴다 마이페이지를 --color-axis-*로 바꾸고 직접 값 삭제
16 헤더 작은 버튼 로그인 36px 등 44px 미만 터치 대상 점검 때 확인
17 대화상자 배경 ::backdrop이 oklch(0 0 0 / .55 ~ .6)로 색 값을 직접 쓴다(마이페이지 다이얼로그 3곳: 꾸미기 · 컬렉션 확대 · 재심 확인) 역할 토큰으로 바꾸거나 예외 이유를 적는다
18 공개 카드 저장 위치 공개 카드 · 카드 아래 성향 · 한 줄 소개를 서버(P-8 PATCH /profile-settings)에 저장한다(FE-81). 카드 아래 줄 종류(성향 · 서명 · 직업 중 하나)와 레벨 접두는 서버(P-8)가 두 칸을 알려 주기 전까지 이 브라우저(localStorage)에만 저장하고, P-5 profileSettings에 cardLine이 오면 서버 값을 따르며 함께 보낸다(FE-116, 자동 감지). 커뮤니티 명함 · 랭킹도 글쓴이가 고른 줄(cardLine)과 레벨 접두를 보인다. 마이페이지 첫 화면에는 경험치 진행률 막대(xp · currentLevelXp · nextLevelXp)를 둔다. 직업은 서버(L-2 PUT /level/job)에 저장하고 카드 꾸미기 창에서 고른다(안 열린 직업은 비활성) 줄 종류 서버 확장은 후속
19 종이 계열 흰색 흰색 카드 #ffffff · 밝은 종이 #fdfcfa(페이지) · 부드러운 종이 #f7f3ee(옅은 면, 책상 종이)가 남아 있다. 책상 종이(#f6f3ec)는 부드러운 종이와 합쳤다. 카드 · 페이지 · 면은 계층을 만들어 합치지 않았다 계층을 합칠지 결정 필요
20 종이 위 보조 버튼 호버 성향 테스트만 kraft 16% + 진한 테두리로 바꿨고, 판결 체험 · 신문 지면 등 다른 종이 위 화면은 전역(흰색 → 부드러운 종이) 그대로라 호버가 안 보인다 다른 종이 위 화면에 확대할지 결정
21 진행 책갈피 중복 정의 공용 부품(components/folderParts.css · .js)으로 합쳤다. 모양은 성향 쪽(폭 28 · 얼굴 24 · 둥근 머리), 길이만 화면별(FE-96). 종이 귀도 같은 파일로 합쳤다 정리됨
22 크기 기준 혼재 화면마다 기준이 다르다(위 "크기 · 자리 기준"). 메인 집무실은 책상 배율 · 방 폭(cqw) · 고정 지면 × zoom · px가 섞이고, 같은 "지면"도 S-02는 px 상한 + vw, 6막 · 성향 신문지는 --u, 무대는 cqw다. 자체 단위 이름(--u)이 화면마다 뜻이 다르다(메인 러그 W / 300, 6막 1cqw 또는 화면 폭 / 118, 마이페이지 결과 × 1.12) 기록만, 새 요소는 표를 보고 고른다
23 모바일 예외(FE-151) 390px 점검에서 일부러 남긴 것: 개발용 .mock-tag 11px · 프로필카드 복제본(커뮤니티 시상대 · 명함 · 마이페이지) 안 글자 7 ~ 10px(카드 그림이라 cqi 비례 유지) · 마이페이지 차트 점 33px(점 간격 29px, "표로 보기"가 대안) · 메인 판단 요소 표 머리 11px · 메인 러그 서류철이 600 ~ 720px에서 오른쪽으로 잘림 · 판결 파일철 601 ~ 720px 탭은 번호만 · 관리자 표 조작 칸은 가로 스크롤로 닿음. 성향 철 올라오기 · 커피잔 다이브는 모바일에서도 돈다(화면을 통째로 돌리는 연출이 아님) 예외로 확정
24 호버 틴트가 P2와 다른 곳 관리자 메뉴(--color-accent-tint) · 커뮤니티 .cm-filter · .cm-action · .cm-attach(--color-surface)가 P2(kraft 16%)와 다르다. FE-151은 호버 가드로 감싸기만 하고 색은 그대로 뒀다 미정리(3번과 함께)
25 태블릿 구간 · 가로 스크롤 표시 721 ~ 1000px 1막 · 6막 신문 글자 작음 · 신문 16유형 갤러리 · 머리기사 단 좁음(FE-160), 모바일 가로 스크롤 줄(신문 면 탭 · 면 색인)에 "더 있음" 표시 없음(FE-161) 후속 이슈

FE-76에서 이미 정리한 것: 안 쓰는 토큰 3개 삭제(--palette-navy-100 · --color-on-case · --space-md), --color-desk-* 주석을 실제 사용에 맞게 수정, .login-error 글자색을 --color-danger-on-dark로 변경.

이 문서를 고치는 법

  • 코드를 바꿨는데 문서와 어긋나면 코드가 맞고 문서가 틀린 것인지 먼저 정한다. 새 규칙이면 문서와 코드를 같은 PR에서 고친다.
  • "현재 코드와 차이" 항목을 정리하면 상태를 갱신하고, 규칙이 확정되면 위 해당 섹션으로 옮긴다.
  • 프론트매터의 색 값은 style.css 팔레트와 같아야 한다. 값이 바뀌면 두 곳을 함께 고친다.