새 컴포넌트 5규칙

새 화면이나 재사용 컴포넌트를 만들 때 따르는 5가지 규칙입니다. 자주 어겨지면 디자인 일관성이 빠르게 무너지는 항목만 추렸습니다.


규칙 1 — 색은 토큰으로, hex는 마지막 수단

Do

// MUI palette 토큰 사용
<Button color="main">조회</Button>
<Box sx={{ color: 'text.secondary', bgcolor: 'background.paper' }} />

// theme 안에서는 palette 참조
const StyledBox = styled('div')(({ theme }) => ({
  color: theme.palette.text.main,
  background: theme.palette.brand.main,
}));

Don't

// hex 직접 박지 마세요
<Box sx={{ color: '#1A1818', bgcolor: '#fafafa' }} />

// 임의 색을 도입하지 마세요
<Box sx={{ color: '#2B5CE0' }} />  // 우리 palette에 없음

예외 — 상태 색 직접 hex가 허용된 곳

Button.danger(#C8003C), Switch.brand(#098FB9) 등 컴포넌트 오버라이드 내부에서는 hex 직접 사용이 현재 컨벤션입니다. 새 컴포넌트를 만들 때는 가능한 palette 확장으로 풀어가는 게 우선이며, 부득이한 경우만 hex를 박습니다.

success/warning 색 토큰이 없습니다. 알림성 UI를 만들기 전 디자이너와 합의 → palette 확장 → 사용 순서로 진행하세요. 임의 #2E8540을 박지 마세요.


규칙 2 — 인터랙션은 알파 단계로

hover·active·selected 상태에서 새 색을 만들지 마세요. 기존 색의 알파를 단계화합니다.

base:    background: #1FA9FF;       /* 100% */
hover:   background: #1FA9FFcc;     /* 80% */
active:  background: #1FA9FFe6;     /* 90% */
disabled: background: #1FA9FF66;    /* 40% */

알파 단계 컨벤션: 1a=10%, 33=20%, 4d=30%, 66=40%, 80=50%, 99=60%, b3=70%, cc=80%, e6=90%. 이 단계만 사용하세요.

알려진 일관성 이슈: Button.danger의 hover 알파(0d=5%)가 base(1a=10%)보다 낮아 호버 시 더 흐려집니다. 새 코드에서 이 패턴을 모방하지 마세요. 상세는 spec/cone-watcher-frontend/design-system.md 8. Feedback 이슈 #1.


규칙 3 — radius는 6단계 안에서만

pill (999)  strong (16)  default (12)  soft (8)  subtle (4)  hairline (2)

새 컴포넌트는 이 6개 중 하나를 선택하세요. 6·10·14·20·28 같은 중간값은 도입 금지입니다.

상황권장 radius
큰 컨테이너(Paper류)16 (strong)
큰 인터랙션(Button·IconButton·페이지네이션)12 (default)
중간 카드(Chip·Note·Profile)8 (soft)
입력 필드·Menu4 (subtle)
Tooltip2 (hairline) — Tooltip 전용, 다른 곳에 도입 금지
검색·pill 형태 Tab·Radio thumb999 (pill)

규칙 4 — 그림자는 Dialog/Tooltip 외 금지

이 규칙은 새 컴포넌트 추가 시 가장 자주 어겨집니다. 카드·드롭다운·푸터에 그림자를 그냥 넣지 마세요.

Do

// Paper 사용 — 보더로 시각 분리, 그림자 없음
<Paper variant="outlined">...</Paper>

// 메뉴는 보더로 분리 (Menu paper 오버라이드가 자동 적용)
<Menu>...</Menu>

Don't

// 임의 그림자 금지
<Box sx={{ boxShadow: '0 2px 8px rgba(0,0,0,0.1)' }}>...</Box>

// Material 3의 5단계 elevation을 흉내내지 마세요
<Box sx={{ boxShadow: 3 }}>...</Box>  // MUI 기본 elevation

깊이감은 보더(0.5px solid/1px solid)와 배경 명도 차이로 표현합니다. Material 3는 5단계 elevation을 가지지만 우리는 의도적으로 단일 평면입니다.


규칙 5 — 인터랙션 컴포넌트엔 포커스 표시 추가

기존 코드는 disableFocusRipple이 깔려 있어 키보드 포커스가 시각적으로 보이지 않습니다(이슈 #3). 새로 만드는 인터랙션 컴포넌트는 다음 패턴을 추가하세요.

const StyledButton = styled('button')(({ theme }) => ({
  // ... 기본 스타일

  '&:focus-visible': {
    outline: `3px solid ${theme.palette.emphasis.main}`,  // #0F42C5
    outlineOffset: '2px',
  },
}));

이유:

  • :focus-visible은 마우스 클릭 시엔 안 나타나고 키보드 탭 이동에서만 표시 — 디자인 노이즈 없음
  • outlineborder와 달리 레이아웃 영향이 없음
  • outline-offset: 2px이 시각적 여유를 줌
  • Material 3 Focus indicator 사양(3px solid)을 우리 emphasis 토큰으로 치환한 결과

기존 컴포넌트 일괄 적용 여부는 별도 합의 사항입니다. 새 코드에서 먼저 시작하면 점진적 마이그레이션이 쉬워집니다.


더 깊이 들어갈 때

무엇을어디로
토큰·컴포넌트 전체 명세 (M3 비교, 이슈 목록 포함)spec/cone-watcher-frontend/design-system.md
시각 카탈로그 (브라우저로 직접 보기)cone-watcher-frontend/docs/design-system/index.html
Heka 커스텀 컴포넌트 코드cone-watcher-frontend/packages/theme/src/components/
MUI 오버라이드 전체 목록cone-watcher-frontend/packages/theme/src/options/components/mui/