/** * @title Primitives * @description Box, Stack, Inline, Flex, Grid, Bleed, Pressable, Anchor 처럼 토큰을 바로 쓰는 레이아웃·상호작용 기본 요소예요. */import * as React from 'react';import { cn } from '@/lib/utils';// Atlassian @atlaskit/primitives(compiled) 스펙. 토큰 이름(예: 'space.100', 'color.background.neutral')을 그대로 props 로 받고,// 내부에서 정적인 Tailwind 토큰 유틸로 바꾼다 (Tailwind 가 클래스를 찾을 수 있도록 맵을 문자열로 둔다).const backgroundColorMap = { 'color.background.accent.lime.subtlest': 'bg-ds-background-accent-lime-subtlest', 'color.background.accent.lime.subtlest.hovered': 'bg-ds-background-accent-lime-subtlest-hovered', 'color.background.accent.lime.subtlest.pressed': 'bg-ds-background-accent-lime-subtlest-pressed', 'color.background.accent.lime.subtler': 'bg-ds-background-accent-lime-subtler', 'color.background.accent.lime.subtler.hovered': 'bg-ds-background-accent-lime-subtler-hovered', 'color.background.accent.lime.subtler.pressed': 'bg-ds-background-accent-lime-subtler-pressed', 'color.background.accent.lime.subtle': 'bg-ds-background-accent-lime-subtle', 'color.background.accent.lime.subtle.hovered': 'bg-ds-background-accent-lime-subtle-hovered', 'color.background.accent.lime.subtle.pressed': 'bg-ds-background-accent-lime-subtle-pressed', 'color.background.accent.lime.bolder': 'bg-ds-background-accent-lime-bolder', 'color.background.accent.lime.bolder.hovered': 'bg-ds-background-accent-lime-bolder-hovered', 'color.background.accent.lime.bolder.pressed': 'bg-ds-background-accent-lime-bolder-pressed', 'color.background.accent.red.subtlest': 'bg-ds-background-accent-red-subtlest', 'color.background.accent.red.subtlest.hovered': 'bg-ds-background-accent-red-subtlest-hovered', 'color.background.accent.red.subtlest.pressed': 'bg-ds-background-accent-red-subtlest-pressed', 'color.background.accent.red.subtler': 'bg-ds-background-accent-red-subtler', 'color.background.accent.red.subtler.hovered': 'bg-ds-background-accent-red-subtler-hovered', 'color.background.accent.red.subtler.pressed': 'bg-ds-background-accent-red-subtler-pressed', 'color.background.accent.red.subtle': 'bg-ds-background-accent-red-subtle', 'color.background.accent.red.subtle.hovered': 'bg-ds-background-accent-red-subtle-hovered', 'color.background.accent.red.subtle.pressed': 'bg-ds-background-accent-red-subtle-pressed', 'color.background.accent.red.bolder': 'bg-ds-background-accent-red-bolder', 'color.background.accent.red.bolder.hovered': 'bg-ds-background-accent-red-bolder-hovered', 'color.background.accent.red.bolder.pressed': 'bg-ds-background-accent-red-bolder-pressed', 'color.background.accent.orange.subtlest': 'bg-ds-background-accent-orange-subtlest', 'color.background.accent.orange.subtlest.hovered': 'bg-ds-background-accent-orange-subtlest-hovered', 'color.background.accent.orange.subtlest.pressed': 'bg-ds-background-accent-orange-subtlest-pressed', 'color.background.accent.orange.subtler': 'bg-ds-background-accent-orange-subtler', 'color.background.accent.orange.subtler.hovered': 'bg-ds-background-accent-orange-subtler-hovered', 'color.background.accent.orange.subtler.pressed': 'bg-ds-background-accent-orange-subtler-pressed', 'color.background.accent.orange.subtle': 'bg-ds-background-accent-orange-subtle', 'color.background.accent.orange.subtle.hovered': 'bg-ds-background-accent-orange-subtle-hovered', 'color.background.accent.orange.subtle.pressed': 'bg-ds-background-accent-orange-subtle-pressed', 'color.background.accent.orange.bolder': 'bg-ds-background-accent-orange-bolder', 'color.background.accent.orange.bolder.hovered': 'bg-ds-background-accent-orange-bolder-hovered', 'color.background.accent.orange.bolder.pressed': 'bg-ds-background-accent-orange-bolder-pressed', 'color.background.accent.yellow.subtlest': 'bg-ds-background-accent-yellow-subtlest', 'color.background.accent.yellow.subtlest.hovered': 'bg-ds-background-accent-yellow-subtlest-hovered', 'color.background.accent.yellow.subtlest.pressed': 'bg-ds-background-accent-yellow-subtlest-pressed', 'color.background.accent.yellow.subtler': 'bg-ds-background-accent-yellow-subtler', 'color.background.accent.yellow.subtler.hovered': 'bg-ds-background-accent-yellow-subtler-hovered', 'color.background.accent.yellow.subtler.pressed': 'bg-ds-background-accent-yellow-subtler-pressed', 'color.background.accent.yellow.subtle': 'bg-ds-background-accent-yellow-subtle', 'color.background.accent.yellow.subtle.hovered': 'bg-ds-background-accent-yellow-subtle-hovered', 'color.background.accent.yellow.subtle.pressed': 'bg-ds-background-accent-yellow-subtle-pressed', 'color.background.accent.yellow.bolder': 'bg-ds-background-accent-yellow-bolder', 'color.background.accent.yellow.bolder.hovered': 'bg-ds-background-accent-yellow-bolder-hovered', 'color.background.accent.yellow.bolder.pressed': 'bg-ds-background-accent-yellow-bolder-pressed', 'color.background.accent.green.subtlest': 'bg-ds-background-accent-green-subtlest', 'color.background.accent.green.subtlest.hovered': 'bg-ds-background-accent-green-subtlest-hovered', 'color.background.accent.green.subtlest.pressed': 'bg-ds-background-accent-green-subtlest-pressed', 'color.background.accent.green.subtler': 'bg-ds-background-accent-green-subtler', 'color.background.accent.green.subtler.hovered': 'bg-ds-background-accent-green-subtler-hovered', 'color.background.accent.green.subtler.pressed': 'bg-ds-background-accent-green-subtler-pressed', 'color.background.accent.green.subtle': 'bg-ds-background-accent-green-subtle', 'color.background.accent.green.subtle.hovered': 'bg-ds-background-accent-green-subtle-hovered', 'color.background.accent.green.subtle.pressed': 'bg-ds-background-accent-green-subtle-pressed', 'color.background.accent.green.bolder': 'bg-ds-background-accent-green-bolder', 'color.background.accent.green.bolder.hovered': 'bg-ds-background-accent-green-bolder-hovered', 'color.background.accent.green.bolder.pressed': 'bg-ds-background-accent-green-bolder-pressed', 'color.background.accent.teal.subtlest': 'bg-ds-background-accent-teal-subtlest', 'color.background.accent.teal.subtlest.hovered': 'bg-ds-background-accent-teal-subtlest-hovered', 'color.background.accent.teal.subtlest.pressed': 'bg-ds-background-accent-teal-subtlest-pressed', 'color.background.accent.teal.subtler': 'bg-ds-background-accent-teal-subtler', 'color.background.accent.teal.subtler.hovered': 'bg-ds-background-accent-teal-subtler-hovered', 'color.background.accent.teal.subtler.pressed': 'bg-ds-background-accent-teal-subtler-pressed', 'color.background.accent.teal.subtle': 'bg-ds-background-accent-teal-subtle', 'color.background.accent.teal.subtle.hovered': 'bg-ds-background-accent-teal-subtle-hovered', 'color.background.accent.teal.subtle.pressed': 'bg-ds-background-accent-teal-subtle-pressed', 'color.background.accent.teal.bolder': 'bg-ds-background-accent-teal-bolder', 'color.background.accent.teal.bolder.hovered': 'bg-ds-background-accent-teal-bolder-hovered', 'color.background.accent.teal.bolder.pressed': 'bg-ds-background-accent-teal-bolder-pressed', 'color.background.accent.blue.subtlest': 'bg-ds-background-accent-blue-subtlest', 'color.background.accent.blue.subtlest.hovered': 'bg-ds-background-accent-blue-subtlest-hovered', 'color.background.accent.blue.subtlest.pressed': 'bg-ds-background-accent-blue-subtlest-pressed', 'color.background.accent.blue.subtler': 'bg-ds-background-accent-blue-subtler', 'color.background.accent.blue.subtler.hovered': 'bg-ds-background-accent-blue-subtler-hovered', 'color.background.accent.blue.subtler.pressed': 'bg-ds-background-accent-blue-subtler-pressed', 'color.background.accent.blue.subtle': 'bg-ds-background-accent-blue-subtle', 'color.background.accent.blue.subtle.hovered': 'bg-ds-background-accent-blue-subtle-hovered', 'color.background.accent.blue.subtle.pressed': 'bg-ds-background-accent-blue-subtle-pressed', 'color.background.accent.blue.bolder': 'bg-ds-background-accent-blue-bolder', 'color.background.accent.blue.bolder.hovered': 'bg-ds-background-accent-blue-bolder-hovered', 'color.background.accent.blue.bolder.pressed': 'bg-ds-background-accent-blue-bolder-pressed', 'color.background.accent.purple.subtlest': 'bg-ds-background-accent-purple-subtlest', 'color.background.accent.purple.subtlest.hovered': 'bg-ds-background-accent-purple-subtlest-hovered', 'color.background.accent.purple.subtlest.pressed': 'bg-ds-background-accent-purple-subtlest-pressed', 'color.background.accent.purple.subtler': 'bg-ds-background-accent-purple-subtler', 'color.background.accent.purple.subtler.hovered': 'bg-ds-background-accent-purple-subtler-hovered', 'color.background.accent.purple.subtler.pressed': 'bg-ds-background-accent-purple-subtler-pressed', 'color.background.accent.purple.subtle': 'bg-ds-background-accent-purple-subtle', 'color.background.accent.purple.subtle.hovered': 'bg-ds-background-accent-purple-subtle-hovered', 'color.background.accent.purple.subtle.pressed': 'bg-ds-background-accent-purple-subtle-pressed', 'color.background.accent.purple.bolder': 'bg-ds-background-accent-purple-bolder', 'color.background.accent.purple.bolder.hovered': 'bg-ds-background-accent-purple-bolder-hovered', 'color.background.accent.purple.bolder.pressed': 'bg-ds-background-accent-purple-bolder-pressed', 'color.background.accent.magenta.subtlest': 'bg-ds-background-accent-magenta-subtlest', 'color.background.accent.magenta.subtlest.hovered': 'bg-ds-background-accent-magenta-subtlest-hovered', 'color.background.accent.magenta.subtlest.pressed': 'bg-ds-background-accent-magenta-subtlest-pressed', 'color.background.accent.magenta.subtler': 'bg-ds-background-accent-magenta-subtler', 'color.background.accent.magenta.subtler.hovered': 'bg-ds-background-accent-magenta-subtler-hovered', 'color.background.accent.magenta.subtler.pressed': 'bg-ds-background-accent-magenta-subtler-pressed', 'color.background.accent.magenta.subtle': 'bg-ds-background-accent-magenta-subtle', 'color.background.accent.magenta.subtle.hovered': 'bg-ds-background-accent-magenta-subtle-hovered', 'color.background.accent.magenta.subtle.pressed': 'bg-ds-background-accent-magenta-subtle-pressed', 'color.background.accent.magenta.bolder': 'bg-ds-background-accent-magenta-bolder', 'color.background.accent.magenta.bolder.hovered': 'bg-ds-background-accent-magenta-bolder-hovered', 'color.background.accent.magenta.bolder.pressed': 'bg-ds-background-accent-magenta-bolder-pressed', 'color.background.accent.gray.subtlest': 'bg-ds-background-accent-gray-subtlest', 'color.background.accent.gray.subtlest.hovered': 'bg-ds-background-accent-gray-subtlest-hovered', 'color.background.accent.gray.subtlest.pressed': 'bg-ds-background-accent-gray-subtlest-pressed', 'color.background.accent.gray.subtler': 'bg-ds-background-accent-gray-subtler', 'color.background.accent.gray.subtler.hovered': 'bg-ds-background-accent-gray-subtler-hovered', 'color.background.accent.gray.subtler.pressed': 'bg-ds-background-accent-gray-subtler-pressed', 'color.background.accent.gray.subtle': 'bg-ds-background-accent-gray-subtle', 'color.background.accent.gray.subtle.hovered': 'bg-ds-background-accent-gray-subtle-hovered', 'color.background.accent.gray.subtle.pressed': 'bg-ds-background-accent-gray-subtle-pressed', 'color.background.accent.gray.bolder': 'bg-ds-background-accent-gray-bolder', 'color.background.accent.gray.bolder.hovered': 'bg-ds-background-accent-gray-bolder-hovered', 'color.background.accent.gray.bolder.pressed': 'bg-ds-background-accent-gray-bolder-pressed', 'color.background.disabled': 'bg-ds-background-disabled', 'color.background.input': 'bg-ds-background-input', 'color.background.input.hovered': 'bg-ds-background-input-hovered', 'color.background.input.pressed': 'bg-ds-background-input-pressed', 'color.background.inverse.subtle': 'bg-ds-background-inverse-subtle', 'color.background.inverse.subtle.hovered': 'bg-ds-background-inverse-subtle-hovered', 'color.background.inverse.subtle.pressed': 'bg-ds-background-inverse-subtle-pressed', 'color.background.neutral': 'bg-ds-background-neutral', 'color.background.neutral.hovered': 'bg-ds-background-neutral-hovered', 'color.background.neutral.pressed': 'bg-ds-background-neutral-pressed', 'color.background.neutral.subtle': 'bg-ds-background-neutral-subtle', 'color.background.neutral.subtle.hovered': 'bg-ds-background-neutral-subtle-hovered', 'color.background.neutral.subtle.pressed': 'bg-ds-background-neutral-subtle-pressed', 'color.background.neutral.bold': 'bg-ds-background-neutral-bold', 'color.background.neutral.bold.hovered': 'bg-ds-background-neutral-bold-hovered', 'color.background.neutral.bold.pressed': 'bg-ds-background-neutral-bold-pressed', 'color.background.selected': 'bg-ds-background-selected', 'color.background.selected.hovered': 'bg-ds-background-selected-hovered', 'color.background.selected.pressed': 'bg-ds-background-selected-pressed', 'color.background.selected.bold': 'bg-ds-background-selected-bold', 'color.background.selected.bold.hovered': 'bg-ds-background-selected-bold-hovered', 'color.background.selected.bold.pressed': 'bg-ds-background-selected-bold-pressed', 'color.background.brand.subtlest': 'bg-ds-background-brand-subtlest', 'color.background.brand.subtlest.hovered': 'bg-ds-background-brand-subtlest-hovered', 'color.background.brand.subtlest.pressed': 'bg-ds-background-brand-subtlest-pressed', 'color.background.brand.bold': 'bg-ds-background-brand-bold', 'color.background.brand.bold.hovered': 'bg-ds-background-brand-bold-hovered', 'color.background.brand.bold.pressed': 'bg-ds-background-brand-bold-pressed', 'color.background.brand.boldest': 'bg-ds-background-brand-boldest', 'color.background.brand.boldest.hovered': 'bg-ds-background-brand-boldest-hovered', 'color.background.brand.boldest.pressed': 'bg-ds-background-brand-boldest-pressed', 'color.background.danger': 'bg-ds-background-danger', 'color.background.danger.hovered': 'bg-ds-background-danger-hovered', 'color.background.danger.pressed': 'bg-ds-background-danger-pressed', 'color.background.danger.subtler': 'bg-ds-background-danger-subtler', 'color.background.danger.subtler.hovered': 'bg-ds-background-danger-subtler-hovered', 'color.background.danger.subtler.pressed': 'bg-ds-background-danger-subtler-pressed', 'color.background.danger.subtle': 'bg-ds-background-danger-subtle', 'color.background.danger.bold': 'bg-ds-background-danger-bold', 'color.background.danger.bold.hovered': 'bg-ds-background-danger-bold-hovered', 'color.background.danger.bold.pressed': 'bg-ds-background-danger-bold-pressed', 'color.background.warning': 'bg-ds-background-warning', 'color.background.warning.hovered': 'bg-ds-background-warning-hovered', 'color.background.warning.pressed': 'bg-ds-background-warning-pressed', 'color.background.warning.subtler': 'bg-ds-background-warning-subtler', 'color.background.warning.subtler.hovered': 'bg-ds-background-warning-subtler-hovered', 'color.background.warning.subtler.pressed': 'bg-ds-background-warning-subtler-pressed', 'color.background.warning.subtle': 'bg-ds-background-warning-subtle', 'color.background.warning.bold': 'bg-ds-background-warning-bold', 'color.background.warning.bold.hovered': 'bg-ds-background-warning-bold-hovered', 'color.background.warning.bold.pressed': 'bg-ds-background-warning-bold-pressed', 'color.background.success': 'bg-ds-background-success', 'color.background.success.hovered': 'bg-ds-background-success-hovered', 'color.background.success.pressed': 'bg-ds-background-success-pressed', 'color.background.success.subtler': 'bg-ds-background-success-subtler', 'color.background.success.subtler.hovered': 'bg-ds-background-success-subtler-hovered', 'color.background.success.subtler.pressed': 'bg-ds-background-success-subtler-pressed', 'color.background.success.subtle': 'bg-ds-background-success-subtle', 'color.background.success.bold': 'bg-ds-background-success-bold', 'color.background.success.bold.hovered': 'bg-ds-background-success-bold-hovered', 'color.background.success.bold.pressed': 'bg-ds-background-success-bold-pressed', 'color.background.discovery': 'bg-ds-background-discovery', 'color.background.discovery.hovered': 'bg-ds-background-discovery-hovered', 'color.background.discovery.pressed': 'bg-ds-background-discovery-pressed', 'color.background.discovery.subtler': 'bg-ds-background-discovery-subtler', 'color.background.discovery.subtler.hovered': 'bg-ds-background-discovery-subtler-hovered', 'color.background.discovery.subtler.pressed': 'bg-ds-background-discovery-subtler-pressed', 'color.background.discovery.subtle': 'bg-ds-background-discovery-subtle', 'color.background.discovery.bold': 'bg-ds-background-discovery-bold', 'color.background.discovery.bold.hovered': 'bg-ds-background-discovery-bold-hovered', 'color.background.discovery.bold.pressed': 'bg-ds-background-discovery-bold-pressed', 'color.background.information': 'bg-ds-background-information', 'color.background.information.hovered': 'bg-ds-background-information-hovered', 'color.background.information.pressed': 'bg-ds-background-information-pressed', 'color.background.information.subtler': 'bg-ds-background-information-subtler', 'color.background.information.subtler.hovered': 'bg-ds-background-information-subtler-hovered', 'color.background.information.subtler.pressed': 'bg-ds-background-information-subtler-pressed', 'color.background.information.subtle': 'bg-ds-background-information-subtle', 'color.background.information.bold': 'bg-ds-background-information-bold', 'color.background.information.bold.hovered': 'bg-ds-background-information-bold-hovered', 'color.background.information.bold.pressed': 'bg-ds-background-information-bold-pressed', 'color.skeleton': 'bg-ds-skeleton', 'color.skeleton.subtle': 'bg-ds-skeleton-subtle', 'color.blanket': 'bg-ds-blanket', 'color.blanket.selected': 'bg-ds-blanket-selected', 'color.blanket.danger': 'bg-ds-blanket-danger', 'elevation.surface': 'bg-ds-surface', 'elevation.surface.hovered': 'bg-ds-surface-hovered', 'elevation.surface.pressed': 'bg-ds-surface-pressed', 'elevation.surface.container': 'bg-ds-surface-container', 'elevation.surface.container.hovered': 'bg-ds-surface-container-hovered', 'elevation.surface.container.pressed': 'bg-ds-surface-container-pressed', 'elevation.surface.overlay': 'bg-ds-surface-overlay', 'elevation.surface.overlay.hovered': 'bg-ds-surface-overlay-hovered', 'elevation.surface.overlay.pressed': 'bg-ds-surface-overlay-pressed', 'elevation.surface.raised': 'bg-ds-surface-raised', 'elevation.surface.raised.hovered': 'bg-ds-surface-raised-hovered', 'elevation.surface.raised.pressed': 'bg-ds-surface-raised-pressed', 'elevation.surface.sunken': 'bg-ds-surface-sunken',} as const;const paddingTopMap = { 'space.0': 'pt-ds-0', 'space.025': 'pt-ds-025', 'space.050': 'pt-ds-050', 'space.075': 'pt-ds-075', 'space.100': 'pt-ds-100', 'space.150': 'pt-ds-150', 'space.200': 'pt-ds-200', 'space.250': 'pt-ds-250', 'space.300': 'pt-ds-300', 'space.400': 'pt-ds-400', 'space.500': 'pt-ds-500', 'space.600': 'pt-ds-600', 'space.800': 'pt-ds-800', 'space.1000': 'pt-ds-1000',} as const;const paddingBottomMap = { 'space.0': 'pb-ds-0', 'space.025': 'pb-ds-025', 'space.050': 'pb-ds-050', 'space.075': 'pb-ds-075', 'space.100': 'pb-ds-100', 'space.150': 'pb-ds-150', 'space.200': 'pb-ds-200', 'space.250': 'pb-ds-250', 'space.300': 'pb-ds-300', 'space.400': 'pb-ds-400', 'space.500': 'pb-ds-500', 'space.600': 'pb-ds-600', 'space.800': 'pb-ds-800', 'space.1000': 'pb-ds-1000',} as const;const paddingStartMap = { 'space.0': 'ps-ds-0', 'space.025': 'ps-ds-025', 'space.050': 'ps-ds-050', 'space.075': 'ps-ds-075', 'space.100': 'ps-ds-100', 'space.150': 'ps-ds-150', 'space.200': 'ps-ds-200', 'space.250': 'ps-ds-250', 'space.300': 'ps-ds-300', 'space.400': 'ps-ds-400', 'space.500': 'ps-ds-500', 'space.600': 'ps-ds-600', 'space.800': 'ps-ds-800', 'space.1000': 'ps-ds-1000',} as const;const paddingEndMap = { 'space.0': 'pe-ds-0', 'space.025': 'pe-ds-025', 'space.050': 'pe-ds-050', 'space.075': 'pe-ds-075', 'space.100': 'pe-ds-100', 'space.150': 'pe-ds-150', 'space.200': 'pe-ds-200', 'space.250': 'pe-ds-250', 'space.300': 'pe-ds-300', 'space.400': 'pe-ds-400', 'space.500': 'pe-ds-500', 'space.600': 'pe-ds-600', 'space.800': 'pe-ds-800', 'space.1000': 'pe-ds-1000',} as const;const columnGapMap = { 'space.0': 'gap-x-ds-0', 'space.025': 'gap-x-ds-025', 'space.050': 'gap-x-ds-050', 'space.075': 'gap-x-ds-075', 'space.100': 'gap-x-ds-100', 'space.150': 'gap-x-ds-150', 'space.200': 'gap-x-ds-200', 'space.250': 'gap-x-ds-250', 'space.300': 'gap-x-ds-300', 'space.400': 'gap-x-ds-400', 'space.500': 'gap-x-ds-500', 'space.600': 'gap-x-ds-600', 'space.800': 'gap-x-ds-800', 'space.1000': 'gap-x-ds-1000', 'space.gap.xxsmall': 'gap-x-ds-gap-xxsmall', 'space.gap.xsmall': 'gap-x-ds-gap-xsmall', 'space.gap.small': 'gap-x-ds-gap-small', 'space.gap.medium': 'gap-x-ds-gap-medium', 'space.gap.large': 'gap-x-ds-gap-large', 'space.gap.xlarge': 'gap-x-ds-gap-xlarge', 'space.gap.xxlarge': 'gap-x-ds-gap-xxlarge',} as const;const rowGapMap = { 'space.0': 'gap-y-ds-0', 'space.025': 'gap-y-ds-025', 'space.050': 'gap-y-ds-050', 'space.075': 'gap-y-ds-075', 'space.100': 'gap-y-ds-100', 'space.150': 'gap-y-ds-150', 'space.200': 'gap-y-ds-200', 'space.250': 'gap-y-ds-250', 'space.300': 'gap-y-ds-300', 'space.400': 'gap-y-ds-400', 'space.500': 'gap-y-ds-500', 'space.600': 'gap-y-ds-600', 'space.800': 'gap-y-ds-800', 'space.1000': 'gap-y-ds-1000', 'space.gap.xxsmall': 'gap-y-ds-gap-xxsmall', 'space.gap.xsmall': 'gap-y-ds-gap-xsmall', 'space.gap.small': 'gap-y-ds-gap-small', 'space.gap.medium': 'gap-y-ds-gap-medium', 'space.gap.large': 'gap-y-ds-gap-large', 'space.gap.xlarge': 'gap-y-ds-gap-xlarge', 'space.gap.xxlarge': 'gap-y-ds-gap-xxlarge',} as const;// ---------------------------------------------------------------------------// 타입// ---------------------------------------------------------------------------export type BackgroundColorToken = keyof typeof backgroundColorMap;export type SpaceToken = keyof typeof paddingTopMap;export type PaddingToken = SpaceToken;export type GapToken = keyof typeof columnGapMap;export type AlignBlock = 'start' | 'center' | 'end' | 'baseline' | 'stretch';export type AlignInline = 'start' | 'center' | 'end' | 'stretch';export type AlignItems = 'center' | 'start' | 'stretch' | 'end' | 'baseline';export type AlignContent = 'center' | 'start' | 'space-around' | 'space-between' | 'space-evenly' | 'stretch' | 'end';export type JustifyContent = 'center' | 'start' | 'space-around' | 'space-between' | 'space-evenly' | 'stretch' | 'end';export type JustifyItems = 'center' | 'start' | 'stretch' | 'end';export type AutoFlow = 'column' | 'row' | 'dense' | 'row dense' | 'column dense';export type Direction = 'column' | 'row';export type Wrap = 'wrap' | 'nowrap';export type Grow = 'hug' | 'fill';export type Spread = 'space-between';export type BleedSpaceToken = 'space.025' | 'space.050' | 'space.100' | 'space.150' | 'space.200';const alignItemsMap: Record<AlignItems, string> = { start: 'items-start', center: 'items-center', baseline: 'items-baseline', end: 'items-end', stretch: 'items-stretch',};const justifyContentMap: Record<JustifyContent, string> = { start: 'justify-start', center: 'justify-center', end: 'justify-end', 'space-between': 'justify-between', 'space-around': 'justify-around', 'space-evenly': 'justify-evenly', stretch: 'justify-stretch',};const alignContentMap: Record<AlignContent, string> = { start: 'content-start', center: 'content-center', end: 'content-end', 'space-between': 'content-between', 'space-around': 'content-around', 'space-evenly': 'content-evenly', stretch: 'content-stretch',};const justifyItemsMap: Record<JustifyItems, string> = { start: 'justify-items-start', center: 'justify-items-center', end: 'justify-items-end', stretch: 'justify-items-stretch',};const autoFlowMap: Record<AutoFlow, string> = { row: 'grid-flow-row', column: 'grid-flow-col', dense: 'grid-flow-dense', 'row dense': 'grid-flow-row-dense', 'column dense': 'grid-flow-col-dense',};const growMap: Record<Grow, string> = { hug: 'grow-0', fill: 'w-full grow',};const bleedInlineMap: Record<BleedSpaceToken, string> = { 'space.025': '-mx-ds-025', 'space.050': '-mx-ds-050', 'space.100': '-mx-ds-100', 'space.150': '-mx-ds-150', 'space.200': '-mx-ds-200',};const bleedBlockMap: Record<BleedSpaceToken, string> = { 'space.025': '-my-ds-025', 'space.050': '-my-ds-050', 'space.100': '-my-ds-100', 'space.150': '-my-ds-150', 'space.200': '-my-ds-200',};// ---------------------------------------------------------------------------// Surface — 굵은 배경 위에서는 Heading·Text 가 자동으로 inverse 색을 쓴다// ---------------------------------------------------------------------------const SurfaceContext = React.createContext<BackgroundColorToken | 'elevation.surface'>('elevation.surface');/** 가장 가까운 Box/Pressable 의 backgroundColor 를 돌려줘요. */function UNSAFE_useSurface() { return React.useContext(SurfaceContext);}/** 굵은 배경 → 그 위에 올릴 글자 색 토큰 */const UNSAFE_inverseColorMap: Partial<Record<string, 'color.text.inverse' | 'color.text.warning.inverse'>> = { 'color.background.neutral.bold': 'color.text.inverse', 'color.background.neutral.bold.hovered': 'color.text.inverse', 'color.background.neutral.bold.pressed': 'color.text.inverse', 'color.background.selected.bold': 'color.text.inverse', 'color.background.selected.bold.hovered': 'color.text.inverse', 'color.background.selected.bold.pressed': 'color.text.inverse', 'color.background.brand.bold': 'color.text.inverse', 'color.background.brand.bold.hovered': 'color.text.inverse', 'color.background.brand.bold.pressed': 'color.text.inverse', 'color.background.brand.boldest': 'color.text.inverse', 'color.background.brand.boldest.hovered': 'color.text.inverse', 'color.background.brand.boldest.pressed': 'color.text.inverse', 'color.background.danger.bold': 'color.text.inverse', 'color.background.danger.bold.hovered': 'color.text.inverse', 'color.background.danger.bold.pressed': 'color.text.inverse', 'color.background.warning.bold': 'color.text.warning.inverse', 'color.background.warning.bold.hovered': 'color.text.warning.inverse', 'color.background.warning.bold.pressed': 'color.text.warning.inverse', 'color.background.success.bold': 'color.text.inverse', 'color.background.success.bold.hovered': 'color.text.inverse', 'color.background.success.bold.pressed': 'color.text.inverse', 'color.background.discovery.bold': 'color.text.inverse', 'color.background.discovery.bold.hovered': 'color.text.inverse', 'color.background.discovery.bold.pressed': 'color.text.inverse', 'color.background.information.bold': 'color.text.inverse', 'color.background.information.bold.hovered': 'color.text.inverse', 'color.background.information.bold.pressed': 'color.text.inverse',};// ---------------------------------------------------------------------------// 공통: padding / backgroundColor props// ---------------------------------------------------------------------------interface BoxStyleProps { /** 배경 토큰. 굵은 배경이면 안의 Heading·Text 가 inverse 색으로 바뀌어요. */ backgroundColor?: BackgroundColorToken; padding?: PaddingToken; paddingBlock?: PaddingToken; paddingBlockStart?: PaddingToken; paddingBlockEnd?: PaddingToken; paddingInline?: PaddingToken; paddingInlineStart?: PaddingToken; paddingInlineEnd?: PaddingToken;}function boxStyleClasses({ backgroundColor, padding, paddingBlock = padding, paddingBlockStart = paddingBlock, paddingBlockEnd = paddingBlock, paddingInline = padding, paddingInlineStart = paddingInline, paddingInlineEnd = paddingInline,}: BoxStyleProps) { return cn( backgroundColor && backgroundColorMap[backgroundColor], paddingBlockStart && paddingTopMap[paddingBlockStart], paddingBlockEnd && paddingBottomMap[paddingBlockEnd], paddingInlineStart && paddingStartMap[paddingInlineStart], paddingInlineEnd && paddingEndMap[paddingInlineEnd], );}function pickBoxStyleProps<T extends BoxStyleProps>(props: T) { const { backgroundColor, padding, paddingBlock, paddingBlockStart, paddingBlockEnd, paddingInline, paddingInlineStart, paddingInlineEnd, ...rest } = props; return [ { backgroundColor, padding, paddingBlock, paddingBlockStart, paddingBlockEnd, paddingInline, paddingInlineStart, paddingInlineEnd }, rest, ] as const;}function withSurface(backgroundColor: BackgroundColorToken | undefined, node: React.ReactElement) { return backgroundColor ? <SurfaceContext.Provider value={backgroundColor}>{node}</SurfaceContext.Provider> : node;}// ---------------------------------------------------------------------------// Box// ---------------------------------------------------------------------------export interface BoxProps extends BoxStyleProps, Omit<React.HTMLAttributes<HTMLElement>, 'color'> { /** 렌더링할 HTML 요소. 버튼은 Pressable, 링크는 Anchor 를 쓰세요. */ as?: Exclude<keyof React.JSX.IntrinsicElements, 'button' | 'a' | 'svg'>; testId?: string; ref?: React.Ref<HTMLElement>;}/** 토큰 기반 배경·패딩을 가진 범용 컨테이너. 기본은 div 예요. */function Box({ as: Comp = 'div', testId, className, ...props }: BoxProps) { const [style, rest] = pickBoxStyleProps(props); const Element = Comp as React.ElementType; return withSurface( style.backgroundColor, <Element data-slot="box" data-testid={testId} className={cn('box-border appearance-none border-none', boxStyleClasses(style), className)} {...rest} />, );}// ---------------------------------------------------------------------------// Flex// ---------------------------------------------------------------------------export interface FlexProps extends React.HTMLAttributes<HTMLElement> { as?: 'div' | 'span' | 'ul' | 'ol' | 'li' | 'dl'; justifyContent?: JustifyContent; alignItems?: AlignItems; gap?: GapToken; columnGap?: GapToken; rowGap?: GapToken; direction?: Direction; wrap?: Wrap; testId?: string; ref?: React.Ref<HTMLElement>;}/** CSS Flexbox 를 그대로 쓰는 프리미티브. 대부분은 Stack·Inline 으로 충분해요. */function Flex({ as: Comp = 'div', justifyContent, alignItems, gap, columnGap, rowGap, direction, wrap, testId, className, ...props}: FlexProps) { const Element = Comp as React.ElementType; return ( <Element data-slot="flex" data-testid={testId} className={cn( 'box-border flex', gap && columnGapMap[gap], gap && rowGapMap[gap], columnGap && columnGapMap[columnGap], rowGap && rowGapMap[rowGap], alignItems && alignItemsMap[alignItems], direction === 'column' ? 'flex-col' : direction === 'row' ? 'flex-row' : undefined, justifyContent && justifyContentMap[justifyContent], wrap === 'wrap' ? 'flex-wrap' : wrap === 'nowrap' ? 'flex-nowrap' : undefined, className, )} {...props} /> );}// ---------------------------------------------------------------------------// Stack// ---------------------------------------------------------------------------export interface StackProps extends React.HTMLAttributes<HTMLElement> { as?: 'div' | 'span' | 'ul' | 'ol' | 'dl'; /** 세로(주축) 정렬 */ alignBlock?: Exclude<AlignBlock, 'baseline'>; /** 가로(교차축) 정렬 */ alignInline?: AlignInline; spread?: Spread; grow?: Grow; /** 자식 사이 간격 */ space?: GapToken; testId?: string; ref?: React.Ref<HTMLElement>;}/** 자식을 세로로 쌓아요. */function Stack({ alignInline, alignBlock = 'stretch', spread, grow, space, className, ...props }: StackProps) { return ( <Flex data-slot="stack" direction="column" gap={space} alignItems={alignInline} justifyContent={spread || alignBlock} className={cn(grow && growMap[grow], className)} {...props} /> );}// ---------------------------------------------------------------------------// Inline// ---------------------------------------------------------------------------export interface InlineProps extends React.HTMLAttributes<HTMLElement> { as?: 'div' | 'span' | 'ul' | 'ol' | 'li' | 'dl'; /** 세로(교차축) 정렬 */ alignBlock?: AlignBlock; /** 가로(주축) 정렬 */ alignInline?: AlignInline; /** 공간이 모자라면 다음 줄로 넘겨요. */ shouldWrap?: boolean; spread?: Spread; grow?: Grow; /** 자식 사이 가로 간격 */ space?: GapToken; /** 줄바꿈될 때 줄 사이 간격 */ rowSpace?: GapToken; /** 자식 사이에 넣을 구분자. 문자열이면 subtle 색 span 으로 감싸요. */ separator?: React.ReactNode; testId?: string; ref?: React.Ref<HTMLElement>;}function InlineSeparator({ children }: { children: React.ReactNode }) { return ( <span data-slot="inline-separator" className="pointer-events-none my-ds-0 -mx-ds-025 select-none text-ds-text-subtle"> {children} </span> );}/** 자식을 가로로 나란히 놓아요. */function Inline({ alignInline, alignBlock = 'start', shouldWrap = false, spread, grow, space, rowSpace, separator, className, children: rawChildren, ...props}: InlineProps) { const separatorNode = typeof separator === 'string' ? <InlineSeparator>{separator}</InlineSeparator> : separator; const children = separatorNode ? React.Children.toArray(rawChildren) .filter(Boolean) .map((child, index) => ( <React.Fragment key={index}> {index > 0 ? separatorNode : null} {child} </React.Fragment> )) : rawChildren; return ( <Flex data-slot="inline" direction="row" alignItems={alignBlock} justifyContent={spread || alignInline} gap={space} rowGap={rowSpace} wrap={shouldWrap ? 'wrap' : undefined} className={cn(grow && growMap[grow], className)} {...props} > {children} </Flex> );}// ---------------------------------------------------------------------------// Grid// ---------------------------------------------------------------------------export interface GridProps extends React.HTMLAttributes<HTMLElement> { as?: 'div' | 'span' | 'ul' | 'ol'; justifyContent?: JustifyContent; justifyItems?: JustifyItems; alignItems?: AlignItems; alignContent?: AlignContent; gap?: GapToken; columnGap?: GapToken; rowGap?: GapToken; autoFlow?: AutoFlow; /** CSS grid-template-columns (예: '1fr 1fr', 'repeat(3, 1fr)') */ templateColumns?: string; /** CSS grid-template-rows */ templateRows?: string; /** CSS grid-template-areas. 한 줄씩 배열로 넘겨요. */ templateAreas?: string[]; testId?: string; ref?: React.Ref<HTMLElement>;}/** CSS Grid 를 그대로 쓰는 프리미티브. */function Grid({ as: Comp = 'div', justifyContent, justifyItems, alignItems, alignContent, gap, columnGap, rowGap, autoFlow, templateColumns, templateRows, templateAreas, testId, className, style, ...props}: GridProps) { const Element = Comp as React.ElementType; return ( <Element data-slot="grid" data-testid={testId} className={cn( 'box-border grid', gap && columnGapMap[gap], gap && rowGapMap[gap], columnGap && columnGapMap[columnGap], rowGap && rowGapMap[rowGap], alignItems && alignItemsMap[alignItems], alignContent && alignContentMap[alignContent], justifyContent && justifyContentMap[justifyContent], justifyItems && justifyItemsMap[justifyItems], autoFlow && autoFlowMap[autoFlow], className, )} style={{ gridTemplateColumns: templateColumns, gridTemplateRows: templateRows, gridTemplateAreas: templateAreas?.map((row) => `"${row}"`).join(' '), ...style, }} {...props} /> );}// ---------------------------------------------------------------------------// Bleed// ---------------------------------------------------------------------------export interface BleedProps extends React.HTMLAttributes<HTMLElement> { as?: 'div' | 'span'; /** 상하좌우 모두 */ all?: BleedSpaceToken; /** 좌우 */ inline?: BleedSpaceToken; /** 상하 */ block?: BleedSpaceToken; testId?: string; ref?: React.Ref<HTMLElement>;}/** 음수 마진으로 부모 패딩 밖까지 내용을 넓혀요. */function Bleed({ as: Comp = 'div', all, inline = all, block = all, testId, className, ...props }: BleedProps) { const Element = Comp as React.ElementType; return ( <Element data-slot="bleed" data-testid={testId} className={cn('box-border', inline && bleedInlineMap[inline], block && bleedBlockMap[block], className)} {...props} /> );}// ---------------------------------------------------------------------------// Focusable 공통 — ADS focus ring (2px border.focused, offset 2px)// ---------------------------------------------------------------------------const focusRing = 'outline-2 outline-offset-2 outline-transparent focus-visible:outline-solid focus-visible:outline-ds-border-focused';// ---------------------------------------------------------------------------// Pressable// ---------------------------------------------------------------------------export interface PressableProps extends BoxStyleProps, Omit<React.ComponentProps<'button'>, 'disabled'> { isDisabled?: boolean; testId?: string;}/** 직접 디자인한 버튼을 만들 때 쓰는 프리미티브. 포커스 링과 비활성 처리를 갖고 있어요. */function Pressable({ isDisabled = false, type = 'button', testId, className, ...props }: PressableProps) { const [style, rest] = pickBoxStyleProps(props); return withSurface( style.backgroundColor, <button data-slot="pressable" data-testid={testId} type={type} disabled={isDisabled} className={cn( 'box-border cursor-pointer appearance-none border-none bg-transparent p-ds-0 text-inherit [font:inherit]', focusRing, isDisabled && 'cursor-not-allowed', boxStyleClasses(style), className, )} {...rest} />, );}// ---------------------------------------------------------------------------// Anchor// ---------------------------------------------------------------------------export interface AnchorProps extends React.ComponentProps<'a'> { /** target="_blank" 일 때 스크린 리더에게 덧붙여 읽어 줄 문구 */ newWindowLabel?: string; testId?: string;}/** 직접 디자인한 링크를 만들 때 쓰는 프리미티브. 기본 밑줄과 포커스 링, 새 창 안내를 갖고 있어요. */function Anchor({ newWindowLabel = '(새 창에서 열림)', target, testId, className, children, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props}: AnchorProps) { const labelId = React.useId(); const opensNewWindow = target === '_blank'; return ( <a data-slot="anchor" data-testid={testId} target={target} rel={opensNewWindow ? 'noopener noreferrer' : undefined} aria-label={ariaLabel && opensNewWindow && !ariaLabelledBy ? `${ariaLabel}, ${newWindowLabel}` : ariaLabel} aria-labelledby={ariaLabelledBy && opensNewWindow ? `${ariaLabelledBy} ${labelId}` : ariaLabelledBy} className={cn('relative box-border underline decoration-current decoration-solid', focusRing, className)} {...props} > {children} {opensNewWindow && ((children && !ariaLabel && !ariaLabelledBy) || ariaLabelledBy) && ( <span id={labelId} className="sr-only"> , {newWindowLabel} </span> )} </a> );}export { Box, Stack, Inline, Flex, Grid, Bleed, Pressable, Anchor, SurfaceContext, UNSAFE_useSurface, UNSAFE_inverseColorMap, backgroundColorMap,};