Layout
먼저 보고, 그다음 계약을 읽습니다
Stack · Cluster · Grid · Container — inline flex/grid 재발명을 대체하는 레이아웃 프리미티브 4종.
Component intent의도와 경계 읽기
spacing scale 토큰(--space-N)에 1:1 매핑되는 얇은 레이아웃 원소 묶음이다. Stack = 세로 flex+gap, Cluster = 가로 flex+wrap+gap(툴바·헤더 액션 줄), Grid = 고정/auto-fill 반응형 grid(대시보드 카드), Container = max-width 중앙 정렬+좌우 gutter. 각 서비스(matrix 헤더의 inline `display:flex;gap`, cortex `.cortex-metric-grid` 등)가 반복 재발명하던 패턴을 SSOT 로 흡수한다. 전부 forwardRef + className 병합이며 순수 CSS 클래스로도 동일하게 소비 가능(상태 없음).
예제
SSOT에 등록된 실제 API 기준 snippet입니다. standalone 배지만 독립 실행 단위이며, fragment는 주변 state·handler 문맥을 생략합니다.
import { Stack } from "@axe/ui";<Stack gap={5} as="main"> <h1>포트폴리오</h1> <p>이번 분기 요약…</p></Stack>import { Cluster, Button } from "@axe/ui";<Cluster gap={2} justify="between"> <h2>딜 파이프라인</h2> <Cluster gap={2}> <Button variant="ghost">필터</Button> <Button variant="primary">새 딜</Button> </Cluster></Cluster>import { Grid, MetricCard } from "@axe/ui";<Grid auto min="16rem" gap={4}> <MetricCard label="TVPI" value="1.8x" /> <MetricCard label="DPI" value="0.4x" /> <MetricCard label="IRR" value="22%" /></Grid>import { Container, Stack } from "@axe/ui";<Container size="md" as="main"> <Stack gap={6}>…</Stack></Container><div class="axe-container axe-container--md"> <div class="axe-stack axe-stack--gap-5"> <div class="axe-cluster axe-cluster--gap-2 axe-cluster--justify-between"> <h2>제목</h2> <button class="axe-btn axe-btn--primary">액션</button> </div> <div class="axe-grid axe-grid--auto axe-grid--gap-4"> <div class="axe-card">…</div> <div class="axe-card">…</div> </div> </div></div>Props
TSX 소스가 진실입니다. 주요 export 컴포넌트의 public API만 노출합니다.
| 이름 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
Stack.gap | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | — | 4 | 항목 간 세로 간격(--space-N, 0=간격 없음). |
Stack.align | "start" | "center" | "end" | "stretch" | — | — | 교차축(가로) 정렬. 미지정 시 flex 기본값 stretch(modifier 클래스 없음). |
Stack.as | React.ElementType | — | "div" | 렌더 태그. main/section/header 등 시맨틱 요소로 교체. |
Cluster.gap | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | — | 3 | 항목 간 간격(--space-N). |
Cluster.align | "start" | "center" | "end" | "baseline" | "stretch" | — | "center" | 교차축(세로) 정렬. 기본 center 는 루트 클래스가 제공(명시 시에만 modifier 부여). |
Cluster.justify | "start" | "center" | "end" | "between" | — | — | 주축(가로) 분배. 미지정 시 flex-start. |
Cluster.nowrap | boolean | — | — | 줄바꿈 비활성화(한 줄 강제). 기본은 wrap. |
Cluster.as | React.ElementType | — | "div" | 렌더 태그. |
Grid.cols | 1 | 2 | 3 | 4 | 5 | 6 | — | 3 | 고정 컬럼 수. auto=false 일 때만 적용. |
Grid.gap | 0 | 1 | 2 | 3 | 4 | 5 | 6 | — | 4 | 항목 간 간격(--space-N). |
Grid.responsive | boolean | — | true | 좁은 화면에서 컬럼 단계 축소(≤1023.98px: cols≥3→2, ≤639.98px: cols≥2→1). auto 모드엔 무효. |
Grid.auto | boolean | — | — | auto-fill 모드 — cols 대신 항목 최소 폭(min)으로 컬럼 수 자동 결정. |
Grid.min | string | — | "15rem" | auto 모드 항목 최소 폭(--axe-grid-min). auto=false 면 무시. |
Container.size | "sm" | "md" | "lg" | "xl" | "full" | — | "lg" | 최대 폭 토큰. sm 40 / md 56 / lg 72 / xl 80rem / full 100%. |
Container.as | React.ElementType | — | "div" | 렌더 태그. main/section 등으로 교체. |
...rest | React.HTMLAttributes<HTMLElement> | — | — | 네 컴포넌트 공통 — className 은 병합, 나머지(id·style·data-* 등)는 루트 요소로 전파(Grid 는 HTMLDivElement). |
.axe-* 클래스 계약
React 밖에서도 같은 표면을 그리는 공개 계약입니다. stable은 minor 버전 안에서 이름을 바꾸지 않습니다.
| 클래스 | 안정성 | 용도 |
|---|---|---|
.axe-stack | stable | Stack 루트 — flex column + gap + min-width:0. |
.axe-stack--gap-{0-10} | stable | 세로 간격 = --space-N (gap-0 은 0). |
.axe-stack--align-{start|center|end|stretch} | stable | 교차축(가로) 정렬. |
.axe-cluster | stable | Cluster 루트 — flex row wrap + align-items:center + gap. |
.axe-cluster--gap-{0-10} | stable | 간격 = --space-N. |
.axe-cluster--align-{start|center|end|baseline|stretch} | stable | 교차축(세로) 정렬. |
.axe-cluster--justify-{start|center|end|between} | stable | 주축(가로) 분배. |
.axe-cluster--nowrap | stable | flex-wrap:nowrap(한 줄 강제). |
.axe-grid | stable | Grid 루트 — display:grid + gap + min-width:0. |
.axe-grid--gap-{0-6} | stable | 간격 = --space-N. |
.axe-grid--cols-{1-6} | stable | 고정 N컬럼(repeat(N, minmax(0,1fr))). |
.axe-grid--auto | stable | auto-fill 모드 — repeat(auto-fill, minmax(--axe-grid-min, 1fr)). |
.axe-grid--responsive | stable | 미디어쿼리로 cols-N 을 좁은 화면에서 단계 축소. |
.axe-container | stable | Container 루트 — max-width(--axe-container-max) 중앙 정렬 + 좌우 gutter(--space-4). |
.axe-container--{sm|md|lg|xl|full} | stable | --axe-container-max 토큰 설정(40/56/72/80rem/100%). |
접근성
키보드, ARIA, 구현 노트를 함께 검토합니다.
네 컴포넌트 모두 시맨틱 없는 표현용 컨테이너 — role/aria 를 자동 부여하지 않는다.
랜드마크가 필요한 자리(본문·헤더·내비)에는 `as`(Stack/Cluster/Container) 로 main/section/header/nav 를 지정해 문서 구조를 세운다. Grid/Stack/Cluster 는 DOM 순서를 재배치하지 않으므로 시각 순서 == 읽기/탭 순서. Stack·Cluster·Grid 루트에는 min-width:0 이 있어 긴 콘텐츠가 flex/grid 트랙을 밀어 넘치는 문제를 방지한다(Container 루트는 예외 — max-width 중앙 정렬만).