Docs

docs/system/components/notification-badge.md

Notification Badge

packages/ui/src/notification-badge.tsx

아이콘·버튼·메뉴 같은 UI 요소에 붙어 새로운 알림이나 읽지 않은 메시지 수를 나타낸다.

역할과 경계

  • 사용자가 아직 처리하지 않은 것이 있다는 사실을 표시한다. 읽지 않은 메시지, 확인되지 않은 알림, 아직 보지 않은 변경사항이 그 대상이다.
  • 상태와 무관한 보조 정보나 분류, 강조는 badge.md가 담당한다. 두 컴포넌트는 모양이 아니라 표시가 처리할 일을 가리키는지로 갈린다.
  • 형태는 둘이며 count 의 유무로 갈린다. count 가 없으면 8px 원이고, 있으면 높이 18px 알약이다. 18px 은 12px 글자 상자 14px 에 위아래 2px 을 더한 값이며, 한 자리 수에서도 원을 유지하도록 최소 너비를 같은 18px 로 둔다. 원은 「있다」만 말하고 알약은 몇 개인지 말한다.
  • 크기 단계를 두지 않는다. 두 형태가 8px 과 18px 로 이미 작고, 필요한 단계는 붙는 아이콘 크기에 따라 갈리므로 실제 자리가 나온 뒤에 더한다.
  • 자체 interaction 이나 state Property 를 갖지 않는다. 누르는 동작은 이 표시가 붙은 host 컨트롤이 갖는다.

Anatomy

  1. Container
  2. Count (Optional)
Notification Badge
└─ Count (Optional)

count 를 넘기지 않으면 Container 만 남아 원이 된다.

Properties

  • count: number. 없으면 점이고 있으면 개수다. 0 이하이면 아무것도 렌더하지 않는다
  • max: 99 | 999, 기본값 99. count 를 함께 넘길 때만 받는다
  • tone: accent | neutral, 기본값 accent. count 를 함께 넘길 때만 받으며 점은 accent 로 고정한다
  • stroke: boolean, 기본값 true. 배경색과 같은 2px 링이다
  • className: 붙는 위치를 받는 자리다. 컴포넌트는 위치를 소유하지 않는다
  • render: 다른 요소로 렌더한다. 누르는 동작을 허용한다는 뜻은 아니다
  • children 은 받지 않는다. 내용은 count 가 정하므로 임의 요소가 들어올 자리가 없다

tonemax 는 개수 형태에서만 뜻을 가지므로 점에 함께 넘기면 타입에서 걸린다.

Behavior

  • count0 이하이면 아무것도 렌더하지 않는다. 그래서 화면 코드가 unread > 0 && 을 쓰지 않아도 된다.
  • countmax 를 넘으면 99+999+ 로 표시한다. 표시 폭이 세 글자와 네 글자를 넘지 않는다.
  • 자체 interaction 이나 state 를 갖지 않는다. Hover·Focus·Active·Disabled 표현이 없고, 누르는 동작과 Focus 표시는 이 표시가 붙은 host 컨트롤이 갖는다.

  • tone 은 accentneutral 둘이며 기본값은 accent 다. 알림 표시는 채운 형태만 있으므로 tone 마다 배경 하나와 글자 하나뿐이다.
  • badge.md의 「색」과 같은 프리미티브 단계 규칙을 쓴다. 배경은 600 단계이고 글자는 white 다.
tone 배경 글자
accent (기본값) red-600 white
neutral neutral-600 white
  • oe-accent-background-bold-primary token 을 쓰지 않는다. 그 token 은 Light 에서 red-600, Dark 에서 red-400 이어서 Dark 에서 white 글자의 대비가 무너진다. 이름은 이 자리를 가리키지만 값이 맞지 않는다.
  • stroke 는 배경색과 같은 2px 링이며 기본값은 켜짐이다. host 와 겹치는 자리에서 경계를 벌리는 용도이고, 겹치지 않는 자리에서는 끈다.

Specification

자리
점 지름 8px
개수 높이 18px
개수 최소 너비 18px
개수 좌우 여백 4px
글자 12px Medium
모서리 완전한 원
2px oe-background-primary
  • 개수 높이 18px 은 12px 글자 상자 14px 에 위아래 2px 을 더한 값이다. 최소 너비를 같은 18px 로 두어 한 자리 수에서도 원을 유지한다.
  • 수에 tabular-nums 를 쓰지 않는다. 등폭 숫자는 열로 늘어놓은 수의 자리를 맞추는 장치인데 이 표시에는 열이 없고, 자릿수가 바뀌면 폭이 어차피 달라진다. 대신 1 의 advance 가 3 과 같은 7.5px 로 넓어져 19 처럼 섞인 수에서 자간이 벌어져 보인다. 비율 숫자에서 1 은 5.4px 다. 같은 자릿수에서 폭이 최대 2px 흔들리는 것은 감수한다.
  • 링 색은 oe-background-primary 로 고정되어 있다. 다른 Surface 위에 놓는 자리에서는 ring-* 클래스로 그 Surface 색을 덮어써야 한다. 컴포넌트는 자기가 어느 Surface 위에 있는지 알지 못한다.

Guidelines

적절한 Shape 선택하기

둘은 무엇을 말하는지로 갈린다. 같은 자리에 둘 다 놓을 수 있으므로, 자리가 아니라 전달할 정보로 고른다.

Shape 제공하는 정보 쓰는 자리
dot 처리할 것이 있다는 사실만 알린다. 수를 세게 만들지 않는다. 공간이 좁거나 정확한 수가 중요하지 않은 자리다. tone 은 accent 하나뿐이다.
count Count 를 함께 표시해 구체적인 알림 수나 상태 정보를 제공한다. 세부 정보가 중요하고 충분한 공간이 있을 때다. max 로 표시 폭의 상한을 정한다.

붙는 위치는 결합하는 요소에 맞춰 정하기

  • 컴포넌트는 모양과 색과 수만 소유한다. 아이콘·버튼·메뉴 가운데 어디에, 어느 모서리에 달릴지는 화면 코드가 relativeabsolute 로 정한다.
  • 컴포넌트가 host 를 감싸는 구조를 들고 있으면 쓰려는 자리의 Layout 을 건드리게 된다. 그래서 위치를 소유하지 않고 className 으로 받는다.
  • host 와 겹치는 자리에서는 stroke 를 켜서 배경색 링으로 경계를 벌린다. 링이 없으면 아이콘의 선과 표시의 경계가 붙어 하나로 읽힌다.

핵심 상태가 있을 때만 표시하기

  • 읽지 않은 메시지, 확인되지 않은 알림, 아직 보지 않은 변경사항처럼 사용자가 처리해야 할 상태가 있을 때만 표시한다.
  • 사용자가 그 상호작용을 완료하면 사라진다. 확인한 뒤에도 표시가 남아 있으면 처리할 것이 남았다는 뜻으로 읽히므로, 표시 자체를 믿지 않게 된다.
  • 상태와 무관한 장식이나 강조에는 쓰지 않는다. 그 자리는 badge.md가 담당한다.

최대 표시 수를 자리에 맞춰 정하기

  • max 는 붙는 자리가 정한다. 아이콘 모서리처럼 좁은 자리는 99, 목록 행처럼 넓은 자리는 999 다.
  • 넘으면 99+999+ 로 표시한다. 값을 둘로 제한해 표시 폭이 세 글자와 네 글자로 고정되므로 좁은 자리에서 넘칠 일이 없다.

0은 표시하지 않기

  • count={0} 이면 컴포넌트가 아무것도 렌더하지 않는다. 화면 코드가 unread > 0 && 을 쓰지 않아도 된다.
  • 처리할 것이 없는데 표시가 남으면 사용자가 없는 알림을 찾게 된다.

접근 이름은 host 컨트롤이 담당하기

  • 수와 뜻은 이 표시가 붙은 host 컨트롤의 접근 이름이 담는다. 아이콘 버튼이라면 그 버튼의 이름을 「알림 3개」나 「읽지 않은 메시지 3개」로 둔다.
  • 컴포넌트는 수를 텍스트로 렌더할 뿐이고 aria-hidden 이나 role="status" 를 두지 않는다. 자기가 어디에 붙는지 모르므로 「3」이 무엇의 3인지 말할 수 없다.
  • host 컨트롤의 이름에 수를 담지 않으면 스크린리더가 「3」만 읽는다. 붙이는 자리마다 이름을 함께 고친다.

남용하지 않기

  • 중요한 액션을 유도할 때만 쓰고 그 수를 최소로 유지한다.
  • 한 화면에 여러 개가 놓이면 시각적 피로를 주고 어느 것이 급한지 판단하는 것을 방해한다. 결국 전부 무시된다.
  • 우선순위가 높은 자리에만 둔다. 나머지는 화면 안에서 다른 방식으로 알린다.

현재 정의하지 않은 항목

  • 크기 단계. 붙는 아이콘 크기에 따라 8px·18px 외의 단계가 필요한지는 실제 자리가 나온 뒤에 본다.
  • 링 색을 prop 으로 받을지. 지금은 oe-background-primary 로 고정되어 있고 다른 Surface 에서는 ring-* 클래스로 덮어쓴다. 덮어쓰는 자리가 자주 나오면 Surface 색을 받는 prop 을 두는 편이 낫다.