RadioBox

Data Input

박스 형태로 하나만 선택하는 라디오.

Usage

설명이 짧은 배타 선택지를 테두리 있는 박스 형태로 강조할 때 사용한다.

import

import

import { RadioBox } from '@mildang/design-system/RadioBox';

예제를 복사해 쓸 때 필요한 준비

@mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.

API Reference

RadioBox Props

Prop

Type

Default

value
필수

string

지정 안 함

aria-label

string

지정 안 함

className

string

지정 안 함

description

ReactNode

지정 안 함

disabled

boolean

false

panda

SystemStyleObject

지정 안 함

size

"sm" | "md"

md

thumbnail

ReactNode

지정 안 함

title

ReactNode

지정 안 함

예제

기본 사용

제목과 설명을 담은 상자형 선택지를 가로로 둡니다 — 선택지마다 부연이 필요할 때 씁니다.

import { RadioGroup, RadioBox } from '@mildang/design-system/Radio';
import { css } from '@mildang/styled-system/css';

type RadioBoxStoryArgs = {
  value: string;
  size: 'sm' | 'md';
  disabled: boolean;
  title: string;
  description: string;
};

const Thumb = ({ size }: { size: 'sm' | 'md' }) => {
  const dim = size === 'md' ? 80 : 50;
  return (
    <svg
      width={dim}
      height={dim}
      viewBox={`0 0 ${dim} ${dim}`}
      role="img"
      aria-label="thumbnail"
      className={css({
        borderRadius: 6,
        border: '1px solid token(colors.neutral.border.low)',
        background: 'neutral.surface.high',
        color: 'neutral.icon.base',
        display: 'inline-block',
      })}
    >
      <rect x="0" y="0" width={dim} height={dim} fill="transparent" />
      <g fill="currentColor">
        <circle cx={dim * 0.3} cy={dim * 0.35} r={dim * 0.1} />
        <rect x={dim * 0.45} y={dim * 0.3} width={dim * 0.25} height={dim * 0.1} rx={dim * 0.02} />
        <rect x={dim * 0.2} y={dim * 0.6} width={dim * 0.6} height={dim * 0.12} rx={dim * 0.02} />
      </g>
    </svg>
  );
};

const STORY_DEFAULT_ARGS = { ...({}), ...({
    value: 'opt1',
    size: 'md',
    disabled: false,
    title: 'Radio Title',
    description: 'Radio Description',
  }) } as RadioBoxStoryArgs;

const RadioBoxDefaultExampleRender = (args: RadioBoxStoryArgs) => (
    <RadioGroup defaultValue="opt1" aria-label="RadioBox" alignItems="flex-start">
      <RadioBox
        value="opt1"
        size={args.size}
        disabled={args.disabled}
        title={args.title}
        description={args.description}
        aria-label={!args.title ? 'Option' : undefined}
        thumbnail={<Thumb size={args.size} />}
      />
    </RadioGroup>
  );

export default function RadioBoxDefaultExample(props: Partial<RadioBoxStoryArgs>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as RadioBoxStoryArgs;
  return RadioBoxDefaultExampleRender(mergedProps);
}

개별 옵션 비활성

고를 수 없는 상자만 disabled 로 잠그고 사유를 설명 자리에 적습니다.

import { css } from '@mildang/styled-system/css';
import { RadioGroup, RadioBox } from '@mildang/design-system/Radio';

const Thumb = ({ size }: { size: 'sm' | 'md' }) => {
  const dim = size === 'md' ? 80 : 50;
  return (
    <svg
      width={dim}
      height={dim}
      viewBox={`0 0 ${dim} ${dim}`}
      role="img"
      aria-label="thumbnail"
      className={css({
        borderRadius: 6,
        border: '1px solid token(colors.neutral.border.low)',
        background: 'neutral.surface.high',
        color: 'neutral.icon.base',
        display: 'inline-block',
      })}
    >
      <rect x="0" y="0" width={dim} height={dim} fill="transparent" />
      <g fill="currentColor">
        <circle cx={dim * 0.3} cy={dim * 0.35} r={dim * 0.1} />
        <rect x={dim * 0.45} y={dim * 0.3} width={dim * 0.25} height={dim * 0.1} rx={dim * 0.02} />
        <rect x={dim * 0.2} y={dim * 0.6} width={dim * 0.6} height={dim * 0.12} rx={dim * 0.02} />
      </g>
    </svg>
  );
};

// 모든 상태(inactive/hover/active/disabled/active-disabled)를 한 화면에 나열 — 문서 States 섹션 + 스냅샷용
const stateCell = css({ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 });

const stateCaption = css({ textStyle: 'caption-md-medium', color: 'neutral.text.base' });

const RadioBoxAllStatesExample = () => (
    <div
      id="radiobox-states"
      className={css({
        display: 'flex',
        gap: 24,
        flexWrap: 'wrap',
        alignItems: 'flex-start',
        padding: '8px',
      })}
    >
      <div className={stateCell}>
        <RadioGroup value="__none__" aria-label="Inactive state">
          <RadioBox value="a" title="Title" description="Description" thumbnail={<Thumb size="md" />} />
        </RadioGroup>
        <span className={stateCaption}>Inactive</span>
      </div>
      <div className={stateCell}>
        <RadioGroup aria-label="Hover state">
          <RadioBox
            value="a"
            title="Title"
            description="Description"
            thumbnail={<Thumb size="md" />}
            className="state-hover"
          />
        </RadioGroup>
        <span className={stateCaption}>Hover</span>
      </div>
      <div className={stateCell}>
        <RadioGroup value="a" aria-label="Active state">
          <RadioBox value="a" title="Title" description="Description" thumbnail={<Thumb size="md" />} />
        </RadioGroup>
        <span className={stateCaption}>Active</span>
      </div>
      <div className={stateCell}>
        <RadioGroup value="__none__" aria-label="Disabled state">
          <RadioBox
            value="a"
            title="Title"
            description="Description"
            disabled
            thumbnail={<Thumb size="md" />}
          />
        </RadioGroup>
        <span className={stateCaption}>Disabled</span>
      </div>
      <div className={stateCell}>
        <RadioGroup value="a" aria-label="Active disabled state">
          <RadioBox
            value="a"
            title="Title"
            description="Description"
            disabled
            thumbnail={<Thumb size="md" />}
          />
        </RadioGroup>
        <span className={stateCaption}>Active Disabled</span>
      </div>
    </div>
  );

export default RadioBoxAllStatesExample;

Full width

import { css } from '@mildang/styled-system/css';
import { RadioGroup, RadioBox } from '@mildang/design-system/Radio';

const Thumb = ({ size }: { size: 'sm' | 'md' }) => {
  const dim = size === 'md' ? 80 : 50;
  return (
    <svg
      width={dim}
      height={dim}
      viewBox={`0 0 ${dim} ${dim}`}
      role="img"
      aria-label="thumbnail"
      className={css({
        borderRadius: 6,
        border: '1px solid token(colors.neutral.border.low)',
        background: 'neutral.surface.high',
        color: 'neutral.icon.base',
        display: 'inline-block',
      })}
    >
      <rect x="0" y="0" width={dim} height={dim} fill="transparent" />
      <g fill="currentColor">
        <circle cx={dim * 0.3} cy={dim * 0.35} r={dim * 0.1} />
        <rect x={dim * 0.45} y={dim * 0.3} width={dim * 0.25} height={dim * 0.1} rx={dim * 0.02} />
        <rect x={dim * 0.2} y={dim * 0.6} width={dim * 0.6} height={dim * 0.12} rx={dim * 0.02} />
      </g>
    </svg>
  );
};

const RadioBoxFullWidthExample = () => (
    <div className={css({ display: 'flex', flexDirection: 'column', gap: 24 })}>
      {[280, 400, 560].map((w) => (
        <div key={w} style={{ width: w }}>
          <RadioGroup defaultValue="opt1" aria-label={`RadioBox ${w}px`} gap="12">
            <RadioBox value="opt1" title="Title" description="Description" thumbnail={<Thumb size="md" />} />
            <RadioBox value="opt2" title="Title" description="Description" thumbnail={<Thumb size="md" />} />
          </RadioGroup>
        </div>
      ))}
    </div>
  );

export default RadioBoxFullWidthExample;