여러 디자인시스템을 살펴보기 (w/Vercel,Atlassian,Appwrite)

지금 회사에서 전체 UI를 변경하는 리브랜딩 작업을 시작했습니다.
물론 초기 스타트업이기도 하고, 런칭한 지 1년도 지나지 않았고, 상주 디자이너가 없는 상황이라
당연히 디자인시스템은 욕심이며 무리였고, shadcn/ui와 추상화된 컴포넌트 정도로 피쳐를 만들어 내며 작업을 하고 있었습니다.

리브랜딩을 하면서 새로운 컬러토큰값, 스페이싱, 라운딩 등을 정의하고 사용해야 했습니다.
시스템화 된 디자인까진 아니더라도 약속된 디자인만 있어도 공통 UI 컴포넌트 모음 정도는 만들 수 있겠다고 바랐지만 안타깝게도 그럴 상황이 안되었습니다.
변신 로봇의 운명을 가진 컴포넌트를 계속해서 만들어야 됨을 새 디자인을 보면서 직감했습니다.

저희 프론트엔드 팀은 피그마를 보면서 좌절하진 않았고, 저희끼리라도 적당히 약속된 컴포넌트를 만들어서
런타임을 확인하지 않고도 쉽게 작성하고 표현할 수 있게 한다.
라는 사명 아래에 새 컴포넌트를 만드는 작업을 시작하기로 했어요.

2달 째 깨져서 빌드가 되지 않는 스토리북도 살려보도록 하구요... (머쓱)

아무튼 디자인시스템에는 많은 철학과 깨달음이 있기에 이를 알아보고 최대한 적용할 수 있는 것들을 녹여보려고 합니다.


🍀 디자인시스템 vs UI 라이브러리

디자인시스템은 UI 모음과는 다릅니다.

Material UI, chakra 등은 UI 라이브러리라고 할 수 있습니다.

디자인시스템디자인 원칙, 브랜드 정체성, 약속, 가이드라인, 의사 결정이 포함되며 서비스나 제품의 일관성을 유지할 수 있도록 하는 시스템입니다.

UI 라이브러리를 레고 블록이라고 한다면, 디자인시스템레고 블록 + 설계도 + 지침이 합쳐진 것이라고 비유할 수 있습니다.

그래서 디자이너와 개발자가 긴밀히 협력해 만들어내야 하는 것입니다.


🐤 보편적으로 따르는 지침들

여러 자료를 살펴보니 디자인시스템에서 보편적으로 사용하는 지침들이 있어 정리해 보았습니다.

▶️ 기능과 형태는 독립적으로

  • 컴파운드 패턴을 사용
  • 역할을 분담한 여러 컴포넌트들이 모여 하나의 동작으로
  • 예시. Trigger / Content 분리
<Modal.Trigger>모달 열기</Modal.Trigger>
<Modal.Content>모달 내용<Modal.Content>

▶️ 웹 접근성을 고려하기

  • 다양한 사용자 환경에 대한 고려
  • 예시. 모든 텍스트와 배경 색상 조합은 충분한 대비율을 유지

▶️ 일관성을 유지하기

  • 디자이너, 기획자와의 소통 자주하기

▶️중요도와 의미를 분리하기

  • 컬러 토큰 등에 중요도와 의미를 부여하지 않고, 상황에 따라 유연하게 사용

  • 예시. ❌ color-danger => danger하지 않은 상황에서도 의미를 가짐

  • 예시. 🅾️ color-red-100 => 색상 정보만 가짐

  • 용도와 의미를 부여한 디자인 토큰을 사용

  • 예시. 컬러 토큰 color-red-100 === 디자인토큰 color-button-danger

  • 예시. Danger Button의 색상 변경이 필요하면 디자인 토큰에 매핑되는 컬러 토큰값을 변경해주기

▶️ 문서화 하기

  • 디자인 가이드, 컴포넌트 문서, 사용 방법 가이드 등 작성

▲ Vercel의 Geist Design System

Vercel의 Geist 디자인시스템을 알아보겠습니다.

vercel.com/geist

컬러 시스템

100부터 1000까지 100단위로 토큰값을 정의합니다. gray-100 ~ gray-1000

특이한 점은 단위마다 역할을 부여해 둔 것입니다.

  • 100~300: 배경색으로 사용
  • 400~600: border로 사용
  • 700~800: 고대비 배경색으로 사용
  • 900~1000: 텍스트 및 아이콘에 사용

아이콘 컬렉션

기본 이름 아이콘 이름에 형태에 따라 서픽스를 붙입니다.

left, right, circle, fill, square, check 등이 있습니다.

컴포넌트

여러 컴포넌트가 있는데 대표적인 몇 개를 살펴봅니다.

Button 컴포넌트는 아래의 props를 가집니다.

  • size: 크기 (small, large 등)
  • type: 특정 스타일 (error, warning 등)
  • shape: 둥글기 (square, circle 등)
  • prefix & suffix: 앞뒤에 컴포넌트 위치
  • loading, disabled, shadow: 특정 스타일

약속된 type의 스타일 버튼만 사용하는지 변경을 하면서 사용하는지는 나와있지 않지않아 궁금하네요.

<Button
  size="small"
  type="secondary"
  shape="square"
  prefix={<ArrowLeft />}
  shadow
>
  Upload
</Button>

Context MenuTriggerContent가 분리되어 작성하지만 같은 상태를 공유하는 컴파운드 패턴으로 구현되어 있습니다.

<ContextMenu>
  <ContextMenu.Trigger>Right click here</ContextMenu.Trigger>
 
  <ContextMenu.Content>
    <ContextMenu.Item>Item one</ContextMenu.Item>
  </ContextMenu.Content>
</ContextMenu>

Modal의 경우도 Body, Header, Action과 같이 역할이 분리된 컴포넌트를 합쳐 사용하도록 합니다. 하지만 Context Menu와는 다르게 Trigger는 포함되어 있지 않고, 상태를 따로 다루고 있어 어떤 이유인지 궁금하네요.

<Button onClick={() => setOpen(true)} size="small">
    Open Modal
</Button>
 
<Modal.Modal active={open} onClickOutside={() => setOpen(false)}>
    <Modal.Body>
        <Modal.Header>
            <Modal.Title>Create Token</Modal.Title>
            <Modal.Subtitle>subtitle</Modal.Subtitle>
        </Modal.Header>
 
        <Text>Some content contained within the modal.</Text>
    </Modal.Body>
 
    <Modal.Actions>
        <Modal.Action onClick={() => setOpen(false)}>Cancel</Modal.Action>
        <Modal.Action onClick={() => setOpen(false)}>Submit</Modal.Action>
    </Modal.Actions>
</Modal.Modal>

텍스트

Text는 아래 props를 가집니다.

  • size: px단위의 숫자, 반응형으로 정의 가능 (10, 12 등)
  • variant: 약속된 스타일, 반응형으로 정의 가능 (heading, label 등)
  • color: 색상 (blue-900 등)
  • as: 태그 (p, span 등)
  • truncate: 말줄임표
  • align: 정렬 (left 등)
  • monospace: monospace 폰트로 변경
<Text size={48} variant={heading-48} color="blue-900">The Evil Rabbit jumps.</Text>
<Text size={{ sm: 24, md: 32, lg: 48 }} as="p" truncate={2}>The Evil Rabbit jumps.</Text>

variant를 다양하게 정의해둔 부분들과 변형 가능한 props들이 약속된 디자인 규칙을 따르면서 확장성을 고려한 디자인 시스템이라고 생각됩니다.
디자인토큰을 정의하고 있지 않지만 variant를 사용해 비슷한 효과를 만들고 있는 점도 흥미롭습니다.

추가로...

회사에서 Badge 컴포넌트를 Geist와 비슷하게 따라해 보다가 궁금한 점이 생겼었습니다.

Geist의 Badge컴포넌트의 variants 정의를 보면 배경 color와 글자 color가 있는 형식의 디자인subtle이라는 단어를 붙입니다.
처음 보는 단어이기도 하고 궁금해서 다른 레퍼런스를 찾아보니 G마켓 디자인시스템도 똑같이 subtle이라는 단어를 사용하더군요.

회사 개발 채널에도 물어보니 덜 강조하는 색상에 사용하는 것을 보았다고 하셔서! 이렇게 또 하나 배웠습니다.


🔼 Atlassian의 Design System

Atlassian의 디자인시스템을 알아보겠습니다.

atlassian.design

컬러 시스템

Lime100, Green600 등으로 정의된 컬레 팔레트가 존재합니다.
하지만 색상 값이 아닌 특정 요구에 맞게 설계된 디자인 토큰을 사용합니다.

색상값에 맞는 디자인토큰을 사용하는 것이 아니라 의미와 용도에 따라 사용하는 것이 규칙입니다.
color.icon.informationcolor.icon.brand는 같은 색상이지만 사용처의 의미에 맞는 토큰값을 선택해야합니다.

이렇게 결정한 이유는 다크 모드 대응, 의사결정 최소화, 시스템 전반에 따른 일관성 있는 변경에 용이 등이 있다고 합니다.
개발자와 디자이너가 디자인 토큰을 쉽게 사용할 수 있는 자동화 도구가 갖춰져 있다고도 하네요. (부럽습니다...)

아이콘 컬렉션

기본 이름 아이콘 이름에 형태에 따라 서픽스를 붙입니다.

left, circle, large 등이 있습니다.

아이콘 사용법을 명시한 점이 인상적이고, 저는 어떻게 써왔는지 되돌아보게 됩니다.

  • 사용법에서 filled 아이콘은 켜진 상태를 의미하므로 기본으로 outlined 아이콘을 사용하기
  • 아이콘을 텍스트와 함께 사용해서 의미를 확실히 하기
  • 접근성을 위해 고대비 색상을 사용하기
  • 너무 많은 아이콘들을 사용하지 않기
  • 아이콘에 과도한 의미를 넣어서 확장하지 않기

컴포넌트

여기도 많은 컴포넌트가 있는데 대표적인 몇 개를 살펴봅니다.

Button 컴포넌트는 지금 변경을 하고 있어 BETA라고 표시되지만 알아보겠습니다.

  • appearance: 특정 스타일 (primary, warning 등)
  • spacing: 패딩
  • shouldFitContainer: full width
  • iconAfter && iconBefore: 아이콘 위치
  • isDisabled: 비활성화

크기를 제어하거나 색상값을 바꾸는 props가 없어서 의아했는데 Custom Button 이라는 컴포넌트를 따로 작성 중에 있다고 하네요.
disabled 라는 기본 속성 대신 isDisabled를 사용하라고 안내하는데 이유가 뭔지 궁금하네요.

<Button appearance="primary" isDisabled>
  Disabled button
</Button>

Modal dialog 컴포넌트는 Geist와 비슷하지만 컴포넌트가 직접 open여부를 제어하지 않는다는 점이 다릅니다.

<Button appearance="primary" onClick={openModal}>
    Open modal
</Button>
 
<ModalTransition>
{isOpen && (
    <Modal onClose={closeModal}>
        <ModalHeader>
            <ModalTitle>TITLE</ModalTitle>
        </ModalHeader>
        <ModalBody>
            BODY
        </ModalBody>
        <ModalFooter>
            <Button appearance="subtle" onClick={closeModal}>
                CLOSE
            </Button>
        </ModalFooter>
    </Modal>
)}
</ModalTransition>

텍스트

Heading 컴포넌트는 베타 버전으로 올라와 있는데 Text 컴포넌트 문서는 비활성화된 듯해 자세하게 알기는 어려웠습니다만
모든 제품에 일관된 경험을 주기 위해 정해진 타이포그래피를 준수하도록 합니다.

Atlassian의 디자인시스템은 사용법과 가이드라인을 컴포넌트마다 설명해두어 굉장히 유용하고 흥미롭습니다.
정말 많은 고민 끝에 만들어졌다고 느껴져서 굉장히 멋있었고, 정해진 디자인 규칙과 용법을 철저히 따르도록 하는 시스템과 문서가 인상적입니다.


💕 Appwrite의 Pink Design

Appwrite의 Pink Design 디자인시스템을 알아보겠습니다.

pink.appwrite.io

위의 2가지 디자인시스템과 다르게 오픈소스로 작성되고 있으며, React 컴포넌트가 아닌 CSS class로 사용하게 됩니다.
그래서 예제도 html로 제공되고 있습니다.

컬러 시스템

primary-100, information-50, warning-100 등 의미를 가진 컬러토큰값을 사용합니다.

특이한 점은 text-danger 등의 Special Colors도 있고,
blue-100 등의 색상을 기반으로 한 컬러토큰값도 존재합니다.

아이콘 컬렉션

기본적으로 heroicons의 서브셋입니다.

circle, narrow, sm 등의 형태를 표시하는데 suffix가 아니라 중간에 추가하는 것이 흥미롭네요.

컴포넌트

Button은 button.scss에서 스타일 정의를 확인할 수 있습니다.

아래의 형태들을 class로 제공할 수 있으며, 형태마다 사용되는 상황과 용법이 정해져 있습니다.

  • types: 특정 스타일 (is-secondary, is-text 등)
  • sizes: 크기 (is-big)
<button class="button is-secondary is-big">
  <span class="text">Secondary</span>
</button>

Modal은 modal.scss에서 스타일 정의를 확인할 수 있습니다.

header, content 등으로 class가 분리되어있으며, 네이티브 dialog 엘리먼트를 사용해 모달을 제어하도록 설명하고 있습니다.

<button class="button"><span>Open Modal</span></button>
<dialog class="modal" id="dialog">
  <form class="modal-form" method="dialog">
    <header class="modal-header">
      <div class="u-flex u-main-space-between u-cross-center u-gap-16">
        <h4 class="modal-title heading-level-5">Modal title</h4>
      </div>
    </header>
    <div class="modal-content u-small">
      <div class="modal-content-spacer"><p>Modal label text.</p></div>
    </div>
    <div class="modal-footer">
      <div class="u-flex u-main-end u-gap-16">
        <button class="button is-text"><span class="text">Button</span></button>
      </div>
    </div>
  </form>
</dialog>

텍스트

본문에는 14px, 16px 그리고 폰트 두께는 400, 600만 사용하며
heading도 레벨에 따라 스타일이 정의되어 있으며, code를 위한 스타일도 존재합니다.

link가 아닌 텍스트에는 밑줄을 사용하지 말라고 안내하고 있습니다.

<p class="body-text-1 u-bold">Body text level 1 SemiBold</p>
<p class="body-text-1">Body text level 1 Regular</p>
<p class="body-text-2 u-bold">Body text level 2 SemiBold</p>

의미와 상황에 맞는 variant들만 제공하는 것으로 보아 전체적으로 일관된 경험을 중요시한다고 생각이 들고, 접근성을 최대한 고려한 점이 인상적입니다.
React가 아니라 css를 기반으로 하기 때문에 어떤 환경에서도 쉽게 적용 가능한 점도 큰 장점이라고 생각됩니다.


개인적으로 Atlassian의 Design System의 문서가 정말 잘되어있고, 많은 고민이 녹아져있음이 느껴져서 감동적입니다. 하지만 작은 회사에서 적용하기엔 무리가 있다고 판단됩니다.
저희 회사는 shadcn/ui를 사용하고 있어, Vercel의 Geist Design System을 참고하면 좋을 듯하네요.

디자인시스템은 designsystems.surf에 모아져 있으니 참고하면 좋습니다.

다른 좋은 디자인시스템 레퍼런스가 있다면 알려주세요. 감사합니다.


레퍼런스