AXISMUNDI Style Guide
AXISMUNDI
Style Guide
Theme
Style Guide

Axismundi Style Guide v0.3.0

Monorepo cycle: v3.5.17

Material 3 기반의 한국어 우선 디자인 시스템. 타입 토큰, 색상 토큰, 컴포넌트 사양을 한 페이지에 모았습니다. 왼쪽 테마 스위처로 라이트/다크/시스템 자동 전환을 확인하세요.

Foundation

Color

Read more

38 system color roles per scheme. All values resolve via var(--md-sys-color-*) tokens. Switch theme above to see light, dark, or system-auto. The side-by-side preview on each subsection forces both schemes simultaneously.

Brand

현재 테마 색상

상단 테마 토글 (Light/Dark/Auto)에 따라 sys 토큰이 동적으로 변경됩니다.

Status

Surface

Inverse

Outline & Other

Foundation

Typography

15 type roles. Each row uses the matching .t-{role} utility class — no literal sizes in markup. Samples include both English and 한글 to verify mixed-script line height.

Typography axis specimen
Components

Button

Validation specimen

Variants — label only

<button class="ax-button is-filled has-state-layer t-label-large">
  Filled
</button>

With leading icon

Toggle (4 variants — text excluded)

Click to toggle. Text variant doesn't support toggle per spec.

Disabled (Pattern A · §0.8)

Components

Icon button

Validation specimen

Variants

<button class="ax-icon-button is-filled has-state-layer" aria-label="Add">
  <span class="material-symbols-rounded notranslate" translate="no" aria-hidden="true" draggable="false">add</span>
</button>

Toggle

Per M3 §6.4: outlined toggle should swap to a filled glyph when selected. Markup is identical — author updates the Material Symbols glyph name and selected state as needed.

Disabled

Components

FAB

Floating action button. 3 sizes (default 56, medium 80, large 96) × 6 color styles. Icon-only — for label use Extended FAB.

Validation specimen

Sizes

<button class="ax-fab" aria-label="Compose"><svg ...></svg></button>
<button class="ax-fab is-medium" aria-label="Compose">...</button>
<button class="ax-fab is-large" aria-label="Compose">...</button>

Color styles

Tonal primary (default) / Tonal secondary / Tonal tertiary / Primary / Secondary / Tertiary. All visually equivalent in M3 §7.3.

Disabled

Components

Extended FAB

FAB with text label + leading icon. 56px height, min-width 80px, label uses title-medium typography.

Validation specimen

Default

<button class="ax-fab-extended">
  <svg class="ax-fab-extended__icon" ...></svg>
  <span class="ax-fab-extended__label">Compose</span>
</button>

Color styles

Disabled

Components

FAB menu

Close button (56×56) anchor + 2-6 list items stacked above. Toggle via .is-open on the wrapper. 3 color sets: Primary (default) / Secondary / Tertiary. Item enter/exit stagger animation deferred to v1.5+.

Primary — open state

Demos forced open via .is-open. In production, wire the close button's click to toggle the class.

<div class="ax-fab-menu is-open" id="compose-menu">
  <button class="ax-fab-menu__close"
          aria-expanded="true" aria-controls="compose-menu-list">
    <svg class="ax-fab-menu__close-icon-rest">...</svg>
    <svg class="ax-fab-menu__close-icon-open">...</svg>
  </button>
  <ul class="ax-fab-menu__list" id="compose-menu-list">
    <li class="ax-fab-menu__item">
      <button class="ax-fab-menu__item-button">
        <span class="ax-fab-menu__item-icon"><svg .../></span>
        <span class="ax-fab-menu__item-label">새 글</span>
      </button>
    </li>
  </ul>
</div>

Interactive — toggle via JS

Click the FAB to toggle. The script below listens to [data-fab-menu-toggle] buttons.

Components

Button group

Composes .ax-button instances. Two variants: standard (selected button widens 15% per M3 §5.6) and connected (2px gap, full-pill outer corners, small inner corners). Two markup patterns supported — radio+label for single-select (CSS-only), button+aria-pressed for multi-toggle toolbars (JS).

Validation specimen

Standard — single-select (radio + label, no JS)

Read more

Click any option — selected button widens with a spring transition. Pure CSS via :checked + .ax-button; native keyboard arrow-key navigation between options.

View mode
<fieldset class="ax-button-group">
  <legend class="u-vh">View mode</legend>

  <input type="radio" name="view" id="v-day"
         class="ax-button-group__input">
  <label  for="v-day"
         class="ax-button is-outlined has-state-layer">Day</label>

  <input type="radio" name="view" id="v-week"
         class="ax-button-group__input" checked>
  <label  for="v-week"
         class="ax-button is-outlined has-state-layer">Week</label>

  <input type="radio" name="view" id="v-month"
         class="ax-button-group__input">
  <label  for="v-month"
         class="ax-button is-outlined has-state-layer">Month</label>
</fieldset>

Connected — single-select (radio + label)

2px gap, full-pill outer corners, small inner corners. Inner corners morph to full pill when selected.

Text alignment

Toolbar — multi-toggle (button + aria-pressed, JS)

Each button toggles independently — wrong semantic for radio. Uses <button aria-pressed> with a small click handler (theme JS in production; inline demo here).

<div class="ax-button-group ax-button-group--connected"
     role="toolbar" aria-label="Text formatting">
  <button type="button" class="ax-button is-tonal has-state-layer"
          aria-pressed="true">B</button>
  <button type="button" class="ax-button is-tonal has-state-layer"
          aria-pressed="false">I</button>
  ...
</div>

<script>
// Theme JS — toggle aria-pressed on click
document.querySelectorAll('[role="toolbar"] [aria-pressed]')
  .forEach(btn => btn.addEventListener('click', () => {
    const v = btn.getAttribute('aria-pressed') === 'true';
    btn.setAttribute('aria-pressed', String(!v));
  }));
</script>
Components

Split button

Composes two .ax-button instances — primary action + dropdown trigger. 2px gap, full-pill outer + 4dp inner (default M), morphs to 8dp on hover and 50% (full pill) when trailing button menu opens.

Default — M size

두 번째 split button (공유)은 aria-expanded="true" 상태 — trailing 모서리가 50% pill로 morph.

<div class="ax-split-button" role="group">
  <button class="ax-button is-filled has-state-layer">저장</button>
  <button class="ax-button is-filled has-state-layer"
          aria-haspopup="menu" aria-expanded="false"
          aria-label="더 많은 저장 옵션">
    <svg class="ax-split-button__trailing-icon">...</svg>
  </button>
</div>

Small size

Components

Toolbar

Slot-based action surface. Two variants (docked full-width, edge-anchored / floating detached pill, level3) × two color schemes (standard surface-container / vibrant primary-container).

v0.3.5 scope: docked + floating, both color schemes. Slots accept icon-button / button / text-field / button-group. Floating-with-FAB variant deferred to v1.5+.

Docked — standard color

Floating — standard color

Floating — vibrant color

<div class="ax-toolbar ax-toolbar--floating ax-toolbar--vibrant"
     role="toolbar">
  <button class="ax-icon-button is-standard has-state-layer is-selected"
          aria-pressed="true" aria-label="Like">...</button>
  <button class="ax-icon-button is-standard has-state-layer"
          aria-label="Comment">...</button>
  ...
</div>
Components

Card

Validation specimen

Variants — rest

Filled card

surface-container-highest · level0

Resting filled surface. Use when the card sits inside another container that needs a lift.

Elevated card

surface-container-low · level1

Floats above the canvas. Hover state only matters when the card is interactive.

Outlined card

surface · 1px outline-variant

Defined by stroke, not fill. Best on busy backgrounds where elevation reads as noise.

<article class="card card--filled">
  <h4 class="card__title t-title-large">Title</h4>
  <p class="card__subtitle">Subtitle</p>
  <p>Body</p>
</article>

Interactive — hover for level1 → level2

Disabled (Pattern B · whole-container 0.38)

Locked content

aria-disabled="true"

Whole container fades to 0.38 opacity per §0.8 Pattern B.

Components

Divider

Record audit

3 variants — between text content

아주 오래된 책들 사이에서 — full-width


A long, low corridor of shelves — inset


먼지 냄새 가득한 — middle inset


그날 오후의 정적.

<hr class="ax-divider is-style-inset" />
Components

App bar

Small (64px · title-large)

Inbox

<header class="app-bar app-bar--small">
  <span class="app-bar__leading">...</span>
  <h1 class="app-bar__title t-title-large">Inbox</h1>
  <span class="app-bar__trailing">...</span>
</header>

Medium-flexible (112–136px · headline-medium)

Discover

Large-flexible (120–152px · display-small) + scrolled state

설정

Account · Notifications · Privacy

Demonstrates [data-scrolled="true"] — surface-container bg + level2 shadow.

Components

Nav bar

Mobile bottom navigation. 64px height. Active indicator pill (56×32) per §0.13. Width is 100% — frame below shows it within constrained width.

Default (4 items)

<nav class="nav-bar" aria-label="Main navigation">
  <button type="button" class="nav-bar__item is-active" aria-current="page">
    <span class="nav-bar__icon"><svg ...></svg></span>
    <span class="nav-bar__label">Home</span>
  </button>
  <button type="button" class="nav-bar__item">
    <span class="nav-bar__icon has-badge">
      <svg ...></svg>
      <span class="ax-badge is-large">3</span>
    </span>
    <span class="nav-bar__label">Notifications</span>
  </button>
</nav>
Components

Nav rail (collapsed)

96px width · 5 items · 1 active

왼쪽 rail의 항목을 클릭하면 active indicator가 이동합니다. 활성 indicator는 56×32 pill에 secondary-container 배경.
Components

Nav rail (expanded)

220–360px 폭. 아이콘 + label-large가 한 줄에 정렬되며 active indicator는 행 전체를 감싸는 full-width pill로 변환됩니다 (collapsed의 56×32 → full-row). Modal expanded variant은 별도 컴포넌트가 아니라 .nav-rail.is-expanded.sheet--side-modal 안에 넣는 컴포지션 패턴입니다.

Standalone — 220px width · 5 items · 1 active

Active indicator가 행 전체를 채우는 full-width pill로 표시됩니다. Label은 label-large typescale.

Modal mode가 필요하면 이 마크업을 .sheet--side-modal 안에 넣으세요 — composition pattern.

Components

Tabs

Primary (3px indicator · top-rounded)

<div class="tabs tabs--primary">
  <button class="tabs__tab is-active">홈</button>
  <button class="tabs__tab">탐색</button>
</div>

Secondary (2px flat indicator)

With icons (64px tall)

Components

Menu

Vertical menu (Standard color). Item slots: leading icon, label, supporting text, trailing supporting (e.g. shortcut), trailing icon. Width is intrinsic — clamped between 112px (min) and 280px (max). Static .is-open for demo; positioning to a trigger is the author's job.

Default — label-only items

<div class="ax-menu is-open" role="menu">
  <button class="ax-menu__item" role="menuitem">
    <span class="ax-menu__item-label">새 항목</span>
  </button>
  <hr class="ax-menu__divider" />
  <button class="ax-menu__item" role="menuitem">...</button>
</div>

With icons + shortcuts

With supporting text

Two-line items use .ax-menu__item-supporting inside .ax-menu__item-label.

Components

Text field

Read more

Refactored markup (v0.3.0+): outer <div class="text-field"> + visual <div class="text-field__container"> + optional <div class="text-field__bottom">. Slot grid: leading / prefix / input+label / suffix / trailing+error. Native :user-invalid drives error state without JS.

Validation specimen

Filled — 5 states

올바른 이메일 형식이 아닙니다.
<div class="text-field text-field--filled">
  <div class="text-field__container">
    <input class="text-field__input" id="tf-email" placeholder=" " />
    <label class="text-field__label" for="tf-email">Email</label>
  </div>
  <div class="text-field__bottom">
    <span class="text-field__supporting">Helper text</span>
  </div>
</div>

Outlined — 5 states

특수문자는 사용할 수 없습니다.

Prefix · Suffix · Counter · Native validation

Prefix/suffix only appear when the label has floated up (focused or populated). Try entering letters in the price field — HTML5 pattern + :user-invalid auto-trigger error after blur.

KRW
숫자만 입력해 주세요.
최대 50자. 0 / 50
<div class="text-field text-field--outlined">
  <div class="text-field__container">
    <input class="text-field__input" id="tf-price" placeholder=" "
           inputmode="numeric" pattern="[0-9]*" maxlength="10" />
    <label class="text-field__label" for="tf-price">가격</label>
    <span class="text-field__prefix">₩</span>
    <span class="text-field__suffix">KRW</span>
    <span class="text-field__error-icon">...</span>
  </div>
  <div class="text-field__bottom">
    <span class="text-field__supporting">숫자만 입력해 주세요.</span>
  </div>
</div>

Leading · Trailing icons

Leading icon (search affordance), trailing icon (clear action). Add .text-field--with-leading on the wrapper so the label clears the leading icon space. For interactive trailing buttons (clear, voice, dropdown), compose with .ax-icon-button.is-standard.has-state-layer. The clear button uses .is-clear for auto-hide-when-empty per M3 §32.

로그인에 사용할 이메일 주소
<div class="text-field text-field--outlined text-field--with-leading">
  <div class="text-field__container">
    <input class="text-field__input" id="tf-search" placeholder=" " />
    <label class="text-field__label" for="tf-search">검색</label>
    <span class="text-field__leading-icon"><svg .../></span>
    <button class="ax-icon-button is-standard has-state-layer
                   text-field__trailing-icon is-clear"
            aria-label="Clear">
      <svg .../>
    </button>
  </div>
</div>

Textarea

최대 280자. 0 / 280
Components

Checkbox

18×18 box with 40×40 state layer halo. Selected primary fill + check icon. Indeterminate state shows horizontal bar. Pressed state shows preview-of-toggle color (M3 §13.3 quirk). Bucket E — system styles native <input type="checkbox"> via custom visual.

Default states

<label class="ax-checkbox">
  <input type="checkbox" class="ax-checkbox__input" />
  <span class="ax-checkbox__visual">
    <svg class="ax-checkbox__check" viewBox="0 0 18 18" aria-hidden="true">
      <polyline points="3.5,9.5 7.5,13.5 14.5,5.5"></polyline>
    </svg>
  </span>
  <span class="ax-checkbox__label">Label</span>
</label>

Error

Disabled

Components

Radio button

20×20 outer ring + 10×10 inner dot. Same state layer + cross-color pressed quirk as checkbox. Used in radio groups for mutually-exclusive choices. Bucket E — styles native <input type="radio">.

Default group

<label class="ax-radio">
  <input type="radio" name="theme" class="ax-radio__input" />
  <span class="ax-radio__visual"></span>
  <span class="ax-radio__label">Auto</span>
</label>

Disabled

Components

Switch

Track 52×32 pill with size-morphing handle (16 unselected → 24 selected → 28 pressed). Selected = primary fill, unselected = surface-container-highest with 2px outline. State layer 40×40 follows handle. Bucket E — styles native <input type="checkbox" role="switch">.

v0.2.5 scope: track + handle + state layer. Optional icon-on-handle deferred.

Default

<label class="ax-switch">
  <input type="checkbox" role="switch" class="ax-switch__input" />
  <span class="ax-switch__track"></span>
  <span class="ax-switch__label">알림 받기</span>
</label>

Disabled

Components

Slider

XS standard horizontal slider. Track 16px secondary-container, handle 4px primary (morphs to 2px on press). Uses native <input type="range"> with vendor pseudo-elements. Active fill via accent-color (browser-native) — for full M3 fidelity, author can add JS-driven gradient.

v0.2.5 scope: XS standard horizontal. S/M/L/XL sizes, vertical, centered, range, stops, value indicator deferred.

Default

<div class="ax-slider">
  <input type="range" class="ax-slider__input" min="0" max="100" value="40" aria-label="Volume" />
</div>

Disabled

Components

Date picker

Read more

Visual structure only. Actual calendar logic (current month, navigation, validation) is the author's responsibility — typically driven by a JS library (flatpickr, vanilla-calendar) or server-rendered template. v0.4.0 = docked variant. Modal + modal-input variants reuse internals + dialog wrapper.

Validation specimen

Docked — sample static layout

날짜 선택 2026년 5월 15일

15일 = selected (primary fill), 11일 = range-start (primary fill, leading 모서리 둥글), 12-14일 = in-range band (secondary-container), 3일 = today (primary outline). 회색 날짜 = is-outside (38% opacity).

Usage patterns (reference)

Date picker는 단독 사용보다 다음 3 패턴 권장 — Jetpack Compose DatePickerDocked / DatePickerModal / DatePickerModalInput와 동일:

  • Docked — readonly text-field + trailing 달력 icon → popup으로 calendar 표시 (작은 form 안에서 inline)
  • Modal.dialog로 wrap + 취소/확인 button (full calendar grid)
  • Modal-input — dialog 안에 text-field만 (calendar 안 보이고 직접 입력 — MM/DD/YYYY 같은)

실제 popup 위치/dismiss/날짜 계산은 author/JS 영역. 이 시스템은 visual primitives만 — calendar grid, date cell 상태 (today/selected/range/outside).

Components

Time picker

Read more

Visual structure only. Input variant uses real <input type="text" inputmode="numeric"> for typeable HH/MM, and <input type="radio"> + <label> for AM/PM (single-select, CSS-only). Clock dial drag interaction stays in author code.

Validation specimen

Input variant — type to enter time

시간 입력
:
AM or PM

시 (10) = 선택됨 (primary-container 채워짐). 분 (30) = 미선택. AM/PM = radio라 CSS-only로 클릭 토글, 키보드 화살표로 이동 가능. 사이에 1px outline 색 divider.

24-hour mode — period selector hidden

.is-24h modifier로 AM/PM selector 숨김. Compose is24Hour=true와 동일.

시간 입력 (24시간)
:

Dial variant — container scaffolding only

12-hour 라벨 + selector handle은 author/JS가 SVG 또는 absolute span으로 그림.

시간 선택

Usage patterns (reference)

Date/Time picker는 단독으로 잘 안 씀. Compose가 정의한 3 패턴 중 선택:

  • Docked — text-field trailing icon 클릭 → popup으로 calendar/dial 표시 (form 안에서 inline 사용)
  • Modal.dialog로 wrap + 취소/확인 button (모바일에서 가장 흔한 패턴)
  • Modal-input — dialog 안에 text-field만 (calendar 안 보이고 직접 타이핑)

실제 wiring (popup show/hide, dialog open/close)은 author/JS 영역. 이 시스템은 visual structure만 제공.

Components

Chip

Validation specimen

Variants — 4 types

Input value
<button class="chip chip--filter has-state-layer" aria-pressed="false">
  <span class="chip__label">Filter</span>
</button>

Filter — selected

Input — with avatar leading

HK 한가람

Input chip leading slot accepts a 24px avatar (size override on the leading element).

Disabled

Components

Badge

Status indicator. Two variants: small (6×6 dot) / large (numeric label, 16×16 to 16×34). Anchored top-trailing of host. Apply .has-badge to host for positioning context.

Record audit

Small (dot)

Used to mark unread / new / changed state without exact count.

<button class="ax-icon-button is-standard has-badge" aria-label="Notifications (unread)">
  <svg ...></svg>
  <span class="ax-badge"></span>
</button>

Large (numeric)

Counts up to ~3 chars. Use 99+ for overflow.

Inline (manual placement)

Use .is-inline to opt out of absolute positioning.

새 알림 5 Updates New
Components

Dialog

Static — basic

변경 사항을 저장하시겠습니까?

저장하지 않으면 마지막 편집 이후의 변경 사항이 모두 사라집니다.

Static — full-screen

새 글 작성

전체 화면 다이얼로그는 본문 영역을 자유롭게 채울 수 있습니다.

Full-screen variant은 viewport 전체를 채우는 디자인 — 위 frame은 100% 컨테이너 fit으로 override되어 있습니다.

Live — open as real modal

실제 viewport 크기로 띄우려면 위 버튼을 사용. body scroll이 잠깁니다.

Components

Sheet

Static — bottom modal

Static — side modal

Live — open as real modal

Components

Snackbar

Validation specimen

4 variants — static

메시지가 전송되었습니다.
초안이 저장되었습니다.
서버 응답이 늦어지고 있습니다. 다시 시도하면 빠르게 처리될 수 있어요.
새 버전이 발견되었습니다. 지금 새로고침하면 최신 인터페이스를 사용할 수 있어요.
<div class="snackbar">
  <span class="snackbar__label">Saved</span>
  <button class="snackbar__action">Undo</button>
</div>
Components

Tooltip

Two variants: plain (short label, inverse-surface) / rich (subhead + body + actions, surface-container). Static .is-open for demo; positioning to a trigger is the author's job.

Validation specimen

Plain

Short, attached to a trigger element. Single-line, max 240px.

새 글 쓰기 ⌘ + N Compose new post
<span class="ax-tooltip is-plain is-open" role="tooltip">새 글 쓰기</span>

Rich

Subhead + body + up to 2 actions. Dismissible.

<div class="ax-tooltip is-rich is-open" role="tooltip">
  <span class="ax-tooltip__subhead">키보드 단축키</span>
  <span class="ax-tooltip__supporting">⌘ + N으로 새 글 작성을 빠르게 시작합니다.</span>
  <div class="ax-tooltip__actions">
    <button class="ax-tooltip__action">자세히</button>
    <button class="ax-tooltip__action">닫기</button>
  </div>
</div>
Components

Loading indicator

Replaces indeterminate circular progress for short waits. Default (uncontained) / contained variants. Auto-respects prefers-reduced-motion.

Default (uncontained, 48px)

<span class="ax-loading" role="status" aria-label="Loading">
  <svg class="ax-loading__svg" viewBox="0 0 50 50" aria-hidden="true">
    <circle class="ax-loading__circle" cx="25" cy="25" r="20"></circle>
  </svg>
</span>

Contained

Pill container with primary-container fill, 38×48px.

Small (24px) — for inline use

Useful inside buttons or near labels.

Components

Progress indicator

Linear (4px track) + circular (40px). Determinate (set --_value via JS) or indeterminate (auto-animated). prefers-reduced-motion fallback for both. Wavy variant deferred to v1.5+.

Linear — determinate

Set --_value on the element to drive fill percentage.

<div class="ax-progress-linear is-determinate"
     style="--_value: 60%;"
     role="progressbar"
     aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"></div>

Linear — indeterminate

Circular — determinate

Circular — indeterminate

<span class="ax-progress-circular is-indeterminate"
      role="progressbar" aria-label="Loading">
  <svg class="ax-progress-circular__svg" viewBox="0 0 40 40">
    <circle class="ax-progress-circular__track"  cx="20" cy="20" r="18"/>
    <circle class="ax-progress-circular__active" cx="20" cy="20" r="18"/>
  </svg>
</span>
Components

Avatar

Record audit

Sizes

지원 지원 지원
<span class="ax-avatar is-size-lg"><span>지원</span></span>

Variants

HK 도윤
Components

List

Container 1+ list items. Single/two/three-line variants. Slots: leading (icon 24 / image 56), content (overline + label + supporting), trailing (icon 24 / text). Selection via .is-selected or aria-selected="true". Expressive shape state machine: rest → corner-extra-small, hover → corner-medium, focus/pressed/selected → corner-large.

Validation specimen

Standard — single-line



<div class="ax-list" role="list">
  <button class="ax-list__item" role="listitem">
    <span class="ax-list__leading"><svg .../></span>
    <span class="ax-list__content">
      <span class="ax-list__label">홈</span>
    </span>
    <span class="ax-list__trailing-text">⌘1</span>
  </button>
  <hr class="ax-list__divider" />
</div>

Two-line — overline + label + supporting


Three-line — overline + label + 2-line supporting

Segmented — gap + per-item rounding

변경 사항을 저장하시겠습니까?

저장하지 않으면 마지막 편집 이후의 변경 사항이 모두 사라집니다.

새 글 작성

전체 화면 다이얼로그는 본문 영역을 자유롭게 채울 수 있습니다.