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;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;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;interactionEffect prop 으로 hover / press 시 표시되는 인터랙션 효과를 선택할 수 있습니다.
highlight(default):interactionColor로 채워진 인터랙션 레이어가 아이콘 뒤에 나타납니다.dim: 인터랙션 레이어를 숨기고, 아이콘 색상을interactionColor로 바꾸면서 투명도를 낮춥니다.none: 인터랙션 효과를 표시하지 않습니다.
dim 은 normal variant 에서만 동작합니다. 다른 variant 에서는 highlight 와 동일하게 동작합니다.
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;interactionColor prop 으로 인터랙션 색상을 변경할 수 있습니다. 기본값은 semantic.foreground.neutral.primary 입니다.
highlight: 인터랙션 레이어의 채움 색상으로 사용됩니다.dim: hover / press 시 아이콘 색상으로 사용됩니다.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"
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;PushBadge 와 함께 사용할 수 있습니다.
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;variant 별로 지원하는 size 가 다릅니다.
- normal
xlarge(default)largemediumsmall
- outlined, solid:
medium(default)small
- background
- 단일 사이즈(32x32) 고정.
number로만 박스 크기를 커스텀할 수 있습니다.
- 단일 사이즈(32x32) 고정.
size={number} 는 모든 variant 에서 박스(컨테이너) 크기 를 의미합니다.
아이콘 크기, normal variant 의 border-radius 는 가장 가까운 theme 토큰으로 자동 매핑됩니다.
WCAG 2.2 SC 2.5.8 Target Size 에 따라 박스 크기는 24x24 px 미만으로 줄지 않습니다.
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;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 하면 레이아웃 바깥으로 그려지는 인터랙션 레이어를 확인할 수 있습니다.
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;PushBadge 와 함께 사용할 때도 동작은 같습니다. 배지는 두 모드 모두 아이콘 모서리 에 정렬되므로, interactionOverflow 를 켜도 offsetX / offsetY 를 다시 맞출 필요가 없습니다. 인터랙션 레이어는 배지 뒤로 레이아웃 바깥에 겹쳐 그려집니다.
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;as 옵션을 통해 Link 등 다른 컴포넌트/태그로 활용할 수 있습니다.
정해진 breakpoint에 따라 아래 옵션을 override 할 수 있습니다.
- size