Icon button

텍스트 라벨 없이 아이콘만으로 특정 액션을 수행하는 버튼 요소입니다. 공간이 제한적이거나 사용자가 즉시 이해할 수 있는 표준적인 기능을 간결하게 제공할 때 사용합니다.

Basic icon button

아이콘을 버튼으로 사용할 때 활용합니다.

접근성을 위해 aria-label 속성을 필수로 작성하길 권장합니다.

import { FlexBox, IconButton } from '@montage-ui/core';
import { IconBlank } from '@montage-ui/icon';

const Demo = () => {
  return (
    <FlexBox gap="12px" justifyContent="center">
      <IconButton variant="normal" aria-label="Basic icon button">
        <IconBlank />
      </IconButton>
    </FlexBox>
  )
}

export default Demo;

Variants

IconButton 은 4가지 variant 를 사용할 수 있습니다.

  • normal (default)
  • background
  • outlined
  • solid
import { FlexBox, IconButton } from '@montage-ui/core';
import { IconBlank } from '@montage-ui/icon';

const Demo = () => {
  return (
    <FlexBox gap="12px" justifyContent="center" alignItems="center">
      <IconButton variant="normal" aria-label="Normal icon button">
        <IconBlank />
      </IconButton>
      <IconButton variant="background" aria-label="Background icon button">
        <IconBlank />
      </IconButton>
      <IconButton variant="outlined" aria-label="Outlined icon button">
        <IconBlank />
      </IconButton>
      <IconButton variant="solid" aria-label="Solid icon button">
        <IconBlank />
      </IconButton>
    </FlexBox>
  )
}

export default Demo;

Alternative

background variant 를 사용할 때에는 alternative prop 으로 배경 색상을 다르게 표시할 수 있습니다.

alternative prop 을 사용하면 blur 효과가 적용되지 않는 환경에서도 자연스러운 스타일을 적용할 수 있습니다.

import { FlexBox, IconButton } from '@montage-ui/core';
import { IconBlank } from '@montage-ui/icon';

const Demo = () => {
  return (
    <FlexBox gap="12px" justifyContent="center" alignItems="center">
      <IconButton variant="background" aria-label="Background icon button">
        <IconBlank />
      </IconButton>
      <IconButton variant="background" alternative aria-label="Alternative background icon button">
        <IconBlank />
      </IconButton>
    </FlexBox>
  )
}

export default Demo;

Interaction effect

interactionEffect prop 으로 hover / press 시 표시되는 인터랙션 효과를 선택할 수 있습니다.

  • highlight (default): interactionColor 로 채워진 인터랙션 레이어가 아이콘 뒤에 나타납니다.
  • dim: 인터랙션 레이어를 숨기고, 아이콘 색상을 interactionColor 로 바꾸면서 투명도를 낮춥니다.
  • none: 인터랙션 효과를 표시하지 않습니다.

dim 은 normal variant 에서만 동작합니다. 다른 variant 에서는 highlight 와 동일하게 동작합니다.

Highlight
Dim
None
import { FlexBox, IconButton, Typography } from '@montage-ui/core';
import { IconBlank } from '@montage-ui/icon';

const Demo = () => {
  return (
    <FlexBox gap="12px" flexDirection="column" alignItems="center">
      <FlexBox gap="12px" alignItems="center">
        <Typography variant="body2-reading">Highlight</Typography>
        <IconButton interactionEffect="highlight" aria-label="Highlight interaction effect icon button">
          <IconBlank />
        </IconButton>
      </FlexBox>

      <FlexBox gap="12px" alignItems="center">
        <Typography variant="body2-reading">Dim</Typography>
        <IconButton interactionEffect="dim" aria-label="Dim interaction effect icon button">
          <IconBlank />
        </IconButton>
      </FlexBox>

      <FlexBox gap="12px" alignItems="center">
        <Typography variant="body2-reading">None</Typography>
        <IconButton interactionEffect="none" aria-label="No interaction effect icon button">
          <IconBlank />
        </IconButton>
      </FlexBox>
    </FlexBox>
  )
}

export default Demo;

Interaction color

interactionColor prop 으로 인터랙션 색상을 변경할 수 있습니다. 기본값은 semantic.foreground.neutral.primary 입니다.

  • highlight: 인터랙션 레이어의 채움 색상으로 사용됩니다.
  • dim: hover / press 시 아이콘 색상으로 사용됩니다.
  • none: 무시됩니다.
Highlight
Dim
import { FlexBox, IconButton, Typography } from '@montage-ui/core';
import { IconBlank } from '@montage-ui/icon';

const Demo = () => {
  return (
    <FlexBox gap="12px" flexDirection="column" alignItems="center">
      <FlexBox gap="12px" alignItems="center">
        <Typography variant="body2-reading">Highlight</Typography>
        <IconButton
          interactionEffect="highlight"
          interactionColor="semantic.foreground.brand.primary"
          aria-label="Highlight interaction effect icon button with brand interaction color"
        >
          <IconBlank />
        </IconButton>
      </FlexBox>

      <FlexBox gap="12px" alignItems="center">
        <Typography variant="body2-reading">Dim</Typography>
        <IconButton
          interactionEffect="dim"
          interactionColor="semantic.foreground.brand.primary"
          aria-label="Dim interaction effect icon button with brand interaction color"
        >
          <IconBlank />
        </IconButton>
      </FlexBox>
    </FlexBox>
  )
}

export default Demo;

With push badge

PushBadge 와 함께 사용할 수 있습니다.

N
3
99+
import { FlexBox, IconButton, PushBadge } from '@montage-ui/core';
import { IconBlank } from '@montage-ui/icon';

const Demo = () => {
  return (
    <FlexBox gap="12px" justifyContent="center" alignItems="center">
      <PushBadge variant="dot">
        <IconButton aria-label="Icon button with dot badge">
          <IconBlank />
        </IconButton>
      </PushBadge>
      <PushBadge variant="text" text="N">
        <IconButton aria-label="Icon button with text N badge">
          <IconBlank />
        </IconButton>
      </PushBadge>
      <PushBadge variant="text" text={3}>
        <IconButton aria-label="Icon button with text badge">
          <IconBlank />
        </IconButton>
      </PushBadge>
      <PushBadge variant="max-count" text={100}>
        <IconButton aria-label="Icon button with max count badge">
          <IconBlank />
        </IconButton>
      </PushBadge>
    </FlexBox>
  )
}

export default Demo;

Sizes

variant 별로 지원하는 size 가 다릅니다.

  • normal
    • xlarge (default)
    • large
    • medium
    • small
  • outlined, solid:
    • medium (default)
    • small
  • background
    • 단일 사이즈(32x32) 고정. number 로만 박스 크기를 커스텀할 수 있습니다.

size={number} 는 모든 variant 에서 박스(컨테이너) 크기 를 의미합니다.

아이콘 크기, normal variant 의 border-radius 는 가장 가까운 theme 토큰으로 자동 매핑됩니다. WCAG 2.2 SC 2.5.8 Target Size 에 따라 박스 크기는 24x24 px 미만으로 줄지 않습니다.

Default
Small
Medium
Large
XLarge
24(Number)
import { TextButton, FlexBox, IconButton, Typography, Slider } from '@montage-ui/core';
import { IconBlank } from '@montage-ui/icon';
import { useState } from 'react';

const Demo = () => {
const [num, setNum] = useState(24);

return (
  <FlexBox gap="12px" flexDirection="column" alignItems="center">
    <FlexBox gap="12px" alignItems="center">
      <Typography variant="body2-reading">Default</Typography>
      <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
        <IconButton variant="normal" aria-label="Default size normal icon button">
          <IconBlank />
        </IconButton>
        <IconButton variant="background" aria-label="Default size background icon button">
          <IconBlank />
        </IconButton>
        <IconButton variant="outlined" aria-label="Default size outlined icon button">
          <IconBlank />
        </IconButton>
        <IconButton variant="solid" aria-label="Default size solid icon button">
          <IconBlank />
        </IconButton>
      </FlexBox>
    </FlexBox>

    <FlexBox gap="12px" alignItems="center">
      <Typography variant="body2-reading">Small</Typography>
      <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
        <IconButton variant="normal" size="small" aria-label="Small size normal icon button">
          <IconBlank />
        </IconButton>
        <IconButton variant="outlined" size="small" aria-label="Small size outlined icon button">
          <IconBlank />
        </IconButton>
        <IconButton variant="solid" size="small" aria-label="Small size solid icon button">
          <IconBlank />
        </IconButton>
      </FlexBox>
    </FlexBox>

    <FlexBox gap="12px" alignItems="center">
      <Typography variant="body2-reading">Medium</Typography>
      <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
        <IconButton size="medium" variant="normal" aria-label="Medium normal icon button">
          <IconBlank />
        </IconButton>
        <IconButton size="medium" variant="outlined" aria-label="Medium outlined icon button">
          <IconBlank />
        </IconButton>
        <IconButton size="medium" variant="solid" aria-label="Medium solid icon button">
          <IconBlank />
        </IconButton>
      </FlexBox>
    </FlexBox>

    <FlexBox gap="12px" alignItems="center">
      <Typography variant="body2-reading">Large</Typography>
      <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
        <IconButton size="large" variant="normal" aria-label="Large normal icon button">
          <IconBlank />
        </IconButton>
      </FlexBox>
    </FlexBox>

    <FlexBox gap="12px" alignItems="center">
      <Typography variant="body2-reading">XLarge</Typography>
      <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
        <IconButton size="xlarge" variant="normal" aria-label="XLarge normal icon button">
          <IconBlank />
        </IconButton>
      </FlexBox>
    </FlexBox>

    <FlexBox gap="12px" alignItems="center">
      <Typography variant="body2-reading">{num}(Number)</Typography>
      <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
        <IconButton size={num} variant="normal" aria-label={`${num} normal icon button`}>
          <IconBlank />
        </IconButton>
        <IconButton size={num} variant="background" aria-label={`${num} background icon button`}>
          <IconBlank />
        </IconButton>
        <IconButton size={num} variant="outlined" aria-label={`${num} outlined icon button`}>
          <IconBlank />
        </IconButton>
        <IconButton size={num} variant="solid" aria-label={`${num} solid icon button`}>
          <IconBlank />
        </IconButton>
      </FlexBox>
    </FlexBox>

    <Slider value={[num]} min={4} max={128} onValueChange={([a]) => setNum(a)} />
  </FlexBox>
)
}

export default Demo;

Interaction overflow

interactionOverflow prop 을 사용하면 normal variant 에서 인터랙션 영역이 레이아웃 밖으로 넘치도록 할 수 있습니다. 기본값은 false 입니다.

  • false: 레이아웃이 컨테이너 크기입니다. (xlarge 기준 36px)

  • true: 레이아웃이 아이콘 크기가 되고, 인터렉션 영역이 레이아웃 밖으로 넘치게 그려집니다.

  • 문자열 size 는 해당 프리셋의 아이콘 크기 가 레이아웃이 됩니다.

  • size={number} 는 아이콘 크기 로 적용됩니다. 컨테이너 크기는 아래 식으로 정해집니다.

    • 컨테이너 = max(24, ceil(아이콘 × 1.5 ÷ 4) × 4) — 아이콘의 1.5배를 4의 배수로 올린 값이며, 24 미만으로 줄지 않습니다. (WCAG 2.2 SC 2.5.8)
    • 네 프리셋(16 → 24, 18 → 28, 20 → 32, 24 → 36)이 이 식으로 그대로 재현되고, 아이콘이 커질수록 컨테이너가 작아지는 일은 없습니다.
    • Radius 는 컨테이너 × 0.3 에 가장 가까운 radius 토큰입니다. (동점이면 작은 쪽)
  • interactionEffect, disabled 와 함께 사용할 수 있습니다.

  • normal 이외의 variant 에서는 무시됩니다.

아래 데모에서 점선은 버튼이 실제로 차지하는 레이아웃 영역입니다. 각 행의 왼쪽은 interactionOverflow 를 켠 버튼, 오른쪽은 같은 size 의 기본 버튼입니다. hover 하면 레이아웃 바깥으로 그려지는 인터랙션 레이어를 확인할 수 있습니다.

Default
Medium
Small
24(Number)
import { FlexBox, IconButton, Slider, Typography } from '@montage-ui/core';
import { IconBlank } from '@montage-ui/icon';
import { useState } from 'react';

const Frame = ({ children }) => {
return (
  <FlexBox sx={{ outline: '1px dashed currentColor' }}>
    {children}
  </FlexBox>
);
};

const Demo = () => {
const [num, setNum] = useState(24);

return (
  <FlexBox gap="12px" flexDirection="column" alignItems="center">
    <FlexBox gap="12px" alignItems="center">
      <Typography variant="body2-reading">Default</Typography>
      <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
        <Frame>
          <IconButton interactionOverflow aria-label="Default size overflow icon button">
            <IconBlank />
          </IconButton>
        </Frame>
        <Frame>
          <IconButton aria-label="Default size icon button">
            <IconBlank />
          </IconButton>
        </Frame>
      </FlexBox>
    </FlexBox>

    <FlexBox gap="12px" alignItems="center">
      <Typography variant="body2-reading">Medium</Typography>
      <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
        <Frame>
          <IconButton size="medium" interactionOverflow aria-label="Medium size overflow icon button">
            <IconBlank />
          </IconButton>
        </Frame>
        <Frame>
          <IconButton size="medium" aria-label="Medium size icon button">
            <IconBlank />
          </IconButton>
        </Frame>
      </FlexBox>
    </FlexBox>

    <FlexBox gap="12px" alignItems="center">
      <Typography variant="body2-reading">Small</Typography>
      <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
        <Frame>
          <IconButton size="small" interactionOverflow aria-label="Small size overflow icon button">
            <IconBlank />
          </IconButton>
        </Frame>
        <Frame>
          <IconButton size="small" aria-label="Small size icon button">
            <IconBlank />
          </IconButton>
        </Frame>
      </FlexBox>
    </FlexBox>

    <FlexBox gap="12px" alignItems="center">
      <Typography variant="body2-reading">{num}(Number)</Typography>
      <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
        <Frame>
          <IconButton size={num} interactionOverflow aria-label={`${num} overflow icon button`}>
            <IconBlank />
          </IconButton>
        </Frame>
        <Frame>
          <IconButton size={num} aria-label={`${num} icon button`}>
            <IconBlank />
          </IconButton>
        </Frame>
      </FlexBox>
    </FlexBox>

    <Slider value={[num]} min={4} max={128} onValueChange={([a]) => setNum(a)} />
  </FlexBox>
)
}

export default Demo;

With push badge

PushBadge 와 함께 사용할 때도 동작은 같습니다. 배지는 두 모드 모두 아이콘 모서리 에 정렬되므로, interactionOverflow 를 켜도 offsetX / offsetY 를 다시 맞출 필요가 없습니다. 인터랙션 레이어는 배지 뒤로 레이아웃 바깥에 겹쳐 그려집니다.

Overflow
N
3
99+
Default
N
3
99+
import { FlexBox, IconButton, PushBadge, Typography } from '@montage-ui/core';
import { IconBlank } from '@montage-ui/icon';

const Demo = () => {
  return (
    <FlexBox gap="12px" flexDirection="column" alignItems="center">
      <FlexBox gap="12px" alignItems="center">
        <Typography variant="body2-reading">Overflow</Typography>
        <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
          <PushBadge variant="dot">
            <IconButton size={24} interactionOverflow aria-label="Overflow icon button with dot badge">
              <IconBlank />
            </IconButton>
          </PushBadge>
          <PushBadge variant="text" text="N">
            <IconButton size={24} interactionOverflow aria-label="Overflow icon button with text N badge">
              <IconBlank />
            </IconButton>
          </PushBadge>
          <PushBadge variant="text" text={3}>
            <IconButton size={24} interactionOverflow aria-label="Overflow icon button with text badge">
              <IconBlank />
            </IconButton>
          </PushBadge>
          <PushBadge variant="max-count" text={100}>
            <IconButton size={24} interactionOverflow aria-label="Overflow icon button with max count badge">
              <IconBlank />
            </IconButton>
          </PushBadge>
        </FlexBox>
      </FlexBox>

      <FlexBox gap="12px" alignItems="center">
        <Typography variant="body2-reading">Default</Typography>
        <FlexBox gap="12px" alignItems="center" flexWrap="wrap">
          <PushBadge variant="dot">
            <IconButton aria-label="Icon button with dot badge">
              <IconBlank />
            </IconButton>
          </PushBadge>
          <PushBadge variant="text" text="N">
            <IconButton aria-label="Icon button with text N badge">
              <IconBlank />
            </IconButton>
          </PushBadge>
          <PushBadge variant="text" text={3}>
            <IconButton aria-label="Icon button with text badge">
              <IconBlank />
            </IconButton>
          </PushBadge>
          <PushBadge variant="max-count" text={100}>
            <IconButton aria-label="Icon button with max count badge">
              <IconBlank />
            </IconButton>
          </PushBadge>
        </FlexBox>
      </FlexBox>
    </FlexBox>
  )
}

export default Demo;

API

as 옵션을 통해 Link 등 다른 컴포넌트/태그로 활용할 수 있습니다.

정해진 breakpoint에 따라 아래 옵션을 override 할 수 있습니다.

  • size
NameTypesdefaultValue
as
ElementType
-
variant
"normal" | "background" | "solid" | "outlined"
"normal"
disabled
boolean
false
size
number | "small" | "medium" | "large" | "xlarge"
-
interactionOverflow
boolean
-
color
ThemeColorsToken
-
interactionEffect
"none" | "highlight" | "dim"
-
interactionColor
ThemeColorsToken
"semantic.foreground.neutral.primary"
alternative
boolean
-
children
ReactNode
-
sx
SxProp
-
xl
Merge<Pick<IconButtonDefaultProps, "size">, { sx?: CSSInterpolation; }> | undefined
-
lg
Merge<Pick<IconButtonDefaultProps, "size">, { sx?: CSSInterpolation; }> | undefined
-
md
Merge<Pick<IconButtonDefaultProps, "size">, { sx?: CSSInterpolation; }> | undefined
-
sm
Merge<Pick<IconButtonDefaultProps, "size">, { sx?: CSSInterpolation; }> | undefined
-
xs
Merge<Pick<IconButtonDefaultProps, "size">, { sx?: CSSInterpolation; }> | undefined
-

© 2026 Wanted Lab, Inc.