Get StartedFoundationsComponentsPatternsRulesDevelopAI & ToolsUpdates

멀티 브랜드 앱

한 앱 안에서 여러 브랜드를 바꿔 가며 쓰는 방법이에요. 모든 브랜드의 토큰을 data-brand 로 스코프해서 불러와요.

브랜드 하나만 쓰는 앱이라면 설치 의 <brand>-theme 하나로 충분해요. 화이트 라벨 앱, 여러 서비스의 관리자 도구, 이 문서 사이트처럼 한 앱에서 브랜드를 바꿔야 한다면 스코프된 CSS 를 써요.

스코프된 CSS 불러오기

@rebar/tokens 는 모든 브랜드를 [data-brand="<id>"] 선택자 아래에 정의한 scoped.css 와, SEED 계열(Clay 등) 공통 Tailwind 매핑인 theme.css 를 제공해요. 바꿔 가며 쓸 수 있는 건 같은 계열의 브랜드끼리예요. Atlassian 계열(Steel·Forge)은 theme-atlas.css 와 Steel 컴포넌트를 써요.

app.css
@import 'tailwindcss';

/* 모든 브랜드 토큰 ([data-brand="<id>"]) */
@import '@rebar/tokens/scoped.css';
/* Tailwind 매핑 + shadcn 별칭 (SEED 계열 공통) */
@import '@rebar/tokens/theme.css';

@custom-variant dark (&:is(.dark *));

@rebar/tokens 는 이 모노레포의 워크스페이스 패키지예요. 같은 저장소 안의 앱이라면 "@rebar/tokens": "workspace:*" 로 의존하고, 다른 저장소라면 pnpm tokens:build 로 만든 packages/tokens/dist/ 를 복사하거나 사내 레지스트리에 배포해서 써요.

컴포넌트는 브랜드 테마 없이 설치해도 돼요. 토큰 유틸 이름이 같은 계열의 모든 브랜드에서 같기 때문이에요.

pnpm dlx shadcn@latest add <site>/r/action-button.json

data-brand 지정하기

브랜드는 가장 가까운 data-brand 조상을 따라요. 보통 <html> 에 걸고, 필요하면 특정 영역만 다른 브랜드로 바꿔요.

// 앱 전체
document.documentElement.dataset.brand = 'clay';

// 일부 영역만
<section data-brand="other-brand">
  <ActionButton>다른 브랜드 버튼</ActionButton>
</section>

포털 컴포넌트

Dialog, Bottom Sheet, Popover, Menu 처럼 포털로 <body> 아래에 그려지는 컴포넌트는 감싼 요소의 data-brand 를 따르지 않아요. 브랜드를 영역별로 나눌 때는 <html> 에 걸거나, 포털 컨테이너를 해당 영역 안으로 지정해요.

브랜드 전환 훅 만들기

브랜드 목록은 @rebar/tokens 의 brands 로 받을 수 있어요. brands 에는 모든 계열이 들어 있으니 system 으로 걸러요. 선택을 localStorage 에 저장하고 data-brand 를 바꾸는 간단한 훅이에요.

hooks/use-brand.ts
import { useEffect, useState } from 'react';
import { brands } from '@rebar/tokens';

const KEY = 'brand';
// SEED 계열 컴포넌트와 함께 쓸 수 있는 브랜드만 (Clay …)
const seedBrands = brands.filter((b) => b.system === 'seed');

export function useBrand() {
  const [brand, setBrand] = useState(() => localStorage.getItem(KEY) ?? seedBrands[0].id);

  useEffect(() => {
    document.documentElement.dataset.brand = brand;
    localStorage.setItem(KEY, brand);
  }, [brand]);

  return { brand, setBrand, brands: seedBrands };
}

서버 렌더링이나 정적 프리렌더를 쓴다면 첫 페인트 전에 data-brand 를 맞춰야 깜빡임이 없어요. <head> 에 작은 인라인 스크립트를 넣어 localStorage 값을 먼저 적용해요. 이 문서 사이트도 같은 방식을 써요.

<script>
  try {
    var b = localStorage.getItem('brand');
    if (b) document.documentElement.dataset.brand = b;
  } catch (e) {}
</script>

다크 모드와 함께 쓰기

.dark 는 data-brand 와 독립적이에요. 두 가지 조합을 모두 지원해요.

<html class="dark" data-brand="clay">…</html>
<div data-brand="clay" class="dark">…</div>

On this page