콘텐츠로 이동

Procedure Hub Design System

이 문서는 Procedure Hub 화면의 크기, 배치, 색상과 정보 노출 방식의 source of truth다. 구현은 @medisolve-ai/medikit 컴포넌트와 frontend/src/components/ui/의 공통 컴포넌트를 우선 사용한다. 임의의 화면 전용 스타일은 공통 컴포넌트로 표현할 수 없는 경우에만 추가한다.

1. 기본 원칙

  • 배경과 기본 surface는 MediKit의 밝은 neutral token을 사용한다.
  • 페이지는 사용 가능한 가로폭을 채운다. 임의의 좁은 max-width로 콘텐츠를 중앙에 가두지 않는다.
  • 색상은 상태와 선택을 설명할 때만 사용한다. 카테고리를 장식하기 위한 무지개색은 사용하지 않는다.
  • 검색, 필터, 상태, 액션은 정해진 위치를 지킨다.
  • 사용자가 일을 시작하기 전에 긴 설명부터 읽게 하지 않는다.
  • 스크롤은 동작하되 브라우저 기본 scrollbar chrome은 노출하지 않는다.

2. 타이포그래피 위계

단계 용도 크기·굵기 공통 구현
L1 페이지 제목 mobile 24px, desktop 32px, bold PageHeader, WorkspaceHeader
L1 보조 페이지 설명 mobile 15px, desktop 16px header description
L2 주요 section 제목 18px, bold PageSection
L3 panel/detail 제목 16px, bold panel 내부 h3
L4 card/list 제목, 본문, 주요 control 15px, medium 또는 bold Card, button, tab, input 내부
L5 label·보조 정보·caption 13px label, count, date, source
L6 기술 metadata 11–12px ID, hash, 짧은 code
  • 11–12px는 eyebrow, 짧은 code, 비대화형 metadata에만 쓴다.
  • 설명문, 버튼, tab, chip, 표의 주요 값은 15px 아래로 내리지 않는다.
  • 표 header와 입력 label은 13px, 표의 주요 값과 입력값은 15px를 사용한다.
  • 안전·판단 결과의 제목은 L3, 사용자가 읽고 행동해야 하는 내용은 L4 아래로 내리지 않는다.
  • 위계는 색상이나 font family가 아니라 크기와 굵기로 만든다.
  • 한 section 안에서 같은 역할의 제목은 같은 크기를 사용한다.

2.1 UI 카피와 명칭

UI 명칭은 내부 field 이름이나 구현 방식을 노출하지 않고 사용자가 실제로 읽거나 수행하는 대상을 말한다.

  • 같은 데이터는 목록·상세·empty state에서 항상 같은 이름으로 부른다.
  • badge 한 묶음은 난이도, 추천 단계, 검증 상태처럼 하나의 기준축만 사용한다. 인기도·가격·고객 등급을 한 묶음에 섞지 않는다.
  • 근거는 citation 또는 검증된 출처가 연결된 정보에만 사용한다. 출처가 없는 내부 판단·설명은 참고, 안내, 요약으로 표현한다.
  • tab은 실제로 전환되는 콘텐츠의 이름을 사용하고 선택 방식인 관점, 모드, 보기를 label로 노출하지 않는다.
  • badge·section·action label에서는 포인트, 정보, 영역처럼 의미를 추가하지 않는 단어와 근거 없는 최고, 가장 인기, VIP 표현을 피한다.
  • 버튼은 대상 + 동작 순서의 짧은 명칭을 사용한다. 같은 동작은 화면마다 다른 동사를 쓰지 않는다.
  • desktop list-detail에서 이름·가격은 현재 선택의 identity로 반복할 수 있지만, list에서 이미 보이는 보조 badge는 detail에서 반복하지 않는다. mobile처럼 list가 사라지는 상태에서는 보조 badge를 유지할 수 있다.

추천 패키지 화면의 canonical vocabulary는 frontend/src/app/recommend/recommendation-copy.ts가 소유한다.

대상 canonical 명칭
패키지 범위 기본 → 표준 → 확장 → 종합
expectedResult 핵심 요약
시술·제품 묶음 패키지 구성
환자 대상 설명 상담 안내
citation 없는 임상 판단 의료진 참고
upsellHook 추가 제안

3. Control 크기

MediKit canonical size를 그대로 사용한다.

크기 높이 용도
l 44px 페이지 검색, 주요 category selector, 전역 primary action
m 36px view tab, section action, 일반 버튼과 contextual selector
s 33px filter chip, compact secondary action
ss 28px icon-only info, close, overflow action
xl 50px modal이나 전용 form의 강조 입력에만 제한적으로 사용

Icon은 section/action 20px, inline 16px, compact metadata 12px를 기본으로 한다. 서로 나란히 놓인 control은 같은 높이 단계로 맞춘다.

4. 페이지 구조

기본 순서는 다음과 같다.

  1. PageHeader: kicker → title → description → stable status
  2. PageToolbar: search → primary selector → filters/tabs → passive status → actions
  3. main content
  4. contextual detail 또는 secondary content

PageHeader.actions는 페이지 전체에 적용되는 저장, 업로드, 생성, 이동 같은 명령만 둔다. filter나 검색을 header action에 넣지 않는다.

Control 역할과 시각 문법

역할 공통 component 시각 문법
2–3개 workspace root scope·parameter·perspective 선택 SegmentedControl grouped choice, 선택 하나만 강조
독립적인 route content view·dataset 또는 선택 entity 영역 이동 LocalTabs content 기준선과 active underline
결과 범위 축소 FilterChipGroup 독립된 compact chip
즉시 on/off Switch 현재 이진 상태를 명시
다수·저빈도 단일 선택 CategorySelect label이 있는 select/combobox
명령 실행 Button primary/secondary/destructive action 위계
  • segmented control은 선택지 수에 맞는 내용 너비를 사용하며 빈 가로폭을 채우지 않는다. 각 segment는 최소 16px의 좌우 내부 여백을 유지하고 focus 표시가 control 크기를 바꾸지 않아야 한다.
  • segmented control은 page section navigation이 아니라 현재 workspace의 기준 scope 또는 결과에 적용되는 하나의 값을 고르는 aria-pressed button group이다. root scope가 바뀌면서 종속 검색·선택이 초기화될 수 있지만, 각각 독립적으로 이름 붙일 수 있는 content view의 navigation에는 LocalTabs를 사용한다.
  • local tab의 count는 label 문장에 합치지 않고 중립 badge로 구분한다.
  • scope, local navigation, filter, switch를 같은 pill button 모양으로 통일하지 않는다.
  • component 이름이 역할과 맞지 않으면 일괄 재사용하지 않고 해당 화면의 역할부터 다시 분류한다.

입력 band 정렬과 소유 관계

  • 같은 가로 입력 band에서 서로 다른 종류의 control을 함께 배치하면 label은 위쪽을 기본으로 하고 control의 시작 높이를 맞춘다.
  • inline 왼쪽 label은 한 개의 독립 control이거나 이웃 control도 모두 같은 inline 문법을 사용할 때만 허용한다.
  • desktop에서 가로로 놓인 입력은 mobile에서 세로로 쌓더라도 label → control 순서와 영역 소유권을 유지한다.
  • 선택 수, 최대 제한, 초기화처럼 특정 control에만 적용되는 정보와 action은 해당 control의 label 또는 section header에 붙인다. 단지 오른쪽 정렬을 위해 page-level status/action slot으로 보내지 않는다.
  • 다음 route에 전달하는 값이 현재 entity가 아니라 상위 scope라면 action도 entity detail이 아니라 해당 scope의 section header가 소유한다. CTA 문구는 실제로 전달되는 대상과 결과를 정확히 말한다.

5. 검색·필터·액션 배치

PageToolbar의 slot을 의미대로 사용한다.

  • search: 항상 첫 번째, desktop 왼쪽, mobile 전체폭. 기본 폭은 288–320px다.
  • selector: 화면의 주 대상을 바꾸는 category·시술 selector. 검색 다음에 둔다.
  • filters: selector 다음. view tabs → 세부 filter 순서를 따른다.
  • status: 결과 수, 완성도, 현재 범위 같은 읽기 전용 정보. 오른쪽 끝 action 앞에 둔다.
  • actions: export, save, version, create 같은 명령. 항상 마지막/오른쪽이다.

검색이 실제로 필요한 경우는 다음 중 하나다.

  • 목록이 길어 탐색 비용을 유의미하게 줄인다.
  • 여러 field를 동시에 조회한다.
  • 사용자가 정확한 이름이나 code를 알고 직접 찾는 workflow가 반복된다.

짧은 category 목록, 이미 충분한 selector가 있는 화면, 결과가 몇 개뿐인 화면에는 검색을 넣지 않는다. 하나의 ComboBox를 담기 위해 빈 full-width card나 toolbar surface를 만들지 않는다.

6. 설명과 경고 노출

설명은 중요도에 따라 세 단계로 나눈다.

항상 노출

사용자의 현재 판단이나 안전에 즉시 영향을 주는 error, destructive warning, emergency condition만 Banner 또는 명확한 inline callout으로 유지한다.

짧게 노출

현재 section을 해석하는 데 반드시 필요한 한 문장은 section 제목 아래 description으로 둔다.

요청 시 노출

다음 정보는 제목 옆 InfoDisclosure에 넣는다.

  • 산정 방법과 기준 population
  • 적용 범위와 제외 대상
  • 긴 예외 목록
  • 데이터 출처와 methodology
  • 화면 사용법 중 자주 보지 않아도 되는 내용

InfoDisclosure는 click으로 열려야 하며 keyboard로도 접근할 수 있어야 한다. Hover-only 정보는 사용하지 않는다. 단순 hover label은 trigger의 title로 보조할 수 있다.

중앙에 긴 설명 card를 놓아 main content의 시작 위치를 아래로 밀지 않는다. 방법론 설명에 purple, green 같은 임의 강조색을 사용하지 않는다.

7. Surface와 spacing

  • 페이지 좌우 padding: mobile 16px, tablet 24px, desktop 32–40px.
  • 주요 section 간격: 32px.
  • section 제목과 콘텐츠 간격: 12px.
  • 일반 Card padding: 16px.
  • 넓은 panel padding: 20–24px.
  • compact row padding: 12px.
  • 기본 panel radius: 12px. 큰 feature container의 16px radius는 제한적으로 사용한다.
  • resting surface에는 shadow를 쓰지 않는다. Popover, dialog, menu 같은 floating overlay만 shadow를 쓴다.
  • border는 container의 구분이 실제로 필요할 때만 한 번 사용한다. 중첩된 card border를 반복하지 않는다.

8. 색상

  • primary: 선택, active navigation, primary action.
  • green/amber/orange/red: 안전도, 위험도, 성공·주의·오류 같은 상태.
  • neutral: category, 설명, 일반 metadata, 비활성 control.
  • 같은 영역에서 active color는 하나만 사용한다.
  • category마다 다른 hue를 부여하지 않는다. 구분은 label, icon, grouping과 spacing으로 만든다.
  • warning은 해결되거나 사용자가 닫을 수 있는 상태여야 한다. 영구 참고문은 warning surface가 아니다.
  • 안전정보 목록은 상태 제목과 왼쪽 rail에만 semantic color를 사용하고 본문은 neutral text로 표시한다.
  • 넓은 tinted warning surface는 현재 action 전체를 막는 critical state에만 사용한다.
  • semantic rail·border·fill에는 원색 token을 유지하되, 작은 상태 제목과 badge의 foreground는 *-strong token을 사용해 밝은 surface에서 최소 4.5:1 대비를 확보한다.

9. Navigation

  • expanded sidebar는 group label + icon + item label을 보여준다.
  • collapsed sidebar는 의미 있는 icon만 보여주고 Tooltip으로 label을 보완한다.
  • 의미 없는 dot 목록, floating collapse handle, 별도 검색 이동 bar는 사용하지 않는다.
  • active item은 primary tint와 명확한 selection indicator를 사용한다.

10. 접근성

조회·선택·체크는 서버 저장이나 임상 검토 완료를 의미하지 않는다. 동의·사전 체크의 현재 선택은 화면의 임시 상태이며 저장·서명 기능처럼 표현하지 않는다. 문헌 식별자 일치와 claim의 임상적 지지는 구분한다. 근거 이동은 실제 데이터 매핑이 있을 때 제공하고, 모바일 목록 복귀 시 검색·필터·선택 맥락을 보존한다.

  • 본문 대비 4.5:1, 큰 텍스트 대비 3:1을 목표로 한다. 준수 완료를 뜻하지 않는다.
  • 키보드 탐색·포커스 표시·탭 순서를 유지한다.
  • 다국어 데이터의 줄바꿈·폰트 fallback·overflow를 확인한다.
  • 모션에는 prefers-reduced-motion 대체를 제공한다.

11. 화면 검수 체크리스트

  • 페이지 제목, section 제목, card 제목의 크기가 역할에 맞는가?
  • 같은 데이터가 목록과 상세에서 다른 이름으로 불리지 않는가?
  • badge 묶음이 하나의 기준축을 사용하며 근거 없는 우열 표현이 없는가?
  • citation 없는 설명을 근거라고 부르지 않는가?
  • 같은 역할의 버튼과 입력 높이가 같은가?
  • 검색은 필요한가? 필요하다면 toolbar의 가장 왼쪽인가?
  • filter는 검색 다음, status와 action은 오른쪽인가?
  • main content보다 먼저 보이는 긴 설명 block이 있는가?
  • 그 설명을 InfoDisclosure 또는 section description으로 줄일 수 있는가?
  • 색상이 상태가 아닌 장식 목적으로 사용됐는가?
  • 넓은 화면에서 불필요하게 빈 가로 공간이나 억지로 늘어난 control이 있는가?
  • 좁은 화면에서 text clipping, 가로 overflow, control 겹침이 없는가?
  • scrollbar를 숨겨도 keyboard, wheel, touch로 모든 콘텐츠에 접근 가능한가?