file: blocks/footer.mdx # Footer 페이지 하단에 사이트 정보, 링크, 법적 고지 등을 표시하는 블록입니다. - npm: npx @seed-design/cli@latest add block:footer-01 - pnpm: pnpm dlx @seed-design/cli@latest add block:footer-01 - yarn: yarn dlx @seed-design/cli@latest add block:footer-01 - bun: bun x @seed-design/cli@latest add block:footer-01 - npm: npx @seed-design/cli@latest add block:footer-02 - pnpm: pnpm dlx @seed-design/cli@latest add block:footer-02 - yarn: yarn dlx @seed-design/cli@latest add block:footer-02 - bun: bun x @seed-design/cli@latest add block:footer-02 - npm: npx @seed-design/cli@latest add block:footer-03 - pnpm: pnpm dlx @seed-design/cli@latest add block:footer-03 - yarn: yarn dlx @seed-design/cli@latest add block:footer-03 - bun: bun x @seed-design/cli@latest add block:footer-03 - npm: npx @seed-design/cli@latest add block:footer-04 - pnpm: pnpm dlx @seed-design/cli@latest add block:footer-04 - yarn: yarn dlx @seed-design/cli@latest add block:footer-04 - bun: bun x @seed-design/cli@latest add block:footer-04 --- file: blocks/layout.mdx # Layout 페이지의 콘텐츠 영역을 반응형으로 제한하고 중앙 정렬하는 페이지 레이아웃 블록입니다. ## Usage \[#usage] ```tsx import { Layout } from "@seed-design/react"; ``` ```tsx {/* 콘텐츠 */} ``` ## Props \[#props] ### `Layout.Root` \[#layoutroot] - `density` - type: `"medium" | "low" | "high" | undefined` - default: `"medium"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `Layout.Content` \[#layoutcontent] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Density \[#density] `density` prop으로 콘텐츠 영역의 최대 너비를 조절합니다. ## Blocks \[#blocks] - npm: npx @seed-design/cli@latest add block:layout-01 - pnpm: pnpm dlx @seed-design/cli@latest add block:layout-01 - yarn: yarn dlx @seed-design/cli@latest add block:layout-01 - bun: bun x @seed-design/cli@latest add block:layout-01 - npm: npx @seed-design/cli@latest add block:layout-02 - pnpm: pnpm dlx @seed-design/cli@latest add block:layout-02 - yarn: yarn dlx @seed-design/cli@latest add block:layout-02 - bun: bun x @seed-design/cli@latest add block:layout-02 --- file: blocks/side-navigation.mdx # Side Navigation 서비스의 최상위 메뉴 간 이동을 돕고 앱의 전체 구조를 탐색할 수 있게 하는 컴포넌트입니다. ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:side-navigation - pnpm: pnpm dlx @seed-design/cli@latest add ui:side-navigation - yarn: yarn dlx @seed-design/cli@latest add ui:side-navigation - bun: bun x @seed-design/cli@latest add ui:side-navigation ## Examples \[#examples] - npm: npx @seed-design/cli@latest add block:side-navigation-01 - pnpm: pnpm dlx @seed-design/cli@latest add block:side-navigation-01 - yarn: yarn dlx @seed-design/cli@latest add block:side-navigation-01 - bun: bun x @seed-design/cli@latest add block:side-navigation-01 - npm: npx @seed-design/cli@latest add block:side-navigation-02 - pnpm: pnpm dlx @seed-design/cli@latest add block:side-navigation-02 - yarn: yarn dlx @seed-design/cli@latest add block:side-navigation-02 - bun: bun x @seed-design/cli@latest add block:side-navigation-02 ## Props \[#props] ### SideNavigationProvider \[#sidenavigationprovider] - `children` - type: `React.ReactNode` - required: `true` - `collapsed` - type: `boolean | undefined` - `defaultCollapsed` - type: `boolean | undefined` - `onCollapsedChange` - type: `((collapsed: boolean) => void) | undefined` ### SideNavigationRoot \[#sidenavigationroot] - `tone` - type: `"neutral" | "transparent" | undefined` - default: `"neutral"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### SideNavigationHeader \[#sidenavigationheader] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### SideNavigationContent \[#sidenavigationcontent] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### SideNavigationFooter \[#sidenavigationfooter] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### SideNavigationTrigger \[#sidenavigationtrigger] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### SideNavigationGroup \[#sidenavigationgroup] - `label` - type: `React.ReactNode` - `items` - type: `({ key?: React.Key; label: React.ReactNode; prefixIcon?: React.ReactNode; } & ({ current?: boolean; disabled?: boolean; onClick?: React.MouseEventHandler; items?: never; defaultOpen?: never; } | { current?: never; disabled?: never; onClick?: never; defaultOpen?: boolean; items: { key?: React.Key; label: React.ReactNode; current?: boolean; disabled?: boolean; onClick?: React.MouseEventHandler; }[]; }))[]` - required: `true` ### SideNavigationItemButton \[#sidenavigationitembutton] - `prefixIcon` - type: `React.ReactNode` - `label` - type: `React.ReactNode` - required: `true` - `suffixIcon` - type: `React.ReactNode` - `disableTooltip` - type: `boolean | undefined` - description: Suppresses the collapsed-state label tooltip. Set when the button is a NavigationMenuTrigger, which already reveals the label via its flyout. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `current` - type: `boolean | undefined` - `disabled` - type: `boolean | undefined` ### SideNavigationInset \[#sidenavigationinset] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. --- file: components/(deprecated)/action-chip.mdx # Action Chip 더 이상 사용되지 않습니다. [Chip](/react/components/chip)의 `Chip.Button`에 `variant="solid"`를 사용하세요. ## Preview ```tsx import { ActionChip } from "@seed-design/react"; export default function ActionChipPreview() { return 라벨; } ``` ## Usage \[#usage] ```tsx import { ActionChip } from "@seed-design/react"; ``` ```tsx ``` ## Props \[#props] - `size` - type: `"small" | "medium" | undefined` - default: `"medium"` - `layout` - type: `"withText" | "iconOnly" | undefined` - default: `"withText"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Medium \[#medium] ```tsx import { ActionChip } from "@seed-design/react"; export default function ActionChipMedium() { return 라벨; } ``` ### Small \[#small] ```tsx import { ActionChip } from "@seed-design/react"; export default function ActionChipSmall() { return 라벨; } ``` ### Icon Only \[#icon-only] ```tsx import { IconPlusFill } from "@karrotmarket/react-monochrome-icon"; import { ActionChip, Icon } from "@seed-design/react"; export default function ActionChipIconOnly() { return ( } /> ); } ``` ### Prefix Icon \[#prefix-icon] ```tsx import { IconPlusFill } from "@karrotmarket/react-monochrome-icon"; import { ActionChip, PrefixIcon } from "@seed-design/react"; export default function ActionChipPrefixIcon() { return ( } /> 라벨 ); } ``` ### Suffix Icon \[#suffix-icon] ```tsx import { IconChevronDownFill } from "@karrotmarket/react-monochrome-icon"; import { ActionChip, SuffixIcon } from "@seed-design/react"; export default function ActionChipSuffixIcon() { return ( 라벨 } /> ); } ``` --- file: components/(deprecated)/action-sheet.mdx # Action Sheet 더 이상 사용되지 않습니다. [Swipeable Menu Sheet](/react/components/swipeable-menu-sheet)을 사용하세요. ## Preview ```tsx import { ActionSheetContent, ActionSheetItem, ActionSheetRoot, ActionSheetTrigger, } from "seed-design/ui/action-sheet"; import { ActionButton } from "seed-design/ui/action-button"; const ActionSheetPreview = () => { return ( Open ); }; export default ActionSheetPreview; ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:action-sheet - pnpm: pnpm dlx @seed-design/cli@latest add ui:action-sheet - yarn: yarn dlx @seed-design/cli@latest add ui:action-sheet - bun: bun x @seed-design/cli@latest add ui:action-sheet ## Props \[#props] ### `ActionSheetRoot` \[#actionsheetroot] - `lazyMount` - type: `boolean | undefined` - default: `true` - description: Whether to enable lazy mounting - `unmountOnExit` - type: `boolean | undefined` - default: `true` - description: Whether to unmount on exit. - `children` - type: `React.ReactNode` - required: `true` - `role` - type: `"alertdialog" | "dialog" | undefined` - default: `"dialog"` - description: The role of the dialog. - `modal` - type: `boolean | undefined` - default: `true` - description: Whether the dialog should behave as a modal overlay. When true, focus is trapped, background content is hidden from assistive technology, and \`aria-modal\` is set. Set to \`false\` to temporarily suspend modal behavior (e.g., when a Stackflow Activity is pushed on top of a mounted dialog). - `closeOnInteractOutside` - type: `boolean | undefined` - default: `true` - description: Whether to close the dialog when the outside is clicked - `closeOnEscape` - type: `boolean | undefined` - default: `true` - description: Whether to close the dialog when the escape key is pressed - `open` - type: `boolean | undefined` - `defaultOpen` - type: `boolean | undefined` - `onOpenChange` - type: `((open: boolean, details?: DialogChangeDetails) => void) | undefined` ### `ActionSheetContent` \[#actionsheetcontent] - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `layerIndex` - type: `number | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `ActionSheetItem` \[#actionsheetitem] - `label` - type: `React.ReactNode` - required: `true` - `tone` - type: `"critical" | "neutral" | undefined` - default: `"neutral"` ## Examples \[#examples] ### Portalled \[#portalled] ``으로 ``를 감싸서 컨텐츠를 원하는 요소에 렌더링할 수 있습니다. Portal은 기본적으로 `document.body`에 렌더링됩니다. ```tsx import { Portal } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { ActionSheetContent, ActionSheetItem, ActionSheetRoot, ActionSheetTrigger, } from "seed-design/ui/action-sheet"; const ActionSheetPortalled = () => { return ( Open ); }; export default ActionSheetPortalled; ``` --- file: components/(deprecated)/columns.mdx # Columns 여러 열로 구성된 레이아웃을 구성합니다. 디자인 토큰을 JSX에서 사용할 수 있도록 도와줍니다. 더 이상 사용되지 않습니다. [`HStack`](/react/components/layout/h-stack)과 `wrap` prop을 사용하세요. ## Preview ```tsx import { Column, Columns } from "@seed-design/react"; /** * @deprecated use `HStack` instead. */ export default function ColumnsPreview() { return ( 1 Content Width 2 ); } ``` ## Usage \[#usage] ```tsx import { Columns, Column } from "@seed-design/react"; ``` ```tsx ``` ## Props \[#props] ### `Columns` \[#columns] - `asChild` - type: `boolean | undefined` - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `flexGrow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: If true, flex-grow will be set to \`1\`. - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - `overflowX` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `overflowY` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `width` - type: `ResponsiveValue | undefined` - `minWidth` - type: `ResponsiveValue | undefined` - `maxWidth` - type: `ResponsiveValue | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `p` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`padding\`. - `px` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `py` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pl` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingLeft\`. - `zIndex` - type: `number | (string & {}) | undefined` - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexDirection` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - `flexWrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: If true, flex-wrap will be set to \`wrap\`. - `justifySelf` - type: `"center" | "stretch" | "start" | "end" | undefined` - description: In flexbox layout, this property is ignored. - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `gridColumn` - type: `string | undefined` - `gridRow` - type: `string | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `as` - type: `React.ElementType | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` ### `Column` \[#column] - `asChild` - type: `boolean | undefined` - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `flexGrow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: If true, flex-grow will be set to \`1\`. - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - `overflowX` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `overflowY` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `minWidth` - type: `ResponsiveValue | undefined` - `maxWidth` - type: `ResponsiveValue | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `p` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`padding\`. - `px` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `py` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pl` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingLeft\`. - `zIndex` - type: `number | (string & {}) | undefined` - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexWrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: If true, flex-wrap will be set to \`wrap\`. - `justifySelf` - type: `"center" | "stretch" | "start" | "end" | undefined` - description: In flexbox layout, this property is ignored. - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `gridColumn` - type: `string | undefined` - `gridRow` - type: `string | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `as` - type: `React.ElementType | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` - `width` - type: `"content" | ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` --- file: components/(deprecated)/control-chip.mdx # Control Chip 더 이상 사용되지 않습니다. [Chip](/react/components/chip)의 `Chip.Toggle` 또는 `Chip.Button`을 사용하세요. ## Preview ```tsx import { ControlChip } from "seed-design/ui/control-chip"; export default function ControlChipPreview() { return 라벨; } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:control-chip - pnpm: pnpm dlx @seed-design/cli@latest add ui:control-chip - yarn: yarn dlx @seed-design/cli@latest add ui:control-chip - bun: bun x @seed-design/cli@latest add ui:control-chip ## Props \[#props] ### ControlChip.Button \[#controlchipbutton] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `size` - type: `"small" | "medium" | undefined` - default: `"medium"` - `layout` - type: `"withText" | "iconOnly" | undefined` - default: `"withText"` ### ControlChip.Toggle \[#controlchiptoggle] - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `rootRef` - type: `React.Ref | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `size` - type: `"small" | "medium" | undefined` - default: `"medium"` - `layout` - type: `"withText" | "iconOnly" | undefined` - default: `"withText"` - `disabled` - type: `boolean | undefined` - `invalid` - type: `boolean | undefined` - `required` - type: `boolean | undefined` - `checked` - type: `boolean | undefined` - `defaultChecked` - type: `boolean | undefined` - `onCheckedChange` - type: `((checked: boolean) => void) | undefined` - `indeterminate` - type: `boolean | undefined` ### ControlChip.RadioRoot \[#controlchipradioroot] - `disabled` - type: `boolean | undefined` - default: `false` - `invalid` - type: `boolean | undefined` - default: `false` - `name` - type: `string | undefined` - `form` - type: `string | undefined` - `value` - type: `string | undefined` - `defaultValue` - type: `string | undefined` - `onValueChange` - type: `((value: string) => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### ControlChip.RadioItem \[#controlchipradioitem] - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `rootRef` - type: `React.Ref | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `size` - type: `"small" | "medium" | undefined` - default: `"medium"` - `layout` - type: `"withText" | "iconOnly" | undefined` - default: `"withText"` - `value` - type: `string` - required: `true` - `disabled` - type: `boolean | undefined` ## Examples \[#examples] ### Medium \[#medium] ```tsx import { ControlChip } from "seed-design/ui/control-chip"; export default function ActionChipMedium() { return 라벨; } ``` ### Small \[#small] ```tsx import { ControlChip } from "seed-design/ui/control-chip"; export default function ActionChipSmall() { return 라벨; } ``` ### Icon Only \[#icon-only] ```tsx import { IconPlusFill } from "@karrotmarket/react-monochrome-icon"; import { Icon } from "@seed-design/react"; import { ControlChip } from "seed-design/ui/control-chip"; export default function ControlChipIconOnly() { return ( } /> ); } ``` ### Prefix Icon \[#prefix-icon] ```tsx import { IconPlusFill } from "@karrotmarket/react-monochrome-icon"; import { PrefixIcon } from "@seed-design/react"; import { ControlChip } from "seed-design/ui/control-chip"; export default function ControlChipPrefixIcon() { return ( } /> 라벨 ); } ``` ### Suffix Icon \[#suffix-icon] ```tsx import { IconChevronDownFill } from "@karrotmarket/react-monochrome-icon"; import { SuffixIcon } from "@seed-design/react"; import { ControlChip } from "seed-design/ui/control-chip"; export default function ControlChipSuffixIcon() { return ( 라벨 } /> ); } ``` --- file: components/(deprecated)/error-state.mdx # Error State 사용자에게 오류 혹은 조회 결과가 없음을 알리는 컴포넌트입니다. 더 이상 사용되지 않습니다. [`ResultSection`](/react/components/result-section)을 사용하세요. ## Preview ```tsx import { VStack } from "@seed-design/react"; import { ErrorState } from "seed-design/ui/error-state"; export default function ErrorStatePreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:error-state - pnpm: pnpm dlx @seed-design/cli@latest add ui:error-state - yarn: yarn dlx @seed-design/cli@latest add ui:error-state - bun: bun x @seed-design/cli@latest add ui:error-state ## Props \[#props] - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - required: `true` - `primaryActionProps` - type: `ActionButtonProps | undefined` - `secondaryActionProps` - type: `ActionButtonProps | undefined` - `variant` - type: `"default" | "basement" | undefined` - default: `"default"` ## Examples \[#examples] ### Basement \[#basement] layer-basement 배경 위에서는 basement variant를 사용합니다. ```tsx import { VStack } from "@seed-design/react"; import { ErrorState } from "seed-design/ui/error-state"; export default function ErrorStateBasement() { return ( ); } ``` --- file: components/(deprecated)/extended-action-sheet.mdx # Extended Action Sheet 더 이상 사용되지 않습니다. [Swipeable Menu Sheet](/react/components/swipeable-menu-sheet)을 사용하세요. ## Preview ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { PrefixIcon } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { ExtendedActionSheetContent, ExtendedActionSheetGroup, ExtendedActionSheetItem, ExtendedActionSheetRoot, ExtendedActionSheetTrigger, } from "seed-design/ui/extended-action-sheet"; const ExtendedActionSheetPreview = () => { return ( Open } /> Action 1 } /> Action 2 } /> Action 3 } /> Action 4 } /> Action 5 ); }; export default ExtendedActionSheetPreview; ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:extended-action-sheet - pnpm: pnpm dlx @seed-design/cli@latest add ui:extended-action-sheet - yarn: yarn dlx @seed-design/cli@latest add ui:extended-action-sheet - bun: bun x @seed-design/cli@latest add ui:extended-action-sheet ## Props \[#props] ### `ExtendedActionSheetRoot` \[#extendedactionsheetroot] - `lazyMount` - type: `boolean | undefined` - default: `true` - description: Whether to enable lazy mounting - `unmountOnExit` - type: `boolean | undefined` - default: `true` - description: Whether to unmount on exit. - `children` - type: `React.ReactNode` - required: `true` - `role` - type: `"alertdialog" | "dialog" | undefined` - default: `"dialog"` - description: The role of the dialog. - `modal` - type: `boolean | undefined` - default: `true` - description: Whether the dialog should behave as a modal overlay. When true, focus is trapped, background content is hidden from assistive technology, and \`aria-modal\` is set. Set to \`false\` to temporarily suspend modal behavior (e.g., when a Stackflow Activity is pushed on top of a mounted dialog). - `closeOnInteractOutside` - type: `boolean | undefined` - default: `true` - description: Whether to close the dialog when the outside is clicked - `closeOnEscape` - type: `boolean | undefined` - default: `true` - description: Whether to close the dialog when the escape key is pressed - `open` - type: `boolean | undefined` - `defaultOpen` - type: `boolean | undefined` - `onOpenChange` - type: `((open: boolean, details?: DialogChangeDetails) => void) | undefined` ### `ExtendedActionSheetTrigger` \[#extendedactionsheettrigger] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `ExtendedActionSheetContent` \[#extendedactionsheetcontent] - `title` - type: `React.ReactNode` - `layerIndex` - type: `number | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `ExtendedActionSheetGroup` \[#extendedactionsheetgroup] ### `ExtendedActionSheetItem` \[#extendedactionsheetitem] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `tone` - type: `"critical" | "neutral" | undefined` - default: `"neutral"` --- file: components/(deprecated)/extended-fab.mdx # Extended FAB 더 이상 사용되지 않습니다. [Contextual Floating Button](/react/components/contextual-floating-button)을 사용하세요. ## Preview ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { ExtendedFab, PrefixIcon } from "@seed-design/react"; export default function ExtendedFabPreview() { return ( } /> 알림 설정 ); } ``` ## Usage \[#usage] ```tsx import { ExtendedFab, PrefixIcon } from "@seed-design/react"; import { IconPlusLine } from "@karrotmarket/react-monochrome-icon"; ``` ```tsx } /> 라벨 ``` ## Props \[#props] - `variant` - type: `"layerFloating" | "neutralSolid" | undefined` - default: `"neutralSolid"` - `size` - type: `"small" | "medium" | undefined` - default: `"medium"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Medium \[#medium] ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { ExtendedFab, PrefixIcon } from "@seed-design/react"; export default function ExtendedFabMedium() { return ( } /> 알림 설정 ); } ``` ### Small \[#small] ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { ExtendedFab, PrefixIcon } from "@seed-design/react"; export default function ExtendedFabSmall() { return ( } /> 알림 설정 ); } ``` ### Neutral Solid \[#neutral-solid] ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { ExtendedFab, PrefixIcon } from "@seed-design/react"; export default function ExtendedFabNeutralSolid() { return ( } /> 알림 설정 ); } ``` ### Layer Floating \[#layer-floating] ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { ExtendedFab, PrefixIcon } from "@seed-design/react"; export default function ExtendedFabLayerFloating() { return ( } /> 알림 설정 ); } ``` --- file: components/(deprecated)/fab.mdx # FAB 더 이상 사용되지 않습니다. [Contextual Floating Button](/react/components/contextual-floating-button)을 사용하세요. ## Preview ```tsx import IconPlusLine from "@karrotmarket/react-monochrome-icon/IconPlusLine"; import { Fab, Icon } from "@seed-design/react"; export default function FabPreview() { return ( } /> ); } ``` ## Usage \[#usage] ```tsx import { Fab, Icon } from "@seed-design/react"; import { IconPlusLine } from "@karrotmarket/react-monochrome-icon"; ``` ```tsx } /> ``` ## Props \[#props] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. --- file: components/(deprecated)/inline-banner.mdx # Inline Banner 더 이상 사용되지 않습니다. [Page Banner](/react/components/page-banner)를 사용하세요. ## Preview ```tsx import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, DismissibleInlineBanner, InlineBanner, } from "seed-design/ui/inline-banner"; export default function InlineBannerPreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:inline-banner - pnpm: pnpm dlx @seed-design/cli@latest add ui:inline-banner - yarn: yarn dlx @seed-design/cli@latest add ui:inline-banner - bun: bun x @seed-design/cli@latest add ui:inline-banner ## Props \[#props] ### `InlineBanner` \[#inlinebanner] - `prefixIcon` - type: `React.ReactNode` - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - required: `true` - `linkProps` - type: `SeedInlineBanner.LinkProps | undefined` - `variant` - type: `"criticalSolid" | "criticalWeak" | "informativeWeak" | "neutralWeak" | "positiveWeak" | "warningSolid" | "warningWeak" | undefined` - default: `"neutralWeak"` ### `ActionableInlineBanner` \[#actionableinlinebanner] - `prefixIcon` - type: `React.ReactNode` - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - required: `true` - `variant` - type: `"criticalSolid" | "criticalWeak" | "informativeWeak" | "neutralWeak" | "positiveWeak" | "warningSolid" | "warningWeak" | undefined` - default: `"neutralWeak"` ### `DismissibleInlineBanner` \[#dismissibleinlinebanner] - `prefixIcon` - type: `React.ReactNode` - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - required: `true` - `variant` - type: `"informativeWeak" | "neutralWeak" | "positiveWeak" | "warningSolid" | "warningWeak" | undefined` - `open` - type: `boolean | undefined` - `defaultOpen` - type: `boolean | undefined` - `onDismiss` - type: `(() => void) | undefined` ## Examples \[#examples] ### Content Layout \[#content-layout] #### With Link Label \[#with-link-label] `linkProps` prop은 `InlineBanner`에서만 제공돼요. ```tsx import { InlineBanner } from "seed-design/ui/inline-banner"; export default function InlineBannerWithLinkLabel() { return ( ); } ``` #### Text Only \[#text-only] ```tsx import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, DismissibleInlineBanner, InlineBanner, } from "seed-design/ui/inline-banner"; export default function InlineBannerTextOnly() { return ( ); } ``` #### With Icon \[#with-icon] ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, DismissibleInlineBanner, InlineBanner, } from "seed-design/ui/inline-banner"; export default function InlineBannerWithIcon() { return ( } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> ); } ``` #### With Title Text \[#with-title-text] ```tsx import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, DismissibleInlineBanner, InlineBanner, } from "seed-design/ui/inline-banner"; export default function InlineBannerWithTitleText() { return ( ); } ``` #### With All \[#with-all] ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, DismissibleInlineBanner, InlineBanner, } from "seed-design/ui/inline-banner"; export default function InlineBannerWithAll() { return ( } title="타이틀" description="사업자 정보를 등록해주세요." /> } title="타이틀" description="사업자 정보를 등록해주세요." /> } title="타이틀" description="사업자 정보를 등록해주세요." /> ); } ``` ### Customizable Parts \[#customizable-parts] #### Rendering `LinkInlineBanner`’s Link Label as Child \[#rendering-linkinlinebanners-link-label-as-child] ```tsx import { InlineBanner } from "seed-design/ui/inline-banner"; export default function InlineBannerLinkLabelAsChild() { return ( 자세히 보기 ), }} /> ); } ``` ### Variants \[#variants] #### Neutral Weak (Default) \[#neutral-weak-default] ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, DismissibleInlineBanner, InlineBanner, } from "seed-design/ui/inline-banner"; export default function InlineBannerNeutralWeak() { return ( } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> ); } ``` #### Positive Weak \[#positive-weak] ```tsx import { IconCheckmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, DismissibleInlineBanner, InlineBanner, } from "seed-design/ui/inline-banner"; export default function InlineBannerPositiveWeak() { return ( } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> ); } ``` #### Informative Weak \[#informative-weak] ```tsx import { IconILowercaseSerifCircleFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, DismissibleInlineBanner, InlineBanner, } from "seed-design/ui/inline-banner"; export default function InlineBannerInformativeWeak() { return ( } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> ); } ``` #### Warning Weak \[#warning-weak] ```tsx import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, DismissibleInlineBanner, InlineBanner, } from "seed-design/ui/inline-banner"; export default function InlineBannerWarningWeak() { return ( } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> ); } ``` #### Warning Solid \[#warning-solid] ```tsx import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, DismissibleInlineBanner, InlineBanner, } from "seed-design/ui/inline-banner"; export default function InlineBannerWarningSolid() { return ( } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> ); } ``` #### Critical Weak \[#critical-weak] ```tsx import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableInlineBanner, InlineBanner } from "seed-design/ui/inline-banner"; export default function InlineBannerCriticalWeak() { return ( } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> ); } ``` #### Critical Solid \[#critical-solid] `criticalWeak`과 `criticalSolid` variant는 `InlineBanner`와 `ActionableInlineBanner`에서만 제공돼요. variant가 `criticalWeak`이나 `criticalSolid`인 경우 `position: sticky` 등을 활용하여 화면을 스크롤했을 때도 인라인 배너가 상단에 고정되도록 해 주세요. ```tsx import { ActionableInlineBanner, InlineBanner } from "seed-design/ui/inline-banner"; import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; export default function InlineBannerCriticalSolid() { return ( } description="사업자 정보를 등록해주세요." /> } description="사업자 정보를 등록해주세요." /> ); } ``` --- file: components/(deprecated)/inline.mdx # Inline 인라인 레이아웃을 구성합니다. 디자인 토큰을 JSX에서 사용할 수 있도록 도와줍니다. 더 이상 사용되지 않습니다. [`HStack`](/react/components/layout/h-stack)과 `wrap` prop을 사용하세요. ## Preview ```tsx import { Box, Inline } from "@seed-design/react"; /** * @deprecated Use `HStack` instead. */ export default function InlinePreview() { return ( 1 2 3 ); } ``` ## Usage \[#usage] ```tsx import { Inline } from "@seed-design/react"; ``` ```tsx ``` ## Props \[#props] - `asChild` - type: `boolean | undefined` - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `flexGrow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: If true, flex-grow will be set to \`1\`. - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - `overflowX` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `overflowY` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `width` - type: `ResponsiveValue | undefined` - `minWidth` - type: `ResponsiveValue | undefined` - `maxWidth` - type: `ResponsiveValue | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `p` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`padding\`. - `px` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `py` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pl` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingLeft\`. - `zIndex` - type: `number | (string & {}) | undefined` - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexDirection` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - `justifySelf` - type: `"center" | "stretch" | "start" | "end" | undefined` - description: In flexbox layout, this property is ignored. - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `gridColumn` - type: `string | undefined` - `gridRow` - type: `string | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `as` - type: `React.ElementType | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` --- file: components/(deprecated)/link-content.mdx # Link Content 더 이상 사용되지 않습니다. [Action Button](/react/components/action-button)에 `variant="ghost"`를 사용하세요. ## Preview ```tsx import { IconChevronRightLine } from "@karrotmarket/react-monochrome-icon"; import { LinkContent, SuffixIcon } from "@seed-design/react"; export default function LinkContentPreview() { return ( 새 글 } /> ); } ``` ## Usage \[#usage] ```tsx import { LinkContent, SuffixIcon } from "@seed-design/react"; import { IconChevronRightLine } from "@karrotmarket/react-monochrome-icon"; ``` ```tsx Label } /> ``` ## Props \[#props] - `weight` - type: `"bold" | "regular" | undefined` - default: `"regular"` - `size` - type: `"t4" | "t6" | "t5" | undefined` - default: `"t4"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` ## Examples \[#examples] ### Size \[#size] ```tsx import { IconChevronRightLine } from "@karrotmarket/react-monochrome-icon"; import { LinkContent, SuffixIcon, VStack } from "@seed-design/react"; export default function LinkContentSize() { return ( 추가 } /> 추가 } /> 추가 } /> ); } ``` ### Color \[#color] ```tsx import { IconChevronRightLine } from "@karrotmarket/react-monochrome-icon"; import { LinkContent, VStack, SuffixIcon } from "@seed-design/react"; export default function LinkContentColor() { return ( 더보기 } /> 더보기 } /> 더보기 } /> 더보기 } /> ); } ``` --- file: components/(deprecated)/menu-sheet.mdx # Menu Sheet 사용자의 작업과 관련된 선택지를 제공하는 시트 형태의 컴포넌트입니다. 더 이상 사용되지 않습니다. [Swipeable Menu Sheet](/react/components/swipeable-menu-sheet)을 사용하세요. ## Preview ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, MenuSheetTrigger, } from "seed-design/ui/menu-sheet"; const MenuSheetPreview = () => { return ( Open } /> } /> } /> } /> } tone="critical" /> ); }; export default MenuSheetPreview; ``` Stackflow와 Menu Sheet를 함께 사용하는 방법에 대해 알아보세요. ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:menu-sheet - pnpm: pnpm dlx @seed-design/cli@latest add ui:menu-sheet - yarn: yarn dlx @seed-design/cli@latest add ui:menu-sheet - bun: bun x @seed-design/cli@latest add ui:menu-sheet ## Props \[#props] ### `MenuSheetRoot` \[#menusheetroot] - `lazyMount` - type: `boolean | undefined` - default: `true` - description: Whether to enable lazy mounting - `unmountOnExit` - type: `boolean | undefined` - default: `true` - description: Whether to unmount on exit. - `skipAnimation` - type: `boolean | undefined` - default: `false` - `children` - type: `React.ReactNode` - required: `true` - `role` - type: `"alertdialog" | "dialog" | undefined` - default: `"dialog"` - description: The role of the dialog. - `modal` - type: `boolean | undefined` - default: `true` - description: Whether the dialog should behave as a modal overlay. When true, focus is trapped, background content is hidden from assistive technology, and \`aria-modal\` is set. Set to \`false\` to temporarily suspend modal behavior (e.g., when a Stackflow Activity is pushed on top of a mounted dialog). - `closeOnInteractOutside` - type: `boolean | undefined` - default: `true` - description: Whether to close the dialog when the outside is clicked - `closeOnEscape` - type: `boolean | undefined` - default: `true` - description: Whether to close the dialog when the escape key is pressed - `open` - type: `boolean | undefined` - `defaultOpen` - type: `boolean | undefined` - `onOpenChange` - type: `((open: boolean, details?: DialogChangeDetails) => void) | undefined` ### `MenuSheetTrigger` \[#menusheettrigger] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `MenuSheetContent` \[#menusheetcontent] - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `layerIndex` - type: `number | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `labelAlign` - type: `"center" | "left" | undefined` - default: `"left"` - description: - \`left\`: 라벨을 왼쪽 정렬합니다. - \`center\`: 라벨을 중앙 정렬합니다. ### `MenuSheetGroup` \[#menusheetgroup] - `labelAlign` - type: `"center" | "left" | undefined` - default: `"left"` - description: - \`left\`: 라벨을 왼쪽 정렬합니다. - \`center\`: 라벨을 중앙 정렬합니다. ### `MenuSheetItem` \[#menusheetitem] - `prefixIcon` - type: `React.ReactNode` - `label` - type: `React.ReactNode` - required: `true` - `description` - type: `React.ReactNode` - `tone` - type: `"critical" | "neutral" | undefined` - default: `"neutral"` - description: - \`neutral\`: 일반적인 작업을 수행하는 기본 아이템입니다. - \`critical\`: 데이터 삭제와 같이 되돌릴 수 없는 작업을 수행하는 아이템입니다. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `labelAlign` - type: `"center" | "left" | undefined` - default: `"left"` - description: - \`left\`: 라벨을 왼쪽 정렬합니다. - \`center\`: 라벨을 중앙 정렬합니다. ## Examples \[#examples] ### Trigger \[#trigger] ``는 `aria-haspopup="dialog"` 속성을 설정하고, MenuSheet의 `open` 상태에 따라 `aria-expanded` 속성을 자동으로 설정합니다. 이 속성은 스크린 리더와 같은 보조 기술에 유용합니다. ## Preview ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, MenuSheetTrigger, } from "seed-design/ui/menu-sheet"; const MenuSheetPreview = () => { return ( Open } /> } /> } /> } /> } tone="critical" /> ); }; export default MenuSheetPreview; ``` ### With Title \[#with-title] `MenuSheetContent`의 `title` prop을 사용하여 시트 헤더에 제목을 표시합니다. ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, MenuSheetTrigger, } from "seed-design/ui/menu-sheet"; const MenuSheetWithTitle = () => { return ( Open } /> } description="Ut nulla et id dolor labore ullamco irure est id occaecat." /> } description="Ut nulla et id dolor labore ullamco irure est id occaecat." /> } /> } tone="critical" /> ); }; export default MenuSheetWithTitle; ``` ### With Title and Description \[#with-title-and-description] `MenuSheetContent`의 `description` prop을 사용하여 제목 아래에 부가 설명을 추가합니다. ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, MenuSheetTrigger, } from "seed-design/ui/menu-sheet"; export default function MenuSheetWithTitleAndDescription() { return ( Open } /> } description="Ut nulla et id dolor labore ullamco irure est id occaecat." /> } description="Ut nulla et id dolor labore ullamco irure est id occaecat." /> } /> } tone="critical" /> ); } ``` ### Label Align \[#label-align] `MenuSheetContent`의 `labelAlign` prop으로 메뉴 항목의 레이블 정렬을 설정합니다. #### `labelAlign="left"` (with `PrefixIcon`) \[#labelalignleft-with-prefixicon] 레이블을 왼쪽 정렬합니다. ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, MenuSheetTrigger, } from "seed-design/ui/menu-sheet"; const MenuSheetWithPrefixIcon = () => { return ( Open } /> } /> } /> } /> } tone="critical" /> ); }; export default MenuSheetWithPrefixIcon; ``` #### `labelAlign="center"` (without `PrefixIcon`) \[#labelaligncenter-without-prefixicon] 레이블을 중앙 정렬합니다. (일반적으로, `prefixIcon` 없는 경우) ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, MenuSheetTrigger, } from "seed-design/ui/menu-sheet"; const MenuSheetWithoutPrefixIcon = () => { return ( Open ); }; export default MenuSheetWithoutPrefixIcon; ``` #### Overriding `labelAlign` \[#overriding-labelalign] 필요한 경우 `MenuSheetContent`에 지정한 `labelAlign`을 `MenuSheetGroup` 또는 `MenuSheetItem`에 지정한 `labelAlign`으로 덮어쓸 수 있습니다. ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, MenuSheetTrigger, } from "seed-design/ui/menu-sheet"; const MenuSheetOverridingLabelAlign = () => { return ( Open ); }; export default MenuSheetOverridingLabelAlign; ``` ### `onOpenChange` Details \[#onopenchange-details] `onOpenChange` 두 번째 인자로 `details`가 제공됩니다. #### `reason` \[#reason] **열릴 때** (`open: true`) - `"trigger"`: `MenuSheetTrigger` (`MenuSheet.Trigger`)로 열림 **닫힐 때** (`open: false`) - `"closeButton"`: `MenuSheet.CloseButton`으로 닫힘 - `"escapeKeyDown"`: ESC 키 사용 - `"interactOutside"`: 외부 영역 클릭 ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { HStack, Text, VStack } from "@seed-design/react"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, MenuSheetTrigger, } from "seed-design/ui/menu-sheet"; export default function MenuSheetOnOpenChangeReason() { const [open, setOpen] = useState(false); const [openReason, setOpenReason] = useState(null); const [closeReason, setCloseReason] = useState(null); return ( { setOpen(open); (open ? setOpenReason : setCloseReason)(details?.reason ?? null); }} > 열기 } /> } /> } /> 마지막 열림 이유: {openReason ?? "-"} 마지막 닫힘 이유: {closeReason ?? "-"} ); } ``` ### Skip Animation \[#skip-animation] `skipAnimation` prop을 사용하여 MenuSheet의 enter/exit 애니메이션을 건너뛸 수 있습니다. ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, MenuSheetTrigger, } from "seed-design/ui/menu-sheet"; const MenuSheetSkipAnimation = () => { return ( Open } /> } /> } /> ); }; export default MenuSheetSkipAnimation; ``` --- file: components/(deprecated)/stack.mdx # Stack 세로로 쌓이는 레이아웃을 구성합니다. 디자인 토큰을 JSX에서 사용할 수 있도록 도와줍니다. 더 이상 사용되지 않습니다. [`VStack`](/react/components/layout/v-stack) 컴포넌트를 사용하세요. ## Preview ```tsx import { Box, Stack } from "@seed-design/react"; /** * @deprecated Use `VStack` instead. */ export default function StackPreview() { return ( 1 2 3 ); } ``` ## Usage \[#usage] ```tsx import { Stack } from "@seed-design/react"; ``` ```tsx ``` ## Props \[#props] - `asChild` - type: `boolean | undefined` - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `flexGrow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: If true, flex-grow will be set to \`1\`. - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - `overflowX` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `overflowY` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `direction` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - description: Shorthand for \`flexDirection\`. - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `width` - type: `ResponsiveValue | undefined` - `minWidth` - type: `ResponsiveValue | undefined` - `maxWidth` - type: `ResponsiveValue | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `p` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`padding\`. - `px` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `py` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pl` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingLeft\`. - `display` - type: `ResponsiveValue<"none" | "flex"> | undefined` - default: `"flex"` - `zIndex` - type: `number | (string & {}) | undefined` - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexWrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: If true, flex-wrap will be set to \`wrap\`. - `justifySelf` - type: `"center" | "stretch" | "start" | "end" | undefined` - description: In flexbox layout, this property is ignored. - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `gridColumn` - type: `string | undefined` - `gridRow` - type: `string | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `as` - type: `React.ElementType | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` - `wrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: Shorthand for \`flexWrap\`. - `align` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - description: Shorthand for \`alignItems\`. - `justify` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - description: Shorthand for \`justifyContent\`. - `grow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: Shorthand for \`flexGrow\`. - `shrink` - type: `true | 0 | (number & {}) | undefined` - description: Shorthand for \`flexShrink\`. --- file: components/(foundation)/iconography/composition.mdx # Composition 아이콘을 사용하는 방법을 설명합니다. ## 개요 \[#개요] [당근 아이콘 라이브러리](/docs/foundation/iconography/library)는 디자인 시스템에 독립적(agnostic)으로 설계되었으며 SEED Design의 의견이 포함되어 있지 않습니다. 때로는 라이브러리에 없는 커스텀 아이콘을 사용하면서도 아이콘 합성 패턴을 활용해야 하는 경우가 있습니다. 이를 위해 `svg` 속성을 통해 아이콘을 받아 디자인 시스템에 연결하는 유틸리티 컴포넌트를 제공합니다: - `Icon`: 아이콘을 독립적으로 사용하거나 아이콘만 표시하는 컴포넌트에서 사용합니다. - `PrefixIcon`, `SuffixIcon`: 컴포넌트 내에 아이콘과 텍스트를 함께 배치할 때 사용합니다. ## Icon 컴포넌트 \[#icon-컴포넌트] `Icon` 컴포넌트는 아이콘을 독립적으로 사용하거나 아이콘만 표시하는 컴포넌트에서 사용합니다. ```jsx import { Icon } from "@seed-design/react"; import { IconHeartFill } from "@karrotmarket/react-monochrome-icon"; // 독립형 아이콘 } /> // 아이콘만 표시하는 버튼 } /> ``` ## PrefixIcon과 SuffixIcon \[#prefixicon과-suffixicon] 컴포넌트 내에 아이콘과 텍스트를 함께 배치할 때는 `PrefixIcon`, `SuffixIcon` 컴포넌트를 children으로 배치합니다. ```jsx import { PrefixIcon, SuffixIcon } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { IconPlusFill, IconChevronRightFill } from "@karrotmarket/react-monochrome-icon"; // 앞에 아이콘 배치 } /> 라벨 // 뒤에 아이콘 배치 라벨 } /> // 아이콘 양쪽에 배치 } /> 라벨 } /> ``` 이 패턴은 다음과 같은 컴포넌트에서 일관되게 사용됩니다: - `ActionButton`: 버튼 내 아이콘 배치 - `Button`: 일반 버튼 내 아이콘 배치 - `ToggleButton`: 토글 버튼 내 아이콘 배치 - `ActionChip`: 액션 칩 내 아이콘 배치 - `ControlChip`: 컨트롤 칩 내 아이콘 배치 --- file: components/(foundation)/iconography/library.mdx # Library 리액트 아이콘 패키지는 아이콘을 사용할 때 필요한 컴포넌트를 제공합니다. ## Overview \[#overview] 아이콘 라이브러리에 포함된 아이콘을 확인합니다. ## Installation \[#installation] - npm: npm install @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon - pnpm: pnpm add @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon - yarn: yarn add @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon - bun: bun add @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon --- file: components/(foundation)/layout/box.mdx # Box Box 컴포넌트는 가장 기초적인 레이아웃 컴포넌트입니다. 디자인 토큰을 JSX에서 사용할 수 있도록 도와줍니다. ## Preview ```tsx import { Box } from "@seed-design/react"; export default function BoxPreview() { return ( Box Example ); } ``` ## Usage \[#usage] ```tsx import { Box } from "@seed-design/react"; ``` ```tsx ``` ## Props \[#props] - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | "full" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | "full" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | "full" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | "full" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | "full" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `width` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `minWidth` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `maxWidth` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `height` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `minHeight` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `maxHeight` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `p` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - description: Shorthand for \`padding\`. - `paddingX` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `px` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `py` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `pr` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `pl` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - description: Shorthand for \`paddingLeft\`. - `display` - type: `ResponsiveValue<"grid" | "none" | "block" | "flex" | "inline-flex" | "inline" | "inline-block"> | undefined` - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - `overflowX` - type: `"visible" | "hidden" | "scroll" | "auto" | undefined` - `overflowY` - type: `"visible" | "hidden" | "scroll" | "auto" | undefined` - `zIndex` - type: `number | (string & {}) | undefined` - `flexGrow` - type: `true | 0 | (number & {}) | 1 | undefined` - description: If true, flex-grow will be set to \`1\`. - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexDirection` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - `flexWrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: If true, flex-wrap will be set to \`wrap\`. - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `justifySelf` - type: `"center" | "start" | "end" | "stretch" | undefined` - description: In flexbox layout, this property is ignored. - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `gridColumn` - type: `string | undefined` - `gridRow` - type: `string | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `as` - type: `React.ElementType | undefined` - `asChild` - type: `boolean | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` ## Examples \[#examples] ### Gradient \[#gradient] ```tsx import { Box, VStack } from "@seed-design/react"; export default function BoxGradient() { return ( Box Gradient Box Gradient ); } ``` ## Responsive Design \[#responsive-design] Box의 레이아웃 프로퍼티에 반응형 값을 전달하여 viewport 크기에 따라 스타일을 조정할 수 있습니다. --- file: components/(foundation)/layout/flex.mdx # Flex Flex 컴포넌트는 flexbox를 사용하며 디자인 토큰을 JSX에서 사용할 수 있도록 도와줍니다. ## Preview ```tsx import { Flex } from "@seed-design/react"; export default function FlexPreview() { return ( 1 2 3 4 5 ); } ``` ## Props \[#props] - `asChild` - type: `boolean | undefined` - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `flexGrow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: If true, flex-grow will be set to \`1\`. - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - `overflowX` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `overflowY` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `width` - type: `ResponsiveValue | undefined` - `minWidth` - type: `ResponsiveValue | undefined` - `maxWidth` - type: `ResponsiveValue | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `p` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`padding\`. - `px` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `py` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pl` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingLeft\`. - `zIndex` - type: `number | (string & {}) | undefined` - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexDirection` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - `flexWrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: If true, flex-wrap will be set to \`wrap\`. - `justifySelf` - type: `"center" | "stretch" | "start" | "end" | undefined` - description: In flexbox layout, this property is ignored. - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `gridColumn` - type: `string | undefined` - `gridRow` - type: `string | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `as` - type: `React.ElementType | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` - `display` - type: `ResponsiveValue<"none" | "flex"> | undefined` - default: `"flex"` - `direction` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - description: Shorthand for \`flexDirection\`. - `wrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: Shorthand for \`flexWrap\`. - `align` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - description: Shorthand for \`alignItems\`. - `justify` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - description: Shorthand for \`justifyContent\`. - `grow` - type: `true | 0 | (number & {}) | 1 | undefined` - description: Shorthand for \`flexGrow\`. - `shrink` - type: `true | 0 | (number & {}) | undefined` - description: Shorthand for \`flexShrink\`. ## Responsive Design \[#responsive-design] Flex의 레이아웃 프로퍼티에 반응형 값을 전달하여 viewport 크기에 따라 스타일을 조정할 수 있습니다. --- file: components/(foundation)/layout/float.mdx # Float Float 컴포넌트는 특정 위치에 고정된 요소를 배치할 때 사용합니다. ## Preview ```tsx import { Box, Float } from "@seed-design/react"; import { ContextualFloatingButton } from "seed-design/ui/contextual-floating-button"; export default function FloatPreview() { return ( Top Start Top Center Top End Middle Start Middle Center Middle End Bottom Start Bottom Center Bottom End ); } ``` ## Props \[#props] - `as` - type: `React.ElementType | undefined` - `placement` - type: `"bottom-end" | "bottom-start" | "top-end" | "top-start" | "bottom-center" | "top-center" | "middle-center" | "middle-end" | "middle-start"` - required: `true` - `offsetX` - type: `0 | Dimension | (string & {}) | undefined` - default: `0` - `offsetY` - type: `0 | Dimension | (string & {}) | undefined` - default: `0` - `zIndex` - type: `number | (string & {}) | undefined` ## Examples \[#examples] ### Offset X \[#offset-x] `offsetX` 속성을 사용하면 좌우 방향으로 위치를 조정할 수 있습니다. `start`, `middle`에서는 우측 방향으로 조정되고, `end`에서는 좌측 방향으로 조정됩니다. ```tsx import { Box, Float } from "@seed-design/react"; import { ContextualFloatingButton } from "seed-design/ui/contextual-floating-button"; export default function FloatOffsetX() { return ( Middle Start Middle Center Middle End ); } ``` ### Offset Y \[#offset-y] `offsetY` 속성을 사용하면 상하 방향으로 위치를 조정할 수 있습니다. `top`, `middle`에서는 하측 방향으로 조정되고, `bottom`에서는 상측 방향으로 조정됩니다. ```tsx import { Box, Float } from "@seed-design/react"; import { ContextualFloatingButton } from "seed-design/ui/contextual-floating-button"; export default function FloatOffsetY() { return ( Top Center Middle Center Bottom Center ); } ``` --- file: components/(foundation)/layout/grid.mdx # Grid Grid 컴포넌트는 CSS Grid를 사용하며 디자인 토큰을 JSX에서 사용할 수 있도록 도와줍니다. ## Preview ```tsx import { Flex, Grid } from "@seed-design/react"; export default function GridPreview() { return ( {Array.from({ length: 6 }).map((_, index) => ( {index + 1} ))} ); } ``` ## Props \[#props] ### `Grid` \[#grid] - `asChild` - type: `boolean | undefined` - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `flexGrow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: If true, flex-grow will be set to \`1\`. - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - `overflowX` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `overflowY` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `width` - type: `ResponsiveValue | undefined` - `minWidth` - type: `ResponsiveValue | undefined` - `maxWidth` - type: `ResponsiveValue | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `p` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`padding\`. - `px` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `py` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pl` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingLeft\`. - `zIndex` - type: `number | (string & {}) | undefined` - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexDirection` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - `flexWrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: If true, flex-wrap will be set to \`wrap\`. - `justifySelf` - type: `"center" | "stretch" | "start" | "end" | undefined` - description: In flexbox layout, this property is ignored. - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `gridColumn` - type: `string | undefined` - `gridRow` - type: `string | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `as` - type: `React.ElementType | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` - `display` - type: `ResponsiveValue<"grid" | "none"> | undefined` - default: `"grid"` - `align` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - description: Shorthand for \`alignItems\`. - `justify` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - description: Shorthand for \`justifyContent\`. - `justifyItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `columns` - type: `ResponsiveValue | undefined` - description: Shorthand for \`gridTemplateColumns\`. If number, \`repeat(\{columns}, minmax(0, 1fr))\` is applied. - `rows` - type: `ResponsiveValue | undefined` - description: Shorthand for \`gridTemplateRows\`. If number, \`repeat(\{rows}, minmax(0, 1fr))\` is applied. - `autoFlow` - type: `"row" | "column" | "row dense" | "column dense" | undefined` - description: Shorthand for \`gridAutoFlow\`. - `autoColumns` - type: `string | undefined` - description: Shorthand for \`gridAutoColumns\`. - `autoRows` - type: `string | undefined` - description: Shorthand for \`gridAutoRows\`. ### `GridItem` \[#griditem] `GridItem`은 Grid 컨테이너 내에서 아이템의 배치를 제어하는 컴포넌트입니다. Grid 내부의 모든 아이템이 `GridItem`일 필요는 없습니다. - `asChild` - type: `boolean | undefined` - default: `false` - description: If true, the component will render its children directly without a wrapper element. - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `flexGrow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: If true, flex-grow will be set to \`1\`. - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - `overflowX` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `overflowY` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `width` - type: `ResponsiveValue | undefined` - `minWidth` - type: `ResponsiveValue | undefined` - `maxWidth` - type: `ResponsiveValue | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `p` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`padding\`. - `px` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `py` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pl` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingLeft\`. - `display` - type: `ResponsiveValue<"none" | "grid" | "inline" | "block" | "flex" | "inline-flex" | "inline-block"> | undefined` - `zIndex` - type: `number | (string & {}) | undefined` - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexDirection` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - `flexWrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: If true, flex-wrap will be set to \`wrap\`. - `justifySelf` - type: `"center" | "stretch" | "start" | "end" | undefined` - description: In flexbox layout, this property is ignored. - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `as` - type: `React.ElementType | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` - `colSpan` - type: `number | "full" | undefined` - description: Number of columns to span, or "full" for full width (1 / -1). - `rowSpan` - type: `number | "full" | undefined` - description: Number of rows to span, or "full" for full height (1 / -1). - `colStart` - type: `number | undefined` - description: Starting column - `colEnd` - type: `number | undefined` - description: Ending column. - `rowStart` - type: `number | undefined` - description: Starting row - `rowEnd` - type: `number | undefined` - description: Ending row. ## Columns and Rows \[#columns-and-rows] ``의 `columns` 또는 `rows` prop에 number를 지정하여 `grid-template-columns` 또는 `grid-template-rows`를 `repeat(${columns|rows}, minmax(0, 1fr))`로 설정할 수 있습니다. ```tsx import { Divider, Flex, Grid, HStack } from "@seed-design/react"; export default function GridNumber() { return ( {[1, 2, 3, 4, 5].map((n) => ( {n} ))} {[1, 2, 3, 4, 5].map((n) => ( {n} ))} ); } ``` ``의 `columns` 또는 `rows`에 `grid-template-columns` 또는 `grid-template-rows` 값을 직접 지정할 수도 있습니다. ```tsx import { Divider, Flex, Grid, HStack } from "@seed-design/react"; export default function GridString() { return ( {[1, 2, 3, 4, 5].map((n) => ( {n} ))} {[1, 2, 3, 4, 5].map((n) => ( {n} ))} ); } ``` ## Spanning Items \[#spanning-items] ``을 활용하여 그리드 아이템이 여러 열이나 행을 차지하도록 할 수 있습니다. - `colSpan` 또는 `colStart`, `colEnd` prop으로 열 span을 지정합니다. `colSpan="full"`을 전달하면 행 전체 (좌우 양 끝)을 차지합니다. - `rowSpan` 또는 `rowStart`, `rowEnd` prop으로 행 span을 지정합니다. `rowSpan="full"`을 전달하면 열 전체 (상하 양 끝)을 차지합니다. ```tsx import { Divider, Grid, GridItem, HStack } from "@seed-design/react"; export default function Spanning() { return ( 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 ); } ``` ## Auto Flow \[#auto-flow] ``의 `autoFlow` prop을 사용하여 아이템이 배치되는 방향을 지정할 수 있습니다. ```tsx import { useState } from "react"; import { Grid, type GridProps, GridItem, VStack } from "@seed-design/react"; import { SegmentedControl, SegmentedControlItem } from "seed-design/ui/segmented-control"; type AutoFlow = NonNullable; export default function AutoFlow() { const [autoFlow, setAutoFlow] = useState("row"); const isColumn = autoFlow.startsWith("column"); const color = isColumn ? "green" : "purple"; const gridProps = isColumn ? { rows: 3 } : { columns: 3 }; const spanProps = isColumn ? { rowSpan: 2 } : { colSpan: 2 }; return ( {[1, 2].map((n) => ( {n} ))} {[3, 4, 5].map((n) => ( {n} ))} setAutoFlow(value as AutoFlow)} aria-label="Auto Flow" > row row dense column column dense ); } ``` ## Auto Rows / Columns \[#auto-rows--columns] ``의 `autoRows` 또는 `autoColumns` prop을 사용하여 암시적으로 생성되는 행이나 열의 크기를 지정할 수 있습니다. 동적으로 아이템이 추가되는 그리드에서 유용합니다. ```tsx import { Divider, Flex, Grid, HStack } from "@seed-design/react"; export default function AutoRowsColumns() { return ( {[1, 2, 3, 4, 5].map((n) => ( {n === 2 ? "Ea anim non aute minim ea deserunt enim Elit deserunt laborum et quis sit." : n} ))} {[1, 2, 3, 4, 5].map((n) => ( {n === 2 ? "Ea anim non aute minim ea deserunt enim Elit deserunt laborum et quis sit." : n} ))} ); } ``` ## Using `asChild` Prop \[#using-aschild-prop] `GridItem`은 `asChild` prop을 사용하여 자식 요소에 직접 grid 속성을 적용할 수 있습니다. `asChild` prop에 대해 자세히 알아봅니다. ```tsx Link spanning 2 columns ``` ## Using Box for Grid Item Placement \[#using-box-for-grid-item-placement] `GridItem`은 내부적으로 `colSpan`, `colStart`, `colEnd`, `rowSpan`, `rowStart`, `rowEnd` prop을 `gridColumn` 및 `gridRow` 스타일로 변환하여 `Box`에 적용합니다. 따라서 `Box` 컴포넌트에서 `gridColumn`, `gridRow` prop을 직접 사용할 수도 있습니다. ```tsx colSpan=2 colSpan=full colStart=2 colEnd=4 ``` ## Responsive Design \[#responsive-design] Grid의 `columns`, `rows`, `gap` 등에 반응형 값을 전달하여 viewport 크기에 따라 레이아웃을 조정할 수 있습니다. --- file: components/(foundation)/layout/h-stack.mdx # HStack 가로로 쌓이는 레이아웃을 구성합니다. 디자인 토큰을 JSX에서 사용할 수 있도록 도와줍니다. ## Preview ```tsx import { Box, HStack } from "@seed-design/react"; export default function HStackPreview() { return ( 1 2 3 ); } ``` ## Usage \[#usage] ```tsx import { HStack } from "@seed-design/react"; ``` ```tsx ``` ## Props \[#props] - `asChild` - type: `boolean | undefined` - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `flexGrow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: If true, flex-grow will be set to \`1\`. - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - `overflowX` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `overflowY` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `direction` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - description: Shorthand for \`flexDirection\`. - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `width` - type: `ResponsiveValue | undefined` - `minWidth` - type: `ResponsiveValue | undefined` - `maxWidth` - type: `ResponsiveValue | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `p` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`padding\`. - `px` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `py` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pl` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingLeft\`. - `display` - type: `ResponsiveValue<"none" | "flex"> | undefined` - default: `"flex"` - `zIndex` - type: `number | (string & {}) | undefined` - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexWrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: If true, flex-wrap will be set to \`wrap\`. - `justifySelf` - type: `"center" | "stretch" | "start" | "end" | undefined` - description: In flexbox layout, this property is ignored. - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `gridColumn` - type: `string | undefined` - `gridRow` - type: `string | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `as` - type: `React.ElementType | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` - `wrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: Shorthand for \`flexWrap\`. - `align` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - description: Shorthand for \`alignItems\`. - `justify` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - description: Shorthand for \`justifyContent\`. - `grow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: Shorthand for \`flexGrow\`. - `shrink` - type: `true | 0 | (number & {}) | undefined` - description: Shorthand for \`flexShrink\`. ## Responsive Design \[#responsive-design] HStack의 레이아웃 프로퍼티에 반응형 값을 전달하여 viewport 크기에 따라 스타일을 조정할 수 있습니다. --- file: components/(foundation)/layout/v-stack.mdx # VStack 세로로 쌓이는 레이아웃을 구성합니다. 디자인 토큰을 JSX에서 사용할 수 있도록 도와줍니다. ## Preview ```tsx import { Box, VStack } from "@seed-design/react"; export default function VStackPreview() { return ( 1 2 3 ); } ``` ## Usage \[#usage] ```tsx import { VStack } from "@seed-design/react"; ``` ```tsx ``` ## Props \[#props] - `asChild` - type: `boolean | undefined` - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `flexGrow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: If true, flex-grow will be set to \`1\`. - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - `overflowX` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `overflowY` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - `direction` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - description: Shorthand for \`flexDirection\`. - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "full" | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `width` - type: `ResponsiveValue | undefined` - `minWidth` - type: `ResponsiveValue | undefined` - `maxWidth` - type: `ResponsiveValue | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `p` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`padding\`. - `px` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `py` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `pl` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Shorthand for \`paddingLeft\`. - `display` - type: `ResponsiveValue<"none" | "flex"> | undefined` - default: `"flex"` - `zIndex` - type: `number | (string & {}) | undefined` - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexWrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: If true, flex-wrap will be set to \`wrap\`. - `justifySelf` - type: `"center" | "stretch" | "start" | "end" | undefined` - description: In flexbox layout, this property is ignored. - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `gridColumn` - type: `string | undefined` - `gridRow` - type: `string | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `as` - type: `React.ElementType | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` - `wrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: Shorthand for \`flexWrap\`. - `align` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - description: Shorthand for \`alignItems\`. - `justify` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - description: Shorthand for \`justifyContent\`. - `grow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: Shorthand for \`flexGrow\`. - `shrink` - type: `true | 0 | (number & {}) | undefined` - description: Shorthand for \`flexShrink\`. ## Responsive Design \[#responsive-design] VStack의 레이아웃 프로퍼티에 반응형 값을 전달하여 viewport 크기에 따라 스타일을 조정할 수 있습니다. --- file: components/(foundation)/typography/text.mdx # Text 텍스트를 표시하는 기본 타이포그래피 컴포넌트입니다. 일관된 글꼴 스타일과 크기를 적용하여 텍스트를 렌더링할 때 사용됩니다. ## Preview ```tsx import { Flex, Text } from "@seed-design/react"; export default function TextPreview() { return ( t1Regular t2Regular t3Regular t4Regular t5Regular t6Bold t7Bold t8Bold t9Bold t10Bold t11Bold t12Bold t13Bold t14Bold ); } ``` ## Usage \[#usage] ```tsx import { Text } from "@seed-design/react"; ``` ```tsx ``` ## Props \[#props] - `as` - type: `"p" | "dd" | "dt" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "legend" | "span" | "strong" | undefined` - default: `"span"` - description: The element to render as - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - description: The color of the text. - `fontSize` - type: `(string & {}) | "t2" | "t3" | "t4" | "t6" | "t5" | "t1" | "t7" | "t8" | "t9" | "t10" | "t11" | "t12" | "t13" | "t14" | "t1Static" | "t2Static" | "t3Static" | "t4Static" | "t5Static" | "t6Static" | "t7Static" | "t8Static" | "t9Static" | "t10Static" | "t11Static" | "t12Static" | "t13Static" | "t14Static" | undefined` - description: The font size of the text. Partially overrides the textStyle. - `lineHeight` - type: `(string & {}) | "t2" | "t3" | "t4" | "t6" | "t5" | "t1" | "t7" | "t8" | "t9" | "t10" | "t11" | "t12" | "t13" | "t14" | "t1Static" | "t2Static" | "t3Static" | "t4Static" | "t5Static" | "t6Static" | "t7Static" | "t8Static" | "t9Static" | "t10Static" | "t11Static" | "t12Static" | "t13Static" | "t14Static" | undefined` - description: The line height of the text. Partially overrides the textStyle. - `fontWeight` - type: `"bold" | "medium" | "regular" | undefined` - description: The font weight of the text. Partially overrides the textStyle. - `maxLines` - type: `number | undefined` - description: The maximum number of lines to display. If the text overflows, it will be truncated. - `align` - type: `"left" | "right" | "center" | undefined` - description: The alignment of the text. - `userSelect` - type: `"auto" | "none" | "text" | undefined` - description: The user-select behavior of the text. - `whiteSpace` - type: `"nowrap" | "pre" | "break-spaces" | "normal" | "pre-line" | "pre-wrap" | undefined` - description: The white-space behavior of the text. - `textStyle` - type: `"screenTitle" | "articleBody" | "articleNote" | "t1Regular" | "t1Medium" | "t1Bold" | "t2Regular" | "t2Medium" | "t2Bold" | "t3Regular" | "t3Medium" | "t3Bold" | "t4Regular" | "t4Medium" | "t4Bold" | "t5Regular" | "t5Medium" | "t5Bold" | "t6Regular" | "t6Medium" | "t6Bold" | "t7Regular" | "t7Medium" | "t7Bold" | "t8Regular" | "t8Medium" | "t8Bold" | "t9Regular" | "t9Medium" | "t9Bold" | "t10Regular" | "t10Medium" | "t10Bold" | "t11Regular" | "t11Medium" | "t11Bold" | "t12Regular" | "t12Medium" | "t12Bold" | "t13Regular" | "t13Medium" | "t13Bold" | "t14Regular" | "t14Medium" | "t14Bold" | "t1StaticRegular" | "t1StaticMedium" | "t1StaticBold" | "t2StaticRegular" | "t2StaticMedium" | "t2StaticBold" | "t3StaticRegular" | "t3StaticMedium" | "t3StaticBold" | "t4StaticRegular" | "t4StaticMedium" | "t4StaticBold" | "t5StaticRegular" | "t5StaticMedium" | "t5StaticBold" | "t6StaticRegular" | "t6StaticMedium" | "t6StaticBold" | "t7StaticRegular" | "t7StaticMedium" | "t7StaticBold" | "t8StaticRegular" | "t8StaticMedium" | "t8StaticBold" | "t9StaticRegular" | "t9StaticMedium" | "t9StaticBold" | "t10StaticRegular" | "t10StaticMedium" | "t10StaticBold" | "t11StaticRegular" | "t11StaticMedium" | "t11StaticBold" | "t12StaticRegular" | "t12StaticMedium" | "t12StaticBold" | "t13StaticRegular" | "t13StaticMedium" | "t13StaticBold" | "t14StaticRegular" | "t14StaticMedium" | "t14StaticBold" | undefined` - default: `"t5Regular"` - description: - \`screenTitle\`: 화면에 크게 표시되는 주요 제목이나 타이틀에 사용합니다. - \`articleBody\`: 게시물이나 콘텐츠 중심 섹션의 본문 텍스트에 사용합니다. - \`articleNote\`: 주석, 참고 사항 및 상세 리스트 등 부가 정보에 사용하며, 일반 본문 텍스트에는 사용하지 않습니다. - \`t11Regular\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t11Medium\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t11Bold\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t12Regular\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t12Medium\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t12Bold\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t13Regular\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t13Medium\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t13Bold\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t14Regular\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t14Medium\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t14Bold\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. - \`t1StaticRegular\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t1StaticMedium\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t1StaticBold\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t2StaticRegular\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t2StaticMedium\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t2StaticBold\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t3StaticRegular\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t3StaticMedium\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t3StaticBold\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t4StaticRegular\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t4StaticMedium\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t4StaticBold\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t5StaticRegular\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t5StaticMedium\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t5StaticBold\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t6StaticRegular\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t6StaticMedium\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t6StaticBold\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t7StaticRegular\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t7StaticMedium\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t7StaticBold\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t8StaticRegular\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t8StaticMedium\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t8StaticBold\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t9StaticRegular\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t9StaticMedium\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t9StaticBold\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t10StaticRegular\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t10StaticMedium\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t10StaticBold\`: 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t11StaticRegular\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t11StaticMedium\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t11StaticBold\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t12StaticRegular\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t12StaticMedium\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t12StaticBold\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t13StaticRegular\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t13StaticMedium\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t13StaticBold\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t14StaticRegular\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t14StaticMedium\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - \`t14StaticBold\`: \`sm\` breakpoint 이상에서만 사용하는 것을 권장합니다. 폰트 스케일링에 반응하지 않도록 static text size와 static line height 토큰을 사용합니다. - `textDecorationLine` - type: `"none" | "line-through" | "underline" | undefined` - default: `"none"` ## Examples \[#examples] ### Text Styles \[#text-styles] Figma의 Text Style과 대응되는 `textStyle` 속성을 사용하는 것이 기본 방법입니다. `textStyle`은 글꼴 크기, 줄 간격, 글꼴 굵기를 한 번에 설정합니다. ```tsx import { Flex, Text } from "@seed-design/react"; export default function TextTextStyles() { return ( t1Regular t2Regular t3Regular t4Regular t5Regular t6Bold t7Bold t8Bold t9Bold t10Bold ); } ``` #### Font Sizes \[#font-sizes] 더 구체적으로 변경이 필요한 경우 `fontSize` 및 `lineHeight` 속성을 각각 사용할 수 있습니다. `lineHeight` 속성은 `fontSize`에 대응되는 기본값으로 설정되며, `lineHeight` 속성으로 변경이 가능합니다. ```tsx import { Flex, Text } from "@seed-design/react"; export default function TextFontSizes() { return ( t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 ); } ``` #### Font Weights \[#font-weights] 더 구체적으로 변경이 필요한 경우 `fontWeight` 속성을 사용할 수 있습니다. ```tsx import { Flex, Text } from "@seed-design/react"; export default function TextFontWeights() { return ( regular medium bold ); } ``` ### Text Decoration Lines \[#text-decoration-lines] `textDecorationLine` 속성을 사용하여 텍스트에 밑줄 또는 취소선을 추가할 수 있습니다. - 링크가 아닌 텍스트를 강조하기 위해 밑줄을 적용할 때는 사용자 경험을 고려해야 합니다. 밑줄이 있는 텍스트는 링크로 인식될 수 있으므로, 혼동을 피하기 위해 적절한 상황에서만 사용해야 합니다. - 본문 밖 영역에서 인라인 텍스트를 링크 용도로 사용하고자 하는 경우 [ActionButton](/react/components/action-button)을 `variant="ghost" bleedX="asPadding" bleedY="asPadding"` 옵션으로 사용하는 것을 고려해보세요. ```tsx import { Flex, Text } from "@seed-design/react"; export default function TextTextDecorationLines() { return ( underline line-through ); } ``` ### Max Lines \[#max-lines] `maxLines` 속성을 사용하여 텍스트가 차지하는 최대 줄 수를 제한할 수 있습니다. 지정된 줄 수를 초과하는 텍스트는 생략 부호(ellipsis) `…` 로 표시됩니다. ```tsx import { Flex, Text } from "@seed-design/react"; export default function TextMaxLines() { return ( maxLines=1 Aliquip pariatur adipisicing elit consectetur velit commodo Lorem nulla eu. Occaecat sint voluptate ut dolore eiusmod minim qui reprehenderit. Do aliquip tempor ipsum aliqua enim. Incididunt irure do ullamco esse sit enim mollit nisi anim laboris do. Ut fugiat aliquip velit eiusmod ad incididunt. Consequat qui quis in ad culpa officia eu in Lorem. Elit voluptate est veniam aliqua magna Lorem proident incididunt amet aliquip. Aliquip sint sit ex eiusmod sint. Eiusmod incididunt consequat fugiat. maxLines=2 Aliquip pariatur adipisicing elit consectetur velit commodo Lorem nulla eu. Occaecat sint voluptate ut dolore eiusmod minim qui reprehenderit. Do aliquip tempor ipsum aliqua enim. Incididunt irure do ullamco esse sit enim mollit nisi anim laboris do. Ut fugiat aliquip velit eiusmod ad incididunt. Consequat qui quis in ad culpa officia eu in Lorem. Elit voluptate est veniam aliqua magna Lorem proident incididunt amet aliquip. Aliquip sint sit ex eiusmod sint. Eiusmod incididunt consequat fugiat. maxLines=3 Aliquip pariatur adipisicing elit consectetur velit commodo Lorem nulla eu. Occaecat sint voluptate ut dolore eiusmod minim qui reprehenderit. Do aliquip tempor ipsum aliqua enim. Incididunt irure do ullamco esse sit enim mollit nisi anim laboris do. Ut fugiat aliquip velit eiusmod ad incididunt. Consequat qui quis in ad culpa officia eu in Lorem. Elit voluptate est veniam aliqua magna Lorem proident incididunt amet aliquip. Aliquip sint sit ex eiusmod sint. Eiusmod incididunt consequat fugiat. ); } ``` ### User Select \[#user-select] `userSelect` 속성을 사용하여 사용자가 텍스트를 선택할 수 있는지 여부를 제어할 수 있습니다. ```tsx import { Flex, Text } from "@seed-design/react"; export default function TextUserSelect() { return ( auto none text ); } ``` ### Static \[#static] 폰트 스케일링에 반응하지 않는 static 토큰을 활용하는 텍스트 스타일을 적용할 수 있습니다. ```tsx import { Flex, Text } from "@seed-design/react"; export default function TextStatic() { return ( t1StaticRegular t2StaticRegular t3StaticRegular t4StaticRegular t5StaticRegular t6StaticBold t7StaticBold t8StaticBold t9StaticBold t10StaticBold ); } ``` ### White Space \[#white-space] `whiteSpace` prop은 `maxLines` prop을 사용하지 않을 때만 적용됩니다. ```tsx import { Box, Text, VStack } from "@seed-design/react"; const sampleText = `이것은 여러 개 의 공백, 줄바꿈과 들여쓰기를 포함한 샘플 텍스트입니다. 각각의 white-space 속성\n값이 어떻게 작동하는지 보여줍니다.`; export default function TextWhiteSpace() { return ( {(["normal", "nowrap", "pre", "pre-wrap", "pre-line", "break-spaces"] as const).map( (value) => ( {value} {sampleText} ), )} ); } ``` --- file: components/accordion.mdx # Accordion 여러 개의 관련된 콘텐츠 섹션을 수직으로 나열하고, 각 섹션을 펼치거나 접어 정보를 탐색할 수 있는 컴포넌트입니다. ## Preview ```tsx import { Box } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionPreview() { return (

첫 번째 항목의 내용입니다.

두 번째 항목의 내용입니다.

세 번째 항목의 내용입니다.

); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:accordion - pnpm: pnpm dlx @seed-design/cli@latest add ui:accordion - yarn: yarn dlx @seed-design/cli@latest add ui:accordion - bun: bun x @seed-design/cli@latest add ui:accordion ## Props \[#props] ### `Accordion` \[#accordion] - `variant` - type: `"inline" | "separated" | undefined` - default: `"inline"` - description: - \`inline\`: Accordion Item들이 하나의 연속된 목록처럼 표현됩니다. 밀접하게 관련된 항목들을 컴팩트하게 나열할 때 사용합니다. - \`separated\`: 각 Accordion Item이 개별 카드 형태로 분리되어 표현됩니다. 항목 간 시각적 독립성이 필요하거나, 각 섹션의 중요도가 동등할 때 사용합니다. - `size` - type: `"medium" | "large" | "responsive" | undefined` - default: `"medium"` - description: - \`responsive\`: 뷰포트 너비에 따라 적용되는 사이즈가 달라집니다. Breakpoint \`md\` 미만에서는 \`medium\`, \`md\` 이상에서는 \`large\`로 적용됩니다. - `values` - type: `string[] | undefined` - `defaultValues` - type: `string[] | undefined` - `onValuesChange` - type: `((values: string[]) => void) | undefined` - `disabled` - type: `boolean | undefined` - `multiple` - type: `boolean | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `AccordionItem` \[#accordionitem] - `value` - type: `string` - required: `true` - `disabled` - type: `boolean | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `AccordionTrigger` \[#accordiontrigger] - `title` - type: `React.ReactNode` - required: `true` - `description` - type: `React.ReactNode` - `prefix` - type: `React.ReactNode` - `suffixIcon` - type: `React.ReactNode` - `headingLevel` - type: `5 | 1 | 2 | 3 | 4 | 6 | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `AccordionContent` \[#accordioncontent] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Inline Variant \[#inline-variant] 기본 variant입니다. 아이템이 연속된 흐름으로 제공되며, 아이템 사이에 구분선이 표시됩니다. ```tsx import { Box } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionInline() { return (

첫 번째 항목의 내용입니다.

두 번째 항목의 내용입니다.

세 번째 항목의 내용입니다.

); } ``` ### Separated \[#separated] `variant="separated"` 를 사용하면 각 항목이 분리된 카드 형태로 표시됩니다. ```tsx import { Box } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionSeparated() { return (

첫 번째 항목의 내용입니다.

두 번째 항목의 내용입니다.

세 번째 항목의 내용입니다.

); } ``` ### Multiple \[#multiple] 기본적으로 한 번에 하나의 항목만 펼칠 수 있습니다. `multiple` prop을 사용하면 여러 항목을 동시에 펼칠 수 있습니다. ```tsx import { Box } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionMultiple() { return (

여러 항목을 동시에 펼칠 수 있습니다.

각 항목은 다른 항목과 독립적으로 열고 닫힙니다.

세 번째 항목의 내용입니다.

); } ``` ### Always one open \[#always-one-open] `values`와 `onValuesChange`를 사용해 controlled 패턴으로 운영하면, 빈 배열이 들어올 때 setter를 호출하지 않는 가드만 추가하여 항상 하나의 항목이 열려 있도록 강제할 수 있습니다. ```tsx import { Box } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; import { useState } from "react"; export default function AccordionControlledRequiredOpen() { const [values, setValues] = useState(["item-1"]); return ( { if (next.length === 0) return; setValues(next); }} >

현재 항목은 다시 눌러도 닫히지 않고, 다른 항목을 선택할 때만 전환됩니다.

평일 오후 2시 이전 주문은 당일 출고되며, 주말 주문은 다음 영업일에 출고됩니다.

수령 후 7일 이내에 교환 또는 반품을 요청할 수 있습니다.

); } ``` ### Size \[#size] `size`로 Accordion의 크기를 정합니다. (default: `medium`) `responsive`는 화면 너비에 따라 size가 자동으로 전환되는 값입니다. 여러 화면 너비를 함께 지원하는 제품에서 `size=responsive`를 사용하여 대응합니다. ```tsx import { Box, VStack } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionSize() { return ( 항목의 내용입니다. 항목의 내용입니다. 항목의 내용입니다. ); } ``` ### Prefix \[#prefix] `prefix` prop에 아이콘 같은 앞쪽 요소를 전달할 수 있습니다. ```tsx import { Box, Icon } from "@seed-design/react"; import { IconCardLine, IconQuestionmarkCircleLine, IconTruckLine, } from "@karrotmarket/react-monochrome-icon"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionWithPrefixIcon() { return ( } />} title="배송 방법" />

일반 배송, 빠른 배송, 방문 수령 중 주문 상황에 맞는 방법을 선택할 수 있습니다.

} />} title="결제 및 쿠폰" />

카드, 간편결제, 보유 쿠폰을 한 번에 확인하고 결제에 적용할 수 있습니다.

} />} title="문의와 환불" />

주문 취소 가능 시간, 환불 소요 기간, 고객센터 문의 방법을 확인할 수 있습니다.

); } ``` ### Description \[#description] `description` prop으로 트리거에 부가 설명을 추가할 수 있습니다. ```tsx import { Box } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionWithDescription() { return (

첫 번째 항목의 내용입니다.

두 번째 항목의 내용입니다.

세 번째 항목의 내용입니다.

); } ``` ### Disabled \[#disabled] `disabled` prop으로 전체 또는 개별 항목을 비활성화할 수 있습니다. - `Accordion`에 `disabled`를 설정하면 모든 항목이 비활성화됩니다. - `AccordionItem`에 `disabled`를 설정하면 해당 항목만 비활성화됩니다. ```tsx import { Box } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionDisabled() { return (

이 항목은 활성화 상태입니다.

이 항목은 비활성화 상태입니다.

이 항목은 활성화 상태입니다.

); } ``` ### Controlled \[#controlled] `values`와 `onValuesChange`를 사용하여 열림 상태를 직접 제어할 수 있습니다. ```tsx import { Box } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; import { useState } from "react"; export default function AccordionControlled() { const [values, setValues] = useState(["item-1"]); return (

첫 번째 항목의 내용입니다.

두 번째 항목의 내용입니다.

세 번째 항목의 내용입니다.

); } ``` ### Value Array Changes \[#value-array-changes] controlled 모드에서는 현재 열려 있는 항목이 `values` 배열로 전달됩니다. 아래 예시는 트리거를 누를 때마다 최신 `values`와 최근 `onValuesChange` 결과를 함께 보여줍니다. ```tsx import { Box, Text, VStack } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; import { useState } from "react"; const DEFAULT_VALUES = ["shipping"]; export default function AccordionValueChanges() { const [values, setValues] = useState(DEFAULT_VALUES); const [history, setHistory] = useState([DEFAULT_VALUES]); return ( { setValues(nextValues); setHistory((prev) => [nextValues, ...prev].slice(0, 5)); }} >

빠른 배송, 새벽 배송, 방문 수령 옵션을 비교할 수 있습니다.

카드, 계좌이체, 간편결제 중에서 원하는 결제 수단을 선택할 수 있습니다.

주문 취소 가능 시간과 환불 소요 기간을 확인할 수 있습니다.

values: {JSON.stringify(values)} onValuesChange history: {history.map((snapshot, index) => ( {index + 1}. {JSON.stringify(snapshot)} ))}
); } ``` ### Default Expanded \[#default-expanded] `defaultValues`를 사용하여 초기 열림 상태를 지정할 수 있습니다. ```tsx import { Box } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionDefaultExpanded() { return (

첫 번째 항목은 기본으로 펼쳐진 상태입니다.

두 번째 항목의 내용입니다.

세 번째 항목의 내용입니다.

); } ``` ### Custom Content \[#custom-content] `AccordionContent`는 열림/닫힘 애니메이션 컨테이너 역할만 합니다. 기본 패딩, 배경색, 테두리, 타이포그래피 스타일은 제공하지 않으므로 내부 콘텐츠에서 직접 구성해야 합니다. 아래 예시처럼 `Box`로 패딩과 배경을 명시적으로 주는 패턴을 권장합니다. ```tsx import { Box, Text, VStack } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionCustomContent() { return ( 일반 배송 주문 후 영업일 기준 2-3일 내에 배송됩니다. 제주 및 도서산간 지역은 1-2일이 추가 소요될 수 있습니다. 1. 고객센터로 반품/교환 요청 2. 상품 수거 (택배 방문 수거) 3. 검수 후 환불 또는 교환 처리 ); } ``` ## Accessibility \[#accessibility] [WAI-ARIA Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/)을 따릅니다. `AccordionTrigger`는 내부적으로 `heading > button` 구조를 구성합니다. 기본 heading level은 `h3`이며, 문서 구조에 맞춰 다른 level이 필요하면 `headingLevel` prop으로 조정할 수 있습니다. WAI-ARIA APG 예시에서도 상위 섹션 구조에 맞춰 `h3`를 사용합니다. - Pattern: [WAI-ARIA APG Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/) - Example: [WAI-ARIA APG Accordion Example](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/examples/accordion/) ### Heading Level Escape Hatch \[#heading-level-escape-hatch] 상위 섹션 heading이 이미 존재한다면 `headingLevel`로 accordion header의 level을 맞춰 주세요. 예를 들어 accordion이 `h3` 섹션 안에 들어간다면 각 항목 header는 `h4`로 내리는 식으로 문서 outline을 유지할 수 있습니다. ```tsx import { Box, Text, VStack } from "@seed-design/react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "seed-design/ui/accordion"; export default function AccordionHeadingLevel() { return ( 주문 도움말

배송 관련 세부 내용은 h4 heading 아래의 accordion section으로 제공할 수 있습니다.

); } ``` ### 키보드 인터랙션 \[#키보드-인터랙션] | 키 | 동작 | | ----------------- | --------------------------------------- | | `Enter` / `Space` | 포커스된 트리거의 패널을 펼치거나 접습니다. | | `Tab` | 다음 포커스 가능한 요소로 이동합니다. | | `Shift + Tab` | 이전 포커스 가능한 요소로 이동합니다. | | `ArrowDown` | 다음 트리거로 포커스를 이동합니다. 마지막이면 첫 번째로 순환합니다. | | `ArrowUp` | 이전 트리거로 포커스를 이동합니다. 첫 번째이면 마지막으로 순환합니다. | | `Home` | 첫 번째 트리거로 포커스를 이동합니다. | | `End` | 마지막 트리거로 포커스를 이동합니다. | --- file: components/action-button.mdx # Action Button 명확한 액션을 쉽게 수행할 수 있도록 돕는 기본 인터랙션 컴포넌트입니다. ## Preview ```tsx import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonPreview() { return 라벨; } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:action-button - pnpm: pnpm dlx @seed-design/cli@latest add ui:action-button - yarn: yarn dlx @seed-design/cli@latest add ui:action-button - bun: bun x @seed-design/cli@latest add ui:action-button ## Props \[#props] - `color` - type: `ScopedColorFg | ScopedColorPalette | undefined` - default: `"fg.neutral"` - description: Color of the label and icons inside the button. Works only when \`variant\` is \`ghost\`. - `fontWeight` - type: `"bold" | "medium" | "regular" | undefined` - default: `"bold"` - description: Weight of the label. Works only when \`variant\` is \`ghost\`. - `variant` - type: `"brandSolid" | "criticalSolid" | "neutralSolid" | "neutralWeak" | "ghost" | "neutralOutline" | "brandOutline" | undefined` - default: `"brandSolid"` - description: - \`brandSolid\`: 브랜드의 핵심 가치를 전달하며, 사용자 간 연결이 일어나는 서비스의 주요 기능에 사용합니다. 한 화면에 하나만 사용하는 것을 권장합니다. - \`neutralSolid\`: 대부분의 화면에서 CTA로 사용합니다. 한 화면에 하나만 사용하는 것을 권장합니다. - \`neutralWeak\`: CTA를 제외한 대부분의 액션에 사용됩니다. - \`criticalSolid\`: 삭제나 초기화처럼 되돌릴 수 없는 중요한 작업에 사용합니다. - \`brandOutline\`: variant=brandSolid, neutralSolid, criticalSolid와 함께 사용할 수 없으며, variant=neutralOutline과 조합하여 사용하는 것을 권장합니다. - \`neutralOutline\`: variant=brandSolid, neutralSolid, criticalSolid와 함께 사용할 수 없으며, variant=brandOutline과 조합하여 사용하는 것을 권장합니다. - \`ghost\`: 배경 없이 텍스트와 아이콘만 표시됩니다. 모두 동일한 색상을 사용하는 조건에서 icon, prefix icon, suffix icon, label에 정의된 color를 변경할 수 있으며, label의 fontWeight를 \`$font-weight.regular\` 또는 \`$font-weight.medium\`으로 변경하여 주목도를 조절할 수 있습니다. - `size` - type: `"small" | "medium" | "large" | "xsmall" | undefined` - default: `"medium"` - description: - \`xsmall\`: 작은 공간에서 효율적으로 사용할 수 있는 Pill 형태로 제공됩니다. - \`small\`: 화면 중앙에서 범용적으로 사용됩니다. - \`medium\`: 화면 중앙에서 범용적으로 사용됩니다. - \`large\`: 주로 CTA 역할로 사용됩니다. - `layout` - type: `"withText" | "iconOnly" | undefined` - default: `"withText"` - description: - \`withText\`: 텍스트와 함께 아이콘을 표시할 수 있습니다. - \`iconOnly\`: 아이콘만으로 의미를 전달하기 때문에 접근성이 떨어집니다. 꼭 필요한 경우에만 접근성 레이블과 함께 사용하는 것을 권장합니다. - `loading` - type: `boolean | undefined` - default: `false` - description: 버튼에 등록된 비동기 작업이 진행 중임을 사용자에게 알립니다. - `disabled` - type: `boolean | undefined` - default: `false` - description: 버튼의 비활성화 여부를 나타냅니다. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `flexGrow` - type: `true | 0 | 1 | (number & {}) | undefined` - description: If true, flex-grow will be set to \`1\`. ## Examples \[#examples] ### Brand Solid \[#brand-solid] ```tsx import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonBrandSolid() { return 라벨; } ``` ### Neutral Solid \[#neutral-solid] ```tsx import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonNeutralSolid() { return 라벨; } ``` ### Neutral Weak \[#neutral-weak] ```tsx import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonNeutralWeak() { return 라벨; } ``` ### Critical Solid \[#critical-solid] ```tsx import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonCriticalSolid() { return 라벨; } ``` ### Brand Outline \[#brand-outline] ```tsx import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonBrandOutline() { return 라벨; } ``` ### Neutral Outline \[#neutral-outline] ```tsx import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonNeutralOutline() { return 라벨; } ``` ### Ghost \[#ghost] Ghost variant는 `color` 속성을 사용해 레이블과 아이콘의 색상을, `fontWeight` 속성을 사용해 글꼴의 굵기를 변경할 수 있습니다. ```tsx import { HStack, PrefixIcon, VStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { IconTagFill } from "@karrotmarket/react-monochrome-icon"; export default function ActionButtonGhost() { return ( } /> Default (fg.neutral) } /> Neutral Subtle } /> Brand Default (Bold) Medium Regular ); } ``` ### Icon Only \[#icon-only] ```tsx import { IconPlusFill } from "@karrotmarket/react-monochrome-icon"; import { Icon } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonIconOnly() { return ( } /> ); } ``` ### Prefix Icon \[#prefix-icon] ```tsx import { IconPlusFill } from "@karrotmarket/react-monochrome-icon"; import { PrefixIcon } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonPrefixIcon() { return ( } /> 라벨 ); } ``` ### Suffix Icon \[#suffix-icon] ```tsx import { IconChevronRightFill } from "@karrotmarket/react-monochrome-icon"; import { SuffixIcon } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonSuffixIcon() { return ( 라벨 } /> ); } ``` ### Disabled \[#disabled] ```tsx import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonDisabled() { return 라벨; } ``` ### Loading \[#loading] ```tsx import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonLoading() { const [loading, setLoading] = useState(false); function handleClick() { setLoading(true); setTimeout(() => setLoading(false), 2000); } // 이벤트 핸들링이 필요할 수 있으므로 loading은 disabled를 포함하지 않습니다. 이벤트 발생을 원하지 않는 경우, disabled 속성을 추가해주세요. return ( 시간이 걸리는 액션 ); } ``` ### Bleed \[#bleed] `bleedX`, `bleedY` 속성을 사용해 버튼이 레이아웃에서 "빠져나오게" 할 수 있습니다. Ghost variant를 시각적으로 정렬해야 할 때 유용합니다. ```tsx import { HStack, Text, VStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; export default function ActionButtonBleed() { return ( Bleed Example Bleed Y Bleed Example Bleed all sides ); } ``` --- file: components/alert-dialog.mdx # Alert Dialog 사용자의 확인이 반드시 필요한 경우 강력한 표현 및 경고 수단으로 활용하는 컴포넌트입니다. ## Preview ```tsx import { ResponsivePair } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; const AlertDialogSingle = () => { return ( // You can set z-index dialog with "--layer-index" custom property. useful for stackflow integration. 열기 주의 이 작업은 되돌릴 수 없습니다. 취소 확인 ); }; export default AlertDialogSingle; ``` Stackflow와 Alert Dialog를 함께 사용하는 방법에 대해 알아보세요. ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:alert-dialog - pnpm: pnpm dlx @seed-design/cli@latest add ui:alert-dialog - yarn: yarn dlx @seed-design/cli@latest add ui:alert-dialog - bun: bun x @seed-design/cli@latest add ui:alert-dialog ## Props \[#props] ### `AlertDialogRoot` \[#alertdialogroot] - `closeOnInteractOutside` - type: `boolean | undefined` - default: `false` - description: Whether to close the dialog when the outside is clicked - `lazyMount` - type: `boolean | undefined` - default: `true` - description: Whether to enable lazy mounting - `unmountOnExit` - type: `boolean | undefined` - default: `true` - description: Whether to unmount on exit. - `skipAnimation` - type: `boolean | undefined` - default: `false` - `children` - type: `React.ReactNode` - required: `true` - `role` - type: `"alertdialog" | "dialog" | undefined` - default: `"dialog"` - description: The role of the dialog. - `modal` - type: `boolean | undefined` - default: `true` - description: Whether the dialog should behave as a modal overlay. When true, focus is trapped, background content is hidden from assistive technology, and \`aria-modal\` is set. Set to \`false\` to temporarily suspend modal behavior (e.g., when a Stackflow Activity is pushed on top of a mounted dialog). - `closeOnEscape` - type: `boolean | undefined` - default: `true` - description: Whether to close the dialog when the escape key is pressed - `open` - type: `boolean | undefined` - `defaultOpen` - type: `boolean | undefined` - `onOpenChange` - type: `((open: boolean, details?: DialogChangeDetails) => void) | undefined` ### `AlertDialogTrigger` \[#alertdialogtrigger] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `AlertDialogContent` \[#alertdialogcontent] - `layerIndex` - type: `number | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `AlertDialogHeader` \[#alertdialogheader] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `AlertDialogTitle` \[#alertdialogtitle] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `AlertDialogDescription` \[#alertdialogdescription] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `AlertDialogFooter` \[#alertdialogfooter] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Trigger \[#trigger] ``는 `aria-haspopup="dialog"` 속성을 설정하고, AlertDialog의 `open` 상태에 따라 `aria-expanded` 속성을 자동으로 설정합니다. 이 속성은 스크린 리더와 같은 보조 기술에 유용합니다. ## Preview ```tsx import { ResponsivePair } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; const AlertDialogSingle = () => { return ( // You can set z-index dialog with "--layer-index" custom property. useful for stackflow integration. 열기 주의 이 작업은 되돌릴 수 없습니다. 취소 확인 ); }; export default AlertDialogSingle; ``` ### Responsive Wrapping \[#responsive-wrapping] `` 컴포넌트를 사용해 버튼 컨텐츠가 길어지는 경우 레이아웃을 세로로 접을 수 있습니다. ```tsx import { PrefixIcon, ResponsivePair } from "@seed-design/react"; import { IconCheckFill } from "@seed-design/react-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; const AlertDialogWrap = () => { return ( // You can set z-index dialog with "--layer-index" custom property. useful for stackflow integration. 열기 Wrapping ResponsivePair 컴포넌트를 사용해 버튼 컨텐츠가 길어지는 경우 레이아웃을 세로로 접을 수 있습니다. 취소 } />긴 레이블 예시 ); }; export default AlertDialogWrap; ``` ### Single Action \[#single-action] ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; const AlertDialogSingle = () => { // You can set z-index dialog with "--layer-index" custom property. useful for stackflow integration. return ( 열기 제목 단일 선택지를 제공합니다. 확인 ); }; export default AlertDialogSingle; ``` ### Neutral Secondary Action \[#neutral-secondary-action] ```tsx import { ResponsivePair } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; const AlertDialogNeutral = () => { return ( // You can set z-index dialog with "--layer-index" custom property. useful for stackflow integration. 열기 제목 중립적인 선택지를 제공합니다. {/* ResponsivePair component wraps layout if button content is too long. */} 취소 확인 ); }; export default AlertDialogNeutral; ``` ### Nonpreferred \[#nonpreferred] ```tsx import { VStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; const AlertDialogNonpreferred = () => { return ( // You can set z-index dialog with "--layer-index" custom property. useful for stackflow integration. 열기 제목 중립적인 선택지를 제공합니다. 라벨 라벨 ); }; export default AlertDialogNonpreferred; ``` ### Critical Action \[#critical-action] ```tsx import { ResponsivePair } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; const AlertDialogCritical = () => { return ( // You can set z-index dialog with "--layer-index" custom property. useful for stackflow integration. 열기 제목 파괴적, 비가역적 작업을 경고합니다. {/* ResponsivePair component wraps layout if button content is too long. */} 취소 확인 ); }; export default AlertDialogCritical; ``` ### Controlled \[#controlled] Trigger 외의 방식으로 AlertDialog를 열고 닫을 수 있습니다. 이 경우 `open` prop을 사용하여 AlertDialog의 상태를 제어합니다. ```tsx import { ResponsivePair } from "@seed-design/react"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, } from "seed-design/ui/alert-dialog"; const AlertDialogControlled = () => { const [open, setOpen] = useState(false); return ( <> setOpen(true)}> 열기 주의 이 작업은 되돌릴 수 없습니다. setOpen(false)}> 취소 setOpen(false)}> 확인 ); }; export default AlertDialogControlled; ``` ### Prevent Close \[#prevent-close] `AlertDialogAction`의 `onClick`에서 `e.preventDefault()`를 호출하면 다이얼로그가 닫히지 않습니다. ```tsx import { Box, VStack } from "@seed-design/react"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; import { Switch } from "seed-design/ui/switch"; export default function AlertDialogPreventClose() { const [preventClose, setPreventClose] = useState(true); return ( 열기 닫기 방지 확인 버튼을 눌러도 다이얼로그가 닫히지 않도록 설정할 수 있습니다. { if (preventClose) { e.preventDefault(); } }} > 확인 ); } ``` ### `onOpenChange` Details \[#onopenchange-details] `onOpenChange` 두 번째 인자로 `details`가 제공됩니다. #### `reason` \[#reason] **열릴 때** (`open: true`) - `"trigger"`: `AlertDialogTrigger` (`Dialog.Trigger`)로 열림 **닫힐 때** (`open: false`) - `"closeButton"`: `AlertDialogAction`으로 닫힘 - `"escapeKeyDown"`: ESC 키 사용 - `"interactOutside"`: 외부 영역 클릭 - `AlertDialogRoot`는 기본적으로 `closeOnInteractOutside={false}`입니다. `interactOutside`는 이 옵션을 `true`로 설정한 경우에만 발생할 수 있습니다. ```tsx import { HStack, ResponsivePair, Text, VStack } from "@seed-design/react"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; export default function AlertDialogOnOpenChangeReason() { const [open, setOpen] = useState(false); const [openReason, setOpenReason] = useState(null); const [closeReason, setCloseReason] = useState(null); return ( { setOpen(open); (open ? setOpenReason : setCloseReason)(details?.reason ?? null); }} > 열기 알림 ESC 키를 누르거나 버튼을 클릭하여 닫아보세요. 취소 확인 마지막 열림 이유: {openReason ?? "-"} 마지막 닫힘 이유: {closeReason ?? "-"} ); } ``` ### Portalled \[#portalled] Portal은 기본적으로 `document.body`에 렌더링됩니다. ```tsx import { ResponsivePair, Portal } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; const AlertDialogPortalled = () => { return ( // You can set z-index dialog with "--layer-index" custom property. useful for stackflow integration. 열기 주의 이 작업은 되돌릴 수 없습니다. 취소 확인 ); }; export default AlertDialogPortalled; ``` ### Skip Animation \[#skip-animation] `skipAnimation` prop을 사용하여 AlertDialog의 enter/exit 애니메이션을 건너뛸 수 있습니다. ```tsx import { ResponsivePair } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; const AlertDialogSkipAnimation = () => { return ( 열기 주의 이 작업은 되돌릴 수 없습니다. 취소 확인 ); }; export default AlertDialogSkipAnimation; ``` ### Stackflow \[#stackflow] ```tsx import { useActivityZIndexBase } from "@seed-design/stackflow"; import { type StaticActivityComponentType, useFlow } from "@stackflow/react/future"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, } from "seed-design/ui/alert-dialog"; declare module "@stackflow/config" { interface Register { ActivityAlertDialogStackflow: {}; } } const ActivityAlertDialogStackflow: StaticActivityComponentType< "ActivityAlertDialogStackflow" > = () => { const { pop } = useFlow(); return ( !open && pop()}> 제목 Stackflow 확인 ); }; export default ActivityAlertDialogStackflow; ``` --- file: components/article.mdx # Article Article은 일관된 selection 및 줄바꿈 정책을 사용할 수 있게 돕는 유틸리티 컴포넌트입니다. ## Preview ```tsx import { Article, Text, VStack } from "@seed-design/react"; export default function ArticlePreview() { return (
Article은 일관된 selection 및 줄바꿈 정책을 사용할 수 있게 돕는 유틸리티 컴포넌트입니다. 여기를 드래그해서 선택해보세요.
); } ``` ## Usage \[#usage] ```tsx import { Article } from "@seed-design/react"; ``` ```tsx
Article
``` ## Props \[#props] [BoxProps](/react/components/layout/box#props)와 동일합니다. ## Examples \[#examples] ### Word Break Behavior \[#word-break-behavior] [`lang` global attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/lang)를 통해 추론된 언어에 따라 단어 내 줄바꿈(word-break) 동작을 설정합니다. 한국어 문서/요소에서는 한 어절 안에서 줄바꿈이 발생하지 않도록 하고, 그 외 언어는 표준 규칙을 따릅니다. 컨테이너를 벗어날 정도로 긴 단어 안에서는 줄바꿈이 발생합니다. 일본어에서 문장 부호 직전, [스테가나](https://ko.wikipedia.org/wiki/스테가나) 직전, [반복 부호](https://ko.wikipedia.org/wiki/반복_부호) 직전 등 어색한 위치에서 줄바꿈이 발생하지 않도록 조정됩니다. ```tsx import { Article, Text, VStack } from "@seed-design/react"; export default function ArticleWordBreak() { return (
ja-JP 日本語の禁則処理では、特定の文字の前後で改行を制御します。例えば人々々々と続く場合や、小さい文字ぁぁぁが連続する場合、そして句読点。。。が続く場合の改行位置を確認できます。 また長い文章では自動的に適切な位置で改行されますが々ぁ。などの文字の前では改行されないことを確認してください。{" "} https://www.example.com/this-is-a-very-long-url-that-might-cause-layout-issues-if-the-word-break-is-not-handled-properly?utm_source=newsletter&utm_medium=email&utm_campaign=spring_sale
핸들을 잡고 너비를 조정해보세요.
); } ``` ### User Select Behavior \[#user-select-behavior] `
` 내부 요소는 사용자가 선택(user-select)할 수 있습니다. ```tsx import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { Article, Divider, Icon, Text, VStack } from "@seed-design/react"; import { PageBanner } from "seed-design/ui/page-banner"; export default function ArticleSelectable() { return ( } description="상위 요소에 `user-select: none;` 스타일 적용됨" tone="warning" variant="solid" /> Article 밖은 선택할 수 없습니다. 상위 요소에 `user-select: none;` 스타일이 적용되어 있어 이 영역의 텍스트는 선택할 수 없습니다. 길게 탭하거나 더블 클릭해보세요.
Article 안 상위 요소에 `user-select: none;` 스타일이 적용되었지만 Article 내부는 선택할 수 있습니다. 길게 탭하거나 더블 클릭해서 텍스트를 선택해보세요.
Article 밖은 선택할 수 없습니다. 길게 탭하거나 더블 클릭해보세요.
); } ``` #### Disable User Selection \[#disable-user-selection] Text 컴포넌트의 [`userSelect="none"` prop](/react/components/typography/text#user-select)을 사용하여 `
` 내부 요소를 선택 불가능하게 만들 수 있습니다. Text 컴포넌트에 대해 자세히 알아봅니다. ```tsx import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { Article, Divider, Icon, Text, VStack } from "@seed-design/react"; import { PageBanner } from "seed-design/ui/page-banner"; export default function ArticleSelectable() { return ( } description="상위 요소에 `user-select: none;` 스타일 적용됨" tone="warning" variant="solid" /> Article 밖은 선택할 수 없습니다. 상위 요소에 `user-select: none;` 스타일이 적용되어 있어 이 영역의 텍스트는 선택할 수 없습니다. 길게 탭하거나 더블 클릭해보세요.
Article 안 상위 요소에 `user-select: none;` 스타일이 적용되었지만 Article 내부는 선택할 수 있습니다. 길게 탭하거나 더블 클릭해서 텍스트를 선택해보세요. 이 요소는 Article 내부에 있지만 선택할 수 없습니다.
Article 밖은 선택할 수 없습니다. 길게 탭하거나 더블 클릭해보세요.
); } ``` #### Prevent `PullToRefresh` or `TabsCarousel` Gestures \[#prevent-pulltorefresh-or-tabscarousel-gestures] `
` 내부 요소는 사용자 선택이 가능합니다. 따라서, 드래그 동작을 통해 내부 요소를 선택 시 의도하지 않은 PTR(당겨서 새로고침) 또는 탭 스와이프 제스처가 발생할 수 있습니다. `PullToRefresh.preventPull` 또는 `Tabs.carouselPreventDrag`를 `
`에 전달하여 Article에서 발생한 이벤트가 제스처를 트리거하지 않도록 할 수 있습니다. PullToRefresh 컴포넌트에서 제스처를 방지할 영역을 지정하는 방법에 대해 알아봅니다. TabsCarousel 컴포넌트에서 제스처를 방지할 영역을 지정하는 방법에 대해 알아봅니다. ```tsx import { VStack, Icon, Text, Article, Divider, PullToRefresh } from "@seed-design/react"; import { type StaticActivityComponentType } from "@stackflow/react/future"; import { AppBar, AppBarMain } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { PullToRefreshContent, PullToRefreshIndicator, PullToRefreshRoot, } from "seed-design/ui/pull-to-refresh"; import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { PageBanner } from "seed-design/ui/page-banner"; declare module "@stackflow/config" { interface Register { ActivityArticlePreventPull: {}; } } const ActivityArticlePreventPull: StaticActivityComponentType< "ActivityArticlePreventPull" > = () => { return ( Pull To Refresh {}} onPtrRefresh={async () => { await new Promise((resolve) => setTimeout(resolve, 1000)); }} > } description="상위 요소에 `user-select: none;` 스타일 적용됨" tone="warning" variant="solid" />
Article 이 요소는 Article 내부에 있으므로 텍스트 선택이 가능합니다. 이 Article은 PTR 제스처를 호출하지 않도록 설정되어 있습니다. 여기를 아래로 끌어 당기면 PTR이 작동하는 대신 텍스트가 선택됩니다.
Article 외부 이 요소는 Article 외부에 있으므로 텍스트 선택이 불가능합니다. 이 요소는 PTR을 호출할 수 있습니다. 여기를 아래로 끌어 당겨보세요.
); }; export default ActivityArticlePreventPull; ```
```tsx import { VStack, Icon, Text, Article, Divider, Tabs } from "@seed-design/react"; import { type StaticActivityComponentType } from "@stackflow/react/future"; import { AppBar, AppBarMain } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { TabsCarousel, TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { PageBanner } from "seed-design/ui/page-banner"; declare module "@stackflow/config" { interface Register { ActivityArticlePreventDrag: {}; } } const ActivityArticlePreventDrag: StaticActivityComponentType< "ActivityArticlePreventDrag" > = () => { return ( Tabs Tab 1 Tab 2 } description="상위 요소에 `user-select: none;` 스타일 적용됨" tone="warning" variant="solid" />
Article 이 요소는 Article 내부에 있으므로 텍스트 선택이 가능합니다. 이 Article은 Tabs 제스처를 호출하지 않도록 설정되어 있습니다. 여기를 왼쪽으로 스와이프하면 탭이 전환되는 대신 텍스트가 선택됩니다.
Article 외부 이 요소는 Article 외부에 있으므로 텍스트 선택이 불가능합니다. 이 요소는 탭 스와이프를 호출할 수 있습니다. 여기를 왼쪽으로 스와이프해보세요.
안녕하세요!
); }; export default ActivityArticlePreventDrag; ```
### Using `asChild` or `as` prop \[#using-aschild-or-as-prop] `
`은 기본적으로 `
`로 렌더링되지만, `asChild` 또는 `as` prop을 사용하여 다른 요소로 변경할 수 있습니다. `asChild` prop에 대해 자세히 알아봅니다. ```tsx import { Article, Divider, VStack, Text } from "@seed-design/react"; export default function ArticleAs() { return (
`as` prop으로 Article을 section으로 변경 Nulla exercitation quis aliqua nostrud.
`asChild` prop으로 Article을 section으로 변경 Elit fugiat elit exercitation laborum id veniam consequat ipsum sit voluptate velit.
); } ``` --- file: components/aspect-ratio.mdx # Aspect Ratio 가로(width)가 정해지면 비율에 따라 세로(height)가 자동으로 결정되는 레이아웃 컨테이너입니다. ## Preview ```tsx import { AspectRatio, Text, VStack } from "@seed-design/react"; export default function AspectRatioPreview() { return ( 4 / 3 1:1 16 / 9 ); } ``` ## Usage \[#usage] ```tsx import { AspectRatio } from "@seed-design/react"; ``` ```tsx ... ``` ## Props \[#props] - `bg` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorPalette | ScopedColorBg | ScopedColorBanner | undefined` - `bgGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - description: Shorthand for \`backgroundGradient\`. - `backgroundGradient` - type: `"glowMagic" | "glowMagicPressed" | "highlightMagic" | "highlightMagicPressed" | "shimmerMagic" | "shimmerNeutral" | undefined` - `bgGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: Shorthand for \`backgroundGradientDirection\`. e.g. \`43deg\` - `backgroundGradientDirection` - type: `(string & {}) | "to right" | "to left" | "to top" | "to bottom" | "to top right" | "to top left" | "to bottom right" | "to bottom left" | undefined` - description: e.g. \`43deg\` - `color` - type: `(string & {}) | ScopedColorFg | ScopedColorPalette | undefined` - `borderColor` - type: `(string & {}) | ScopedColorPalette | ScopedColorStroke | undefined` - `borderWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderTopWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRightWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderBottomWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderLeftWidth` - type: `0 | (string & {}) | 1 | undefined` - `borderRadius` - type: `0 | (string & {}) | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | "full" | undefined` - `borderTopLeftRadius` - type: `0 | (string & {}) | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | "full" | undefined` - `borderTopRightRadius` - type: `0 | (string & {}) | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | "full" | undefined` - `borderBottomRightRadius` - type: `0 | (string & {}) | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | "full" | undefined` - `borderBottomLeftRadius` - type: `0 | (string & {}) | "r0_5" | "r1" | "r1_5" | "r2" | "r2_5" | "r3" | "r3_5" | "r4" | "r5" | "r6" | "full" | undefined` - `boxShadow` - type: `(string & {}) | "s1" | "s2" | "s3" | undefined` - `width` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `minWidth` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `maxWidth` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `height` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `minHeight` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `maxHeight` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `top` - type: `0 | (string & {}) | undefined` - `left` - type: `0 | (string & {}) | undefined` - `right` - type: `0 | (string & {}) | undefined` - `bottom` - type: `0 | (string & {}) | undefined` - `padding` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `p` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - description: Shorthand for \`padding\`. - `paddingX` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `px` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - description: Shorthand for \`paddingX\`. - `paddingY` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `py` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - description: Shorthand for \`paddingY\`. - `paddingTop` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | "safeArea"> | undefined` - `pt` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | "safeArea"> | undefined` - description: Shorthand for \`paddingTop\`. - `paddingRight` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `pr` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - description: Shorthand for \`paddingRight\`. - `paddingBottom` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | "safeArea"> | undefined` - `pb` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | "safeArea"> | undefined` - description: Shorthand for \`paddingBottom\`. - `paddingLeft` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `pl` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - description: Shorthand for \`paddingLeft\`. - `display` - type: `ResponsiveValue<"grid" | "none" | "block" | "flex" | "inline-flex" | "inline" | "inline-block"> | undefined` - `position` - type: `"fixed" | "relative" | "absolute" | "sticky" | undefined` - default: `"relative"` - `overflowX` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - default: `"hidden"` - `overflowY` - type: `"hidden" | "visible" | "scroll" | "auto" | undefined` - default: `"hidden"` - `zIndex` - type: `number | (string & {}) | undefined` - `flexGrow` - type: `true | 0 | (number & {}) | 1 | undefined` - description: If true, flex-grow will be set to \`1\`. - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `flexDirection` - type: `ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse"> | undefined` - `flexWrap` - type: `true | "wrap" | "wrap-reverse" | "nowrap" | undefined` - description: If true, flex-wrap will be set to \`wrap\`. - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `justifySelf` - type: `"center" | "start" | "end" | "stretch" | undefined` - description: In flexbox layout, this property is ignored. - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignContent` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `alignSelf` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` - `gap` - type: `ResponsiveValue<0 | (string & {}) | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `gridColumn` - type: `string | undefined` - `gridRow` - type: `string | undefined` - `unstable_transform` - type: `string | undefined` - `_active` - type: `{ bg?: ScopedColorBg | ScopedColorPalette | (string & {}); background?: ScopedColorBg | ScopedColorPalette | (string & {}); } | undefined` - `bleed` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative margin on all four sides to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedX` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative x-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedY` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative y-axis margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedTop` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative top margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedRight` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative right margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedBottom` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative bottom margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `bleedLeft` - type: `ResponsiveValue<0 | "asPadding" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - description: Negative left margin to extend the element outside its parent. If set to "asPadding", it will use the padding value in the same direction. Cannot be combined with any \`margin\*\` prop. - `margin` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Margin on all four sides. Cannot be combined with any \`bleed\*\` prop. - `m` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`margin\`. Cannot be combined with any \`bleed\*\` prop. - `marginX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Horizontal margin (left + right). Cannot be combined with any \`bleed\*\` prop. - `mx` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginX\`. Cannot be combined with any \`bleed\*\` prop. - `marginY` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Vertical margin (top + bottom). Cannot be combined with any \`bleed\*\` prop. - `my` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginY\`. Cannot be combined with any \`bleed\*\` prop. - `marginTop` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Top margin. Cannot be combined with any \`bleed\*\` prop. - `mt` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginTop\`. Cannot be combined with any \`bleed\*\` prop. - `marginRight` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Right margin. Cannot be combined with any \`bleed\*\` prop. - `mr` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginRight\`. Cannot be combined with any \`bleed\*\` prop. - `marginBottom` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Bottom margin. Cannot be combined with any \`bleed\*\` prop. - `mb` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginBottom\`. Cannot be combined with any \`bleed\*\` prop. - `marginLeft` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Left margin. Cannot be combined with any \`bleed\*\` prop. - `ml` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {}) | "auto"> | undefined` - description: Shorthand for \`marginLeft\`. Cannot be combined with any \`bleed\*\` prop. - `as` - type: `React.ElementType | undefined` - `asChild` - type: `boolean | undefined` - `hideFrom` - type: `BreakpointThreshold | undefined` - `ratio` - type: `number | undefined` - default: `4 / 3` - description: The aspect ratio of the aspect ratio container (width / height). ## Examples \[#examples] ### Ratio \[#ratio] 다양한 비율을 지정할 수 있습니다. `1`은 정사각형, `4/3`은 일반적인 사진 비율, `16/9`는 와이드스크린 비율입니다. ```tsx import { AspectRatio, Box, HStack } from "@seed-design/react"; export default function AspectRatioRatio() { return ( 1:1 Square 4:3 16:9 ); } ``` --- file: components/attachment-display-field.mdx # Attachment Display Field 외부 소스에서 제공된 미디어를 URL 기반으로 표시하고 관리하는 컴포넌트입니다. ## Preview ```tsx import { VStack } from "@seed-design/react"; import type { DisplayItemEntry } from "@seed-design/react/primitive"; import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; const sampleEntries: DisplayItemEntry[] = [ { id: "1", thumbnailUrl: "https://picsum.photos/seed/seed1/200/200", status: "success", }, { id: "2", thumbnailUrl: "https://picsum.photos/seed/seed2/200/200", status: "success", }, ]; // 외부 미디어 피커 모킹. 실제 환경에서는 네이티브 브릿지/모달/서버 호출 등으로 교체하세요. async function openMediaPicker(): Promise { const id = crypto.randomUUID(); return [ { id, thumbnailUrl: `https://picsum.photos/seed/${id}/200/200`, status: "success", }, ]; } export default function AttachmentDisplayPreview() { return ( { addEntries(await openMediaPicker()); }} /> ); } ``` ## Installation \[#installation] ### Default \[#default] 순서 변경이 불필요한 경우 사용할 수 있는 컴포넌트를 포함합니다. - npm: npx @seed-design/cli@latest add ui:attachment-display-field - pnpm: pnpm dlx @seed-design/cli@latest add ui:attachment-display-field - yarn: yarn dlx @seed-design/cli@latest add ui:attachment-display-field - bun: bun x @seed-design/cli@latest add ui:attachment-display-field ### Reorderable \[#reorderable] 드래그 앤 드롭을 통한 항목 순서 변경이 필요한 경우 활용할 수 있는 컴포넌트를 포함합니다. 프로젝트에 [dnd-kit](https://dndkit.com/overview) 의존성이 추가됩니다. - npm: npx @seed-design/cli@latest add ui:attachment-display-field-reorderable - pnpm: pnpm dlx @seed-design/cli@latest add ui:attachment-display-field-reorderable - yarn: yarn dlx @seed-design/cli@latest add ui:attachment-display-field-reorderable - bun: bun x @seed-design/cli@latest add ui:attachment-display-field-reorderable ## Props \[#props] ### `AttachmentDisplayField` \[#attachmentdisplayfield] - `label` - type: `React.ReactNode` - `labelWeight` - type: `"bold" | "medium" | undefined` - default: `"medium"` - `indicator` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `errorMessage` - type: `React.ReactNode` - `showRequiredIndicator` - type: `boolean | undefined` - `disabled` - type: `boolean | undefined` - default: `false` - `invalid` - type: `boolean | undefined` - default: `false` - `readOnly` - type: `boolean | undefined` - default: `false` - `maxEntries` - type: `number | undefined` - default: `1` - `entries` - type: `DisplayItemEntry[] | undefined` - `defaultEntries` - type: `DisplayItemEntry[] | undefined` - `onEntriesChange` - type: `((entries: DisplayItemEntry[]) => void) | undefined` ### `AttachmentDisplay` \[#attachmentdisplay] - `onTriggerClick` - type: `(helpers: Pick) => void` - required: `true` - `children` - type: `((context: UseAttachmentDisplayContext) => React.ReactNode) | undefined` - `onRetry` - type: `((entry: DisplayItemEntry, helpers: Pick) => void) | undefined` ### `AttachmentDisplayItem` \[#attachmentdisplayitem] - `onRetry` - type: `(() => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `entry` - type: `DisplayItemEntry` - required: `true` ## Usage \[#usage] ### 기본 사용법 \[#기본-사용법] `AttachmentDisplayField` 안에 `AttachmentDisplay` 또는 `AttachmentDisplayReorderable`을 조합하여 사용합니다. `AttachmentDisplay`는 HTML ``을 사용하지 않습니다. `onTriggerClick`으로 외부 미디어 피커를 호출하고, 콜백 인자로 전달되는 `addEntries`에 그 결과를 넘겨 표시하세요. `addEntries`는 `maxEntries` 상한과 single-mode(`maxEntries={1}`) 치환을 내부에서 처리하므로, `entries`를 직접 펼쳐 넣는 것보다 안전합니다. [`AttachmentField`](/react/components/attachment-field)와 달리 `AttachmentDisplayField`는 파일의 유효성을 검증하거나 파일 객체를 직접 다루지 않습니다. ```tsx import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; { const pickedEntries = await openMediaPicker(); addEntries(pickedEntries); }} /> ; ``` `entries`와 `onEntriesChange`로 목록을 직접 제어하는 controlled 방식도 지원합니다. 이 경우에도 `addEntries`는 동일하게 동작합니다([Controlled](#controlled) 참고). ### Item 직접 구성하기 \[#item-직접-구성하기] `AttachmentDisplay`, `AttachmentDisplayReorderable`은 `children`을 render prop으로 사용합니다. `entries`를 활용하여 `AttachmentDisplayItem`을 직접 렌더링할 수 있습니다. 이때 `DisplayItemEntry` 타입이 제공하는 `id`를 `key`로 활용하는 것을 권장합니다. `children`을 제공하지 않는 경우 자동으로 `entries`를 `AttachmentDisplayItem`으로 렌더링합니다. ```tsx import { AttachmentDisplay, AttachmentDisplayField, AttachmentDisplayItem, } from "seed-design/ui/attachment-display-field"; { addEntries(await openMediaPicker()); }} > {({ entries }) => entries.map((entry) => ( )) } ; ``` ## Adding Entries \[#adding-entries] ### Trigger \[#trigger] `AttachmentDisplay`는 trigger(업로드 버튼)가 포함된 레이아웃을 제공합니다. trigger 클릭 시 `onTriggerClick` 콜백이 실행됩니다. 일반적으로 외부 미디어 피커 호출을 수행합니다. 콜백은 `{ addEntries, updateEntryStatus }`를 인자로 받아, 피커 결과를 추가하고 곧바로 업로드 상태를 갱신할 수 있습니다. ```tsx import type { DisplayItemEntry } from "@seed-design/react/primitive"; import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; const defaultEntries: DisplayItemEntry[] = [ { id: "1", thumbnailUrl: "https://picsum.photos/seed/trigger1/200/200", status: "success", }, ]; // 외부 미디어 피커 모킹. 실제 환경에서는 네이티브 브릿지/모달/서버 호출 등으로 교체하세요. async function openMediaPicker(): Promise { const id = crypto.randomUUID(); return [ { id, thumbnailUrl: `https://picsum.photos/seed/${id}/200/200`, status: "success", }, ]; } export default function AttachmentDisplayTrigger() { return ( { addEntries(await openMediaPicker()); }} /> ); } ``` ### Listening to Entry Changes \[#listening-to-entry-changes] `entries`는 현재 표시되고 있는 항목의 목록입니다. `onEntriesChange` 콜백으로 `entries`에 등록된 파일 변경 이벤트를 감지할 수 있습니다. ```tsx "use client"; import { Text, VStack } from "@seed-design/react"; import type { DisplayItemEntry } from "@seed-design/react/primitive"; import { useRef, useState } from "react"; import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; // 외부 미디어 피커 모킹. 실제 환경에서는 네이티브 브릿지/모달/서버 호출 등으로 교체하세요. async function openMediaPicker(): Promise { const id = crypto.randomUUID(); return [ { id, thumbnailUrl: `https://picsum.photos/seed/${id}/200/200`, status: "success", }, ]; } export default function AttachmentDisplayValueChanges() { const [entries, setEntries] = useState([]); const entriesRef = useRef(entries); const [logs, setLogs] = useState([]); entriesRef.current = entries; const addLog = (message: string) => { setLogs((prev) => [...prev, `[${new Date().toLocaleTimeString()}] ${message}`]); }; // addEntries로 추가하든 제거 버튼으로 지우든 변경은 항상 onEntriesChange로 흐르므로, // 추가/삭제 감지를 여기 한 곳에서 처리합니다. const handleEntriesChange = (next: DisplayItemEntry[]) => { const prev = entriesRef.current; const added = next.filter((n) => !prev.some((p) => p.id === n.id)); const removed = prev.filter((p) => !next.some((n) => n.id === p.id)); if (added.length > 0) addLog(`added: ${added.map((a) => a.id).join(", ")}`); if (removed.length > 0) addLog(`removed: ${removed.map((r) => r.id).join(", ")}`); setEntries(next); }; return ( {logs.length === 0 ? ( 아이템을 추가하거나 삭제하면 로그가 표시됩니다. ) : ( logs.map((log, index) => ( {log} )) )} { addEntries(await openMediaPicker()); }} /> ); } ``` ## Managing Item Status \[#managing-item-status] `entries`의 각 항목은 `pending`, `uploading`, `success`, `error`의 status를 가질 수 있습니다. 새로 추가되는 항목의 status 기본값은 의도에 맞게 자유롭게 지정할 수 있습니다(외부 피커가 막 던진 항목이라면 `uploading`, 이미 업로드 완료된 미디어를 hydrate한다면 `success`). 외부 업로드 API와 연동하는 경우, `onTriggerClick`·`onRetry` 콜백으로 함께 전달되는 `updateEntryStatus` 헬퍼를 사용하여 각 항목의 status를 업데이트합니다. ```tsx "use client"; import { VStack } from "@seed-design/react"; import type { DisplayItemEntry, DisplayItemStatusDetails } from "@seed-design/react/primitive"; import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; const defaultEntries: DisplayItemEntry[] = [ { id: "1", thumbnailUrl: "https://picsum.photos/seed/upload1/200/200", status: "uploading", progress: 30, }, { id: "2", thumbnailUrl: "https://picsum.photos/seed/upload2/200/200", status: "success", }, { id: "3", thumbnailUrl: "https://picsum.photos/seed/upload3/200/200", status: "error", }, ]; // 외부 미디어 피커 모킹. 실제 환경에서는 네이티브 브릿지/모달/서버 호출 등으로 교체하세요. async function openMediaPicker(): Promise { const id = crypto.randomUUID(); return [ { id, thumbnailUrl: `https://picsum.photos/seed/${id}/200/200`, status: "uploading", }, ]; } // 실제 환경에서는 네이티브 브릿지 또는 외부 업로드 API와 연동하세요. // status는 컴포넌트가 콜백으로 전달하는 updateEntryStatus 헬퍼로만 갱신합니다. function simulateUpload( id: string, updateEntryStatus: (id: string, details: DisplayItemStatusDetails) => void, ) { updateEntryStatus(id, { status: "uploading", progress: 0 }); let progress = 0; const interval = setInterval(() => { progress += 20; if (progress >= 100) { clearInterval(interval); updateEntryStatus(id, Math.random() > 0.5 ? { status: "success" } : { status: "error" }); } else { updateEntryStatus(id, { status: "uploading", progress }); } }, 300); } export default function AttachmentDisplayStatus() { return ( { const pickedEntries = await openMediaPicker(); addEntries(pickedEntries); for (const entry of pickedEntries) { simulateUpload(entry.id, updateEntryStatus); } }} onRetry={(entry, { updateEntryStatus }) => simulateUpload(entry.id, updateEntryStatus)} /> ); } ``` - `uploading`: [ProgressCircle](/react/components/progress-circle)이 표시됩니다. - `progress`를 설정하여 업로드 진행률을 표시할 수 있습니다. - `progress`를 지정하지 않는 경우 [indeterminate](/react/components/progress-circle#indeterminate) 상태로 표시됩니다. - `error`: 재시도 버튼이 표시됩니다. - 클릭 시 `AttachmentDisplay`에 지정한 `onRetry` 콜백이 `(entry, { updateEntryStatus })` 인자로 실행됩니다. `updateEntryStatus`로 해당 항목을 다시 `uploading` 상태로 되돌려 업로드를 재시도하세요. ## Reordering Entries \[#reordering-entries] `AttachmentDisplayReorderable`을 사용하면 드래그로 항목의 순서를 변경할 수 있습니다. 해당 컴포넌트는 `dnd-kit` 의존성 분리를 위해 별도 snippet [`ui:attachment-display-field-reorderable`](#reorderable)로 제공됩니다. Context를 통해 `reorderEntry`가 제공되므로, 필요한 경우 원하는 드래그 앤 드롭 동작을 직접 구현하거나, 이미 프로젝트에서 사용 중인 드래그 앤 드롭 라이브러리와 연동하여 사용할 수 있습니다. ```tsx "use client"; import type { DisplayItemEntry } from "@seed-design/react/primitive"; import { useState } from "react"; import { AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; import { AttachmentDisplayReorderable } from "seed-design/ui/attachment-display-field-reorderable"; const defaultEntries: DisplayItemEntry[] = [ { id: "1", thumbnailUrl: "https://picsum.photos/seed/reorder1/200/200", status: "success" }, { id: "2", thumbnailUrl: "https://picsum.photos/seed/reorder2/200/200", status: "success" }, { id: "3", thumbnailUrl: "https://picsum.photos/seed/reorder3/200/200", status: "success" }, ]; // 외부 미디어 피커 모킹. 실제 환경에서는 네이티브 브릿지/모달/서버 호출 등으로 교체하세요. async function openMediaPicker(): Promise { const id = crypto.randomUUID(); return [ { id, thumbnailUrl: `https://picsum.photos/seed/${id}/200/200`, status: "success", }, ]; } export default function AttachmentDisplayReorderableExample() { const [entries, setEntries] = useState(defaultEntries); return ( { addEntries(await openMediaPicker()); }} /> ); } ``` ## Examples \[#examples] ### Disabled \[#disabled] `disabled` prop으로 trigger 버튼을 비활성화할 수 있습니다. ```tsx import { VStack } from "@seed-design/react"; import type { DisplayItemEntry } from "@seed-design/react/primitive"; import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; const sampleEntries: DisplayItemEntry[] = [ { id: "1", thumbnailUrl: "https://picsum.photos/seed/disabled1/200/200", status: "success", }, ]; export default function AttachmentDisplayDisabled() { return ( {}} /> ); } ``` ### Read Only \[#read-only] `readOnly` prop으로 읽기 전용 상태를 표현할 수 있습니다. trigger, 파일 제거 버튼, 순서 변경 모두 비활성화됩니다. ```tsx import { VStack } from "@seed-design/react"; import type { DisplayItemEntry } from "@seed-design/react/primitive"; import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; const sampleEntries: DisplayItemEntry[] = [ { id: "1", thumbnailUrl: "https://picsum.photos/seed/readonly1/200/200", status: "success", }, { id: "2", thumbnailUrl: "https://picsum.photos/seed/readonly2/200/200", status: "success", }, ]; export default function AttachmentDisplayReadOnly() { return ( {}} /> ); } ``` ### Controlled \[#controlled] `entries`와 `onEntriesChange`를 사용하여 외부에서 아이템 목록을 제어할 수 있습니다. ```tsx "use client"; import { HStack, Text, VStack } from "@seed-design/react"; import type { DisplayItemEntry } from "@seed-design/react/primitive"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; // 외부 미디어 피커 모킹. 실제 환경에서는 네이티브 브릿지/모달/서버 호출 등으로 교체하세요. async function openMediaPicker(): Promise { const id = crypto.randomUUID(); return [ { id, thumbnailUrl: `https://picsum.photos/seed/${id}/200/200`, status: "success", }, ]; } export default function AttachmentDisplayControlled() { const [entries, setEntries] = useState([]); return ( { addEntries(await openMediaPicker()); }} /> 현재 아이템: {entries.length}개 setEntries([])}> 전체 삭제 ); } ``` ### Custom Inset \[#custom-inset] `--seed-attachment-input-extend-x` CSS 변수를 사용하여 스크롤되는 아이템 목록이 레이아웃 바깥으로 빠져나오도록 구성할 수 있습니다. ```tsx import { vars } from "@seed-design/css/vars"; import { VStack } from "@seed-design/react"; import type { DisplayItemEntry } from "@seed-design/react/primitive"; import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; const defaultEntries: DisplayItemEntry[] = Array.from({ length: 8 }, (_, i) => ({ id: String(i + 1), thumbnailUrl: `https://picsum.photos/seed/inset${i + 1}/200/200`, status: "success", })); export default function AttachmentDisplayCustomInset() { return ( { // 외부 미디어 피커 호출 자리 }} /> ); } ``` ### Field Integration \[#field-integration] `label`, `description`, `errorMessage` 등 Field 관련 prop을 전달할 수 있습니다. ```tsx "use client"; import { VStack } from "@seed-design/react"; import type { DisplayItemEntry } from "@seed-design/react/primitive"; import { useState } from "react"; import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; const defaultEntries: DisplayItemEntry[] = [ { id: "1", thumbnailUrl: "https://picsum.photos/seed/field1/200/200", status: "success" }, ]; // 외부 미디어 피커 모킹. 실제 환경에서는 네이티브 브릿지/모달/서버 호출 등으로 교체하세요. async function openMediaPicker(): Promise { const id = crypto.randomUUID(); return [ { id, thumbnailUrl: `https://picsum.photos/seed/${id}/200/200`, status: "success", }, ]; } export default function AttachmentDisplayFieldExample() { const [entries, setEntries] = useState(defaultEntries); const invalid = entries.length < 1; return ( { addEntries(await openMediaPicker()); }} /> ); } ``` ### Customizing Items \[#customizing-items] Snippet이 제공하는 기본 아이템 구성 외에 추가적인 커스터마이징이 필요한 경우, `@seed-design/react`에서 제공하는 `AttachmentDisplay.ItemBadge` 등의 요소를 활용하여 직접 아이템을 구성할 수 있습니다. 아래 예시에서는 `AttachmentDisplay.ItemBadge`를 사용하여 첫 번째 이미지에 "대표사진" 배지를 표시합니다. ```tsx "use client"; import { IconArrowClockwiseCircularFill, IconXmarkFill } from "@karrotmarket/react-monochrome-icon"; import { AttachmentDisplay as SeedAttachmentDisplay, Icon, VStack } from "@seed-design/react"; import type { DisplayItemEntry } from "@seed-design/react/primitive"; import { AttachmentDisplay, AttachmentDisplayField } from "seed-design/ui/attachment-display-field"; import { ProgressCircle } from "seed-design/ui/progress-circle"; const LABEL_REMOVE = "삭제"; const LABEL_RETRY = "재시도"; function CustomImageItem({ entry, isCover, onRetry, }: { entry: DisplayItemEntry; isCover?: boolean; onRetry?: () => void; }) { return ( {isCover && 대표사진} {(e) => ( )} {onRetry && ( } /> {LABEL_RETRY} )} } /> ); } const defaultEntries: DisplayItemEntry[] = [ { id: "1", thumbnailUrl: "https://picsum.photos/seed/customizing1/200/200", status: "success" }, { id: "2", thumbnailUrl: "https://picsum.photos/seed/customizing2/200/200", status: "success" }, { id: "3", thumbnailUrl: "https://picsum.photos/seed/customizing3/200/200", status: "success" }, ]; export default function AttachmentDisplayCustomizingItems() { return ( { // 외부 미디어 피커 호출 자리 }} > {({ entries }) => entries.map((entry, index) => ( )) } ); } ``` ## Attachment Display Field vs. Attachment Field \[#attachment-display-field-vs-attachment-field] HTML ``을 사용해야 하는 경우 `AttachmentField`를, 외부 소스와 연동하여 URL 기반으로 미디어를 표시해야 하는 경우 `AttachmentDisplayField`를 사용하세요. | | Attachment Field | Attachment Display | | -------------- | -------------------------- | ---------------------------- | | 미디어 소스 | HTML `` | 이미지 URL | | 데이터 모델 | `File` 기반 `FileEntry` | URL 기반 `DisplayItemEntry` | | 파일 선택 | `` | 다루지 않음 (`onTriggerClick` 위임) | | 드래그 앤 드롭으로 업로드 | `AttachmentDropzone` | 다루지 않음 | | 파일 검증 | accept, maxFileSize 등 | 다루지 않음 | | Form 연동 | `` 동기화 | 다루지 않음 | --- file: components/attachment-field.mdx # Attachment Field 파일을 선택하거나 드래그 앤 드롭으로 업로드할 수 있는 컴포넌트입니다. ## Preview ```tsx import { VStack } from "@seed-design/react"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; export default function AttachmentFieldPreview() { return ( ); } ``` ## Installation \[#installation] ### Default \[#default] 파일 순서 변경이 불필요한 경우 사용할 수 있는 컴포넌트를 포함합니다. - npm: npx @seed-design/cli@latest add ui:attachment-field - pnpm: pnpm dlx @seed-design/cli@latest add ui:attachment-field - yarn: yarn dlx @seed-design/cli@latest add ui:attachment-field - bun: bun x @seed-design/cli@latest add ui:attachment-field ### Reorderable \[#reorderable] 드래그 앤 드롭을 통한 파일 순서 변경이 필요한 경우 활용할 수 있는 컴포넌트를 포함합니다. 프로젝트에 [dnd-kit](https://dndkit.com/overview) 의존성이 추가됩니다. - npm: npx @seed-design/cli@latest add ui:attachment-field-reorderable - pnpm: pnpm dlx @seed-design/cli@latest add ui:attachment-field-reorderable - yarn: yarn dlx @seed-design/cli@latest add ui:attachment-field-reorderable - bun: bun x @seed-design/cli@latest add ui:attachment-field-reorderable ## Props \[#props] ### `AttachmentField` \[#attachmentfield] - `label` - type: `React.ReactNode` - `labelWeight` - type: `"bold" | "medium" | undefined` - default: `"medium"` - `indicator` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `errorMessage` - type: `React.ReactNode` - `showRequiredIndicator` - type: `boolean | undefined` - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `fieldRef` - type: `React.Ref | undefined` - `rootProps` - type: `React.HTMLAttributes | undefined` - `disabled` - type: `boolean | undefined` - default: `false` - `name` - type: `string | undefined` - `invalid` - type: `boolean | undefined` - default: `false` - `readOnly` - type: `boolean | undefined` - default: `false` - `accept` - type: `string | string[] | undefined` - description: Accepted file types (e.g., "image/\*", ".pdf", "image/png") - `required` - type: `boolean | undefined` - default: `false` - `maxFiles` - type: `number | undefined` - default: `1` - `maxFileSize` - type: `number | undefined` - default: `Infinity` - `minFileSize` - type: `number | undefined` - default: `0` - `validate` - type: `((file: File) => FileError[] | null) | undefined` - description: Custom validation function. - `preventDocumentDrop` - type: `boolean | undefined` - default: `true` - description: Whether to prevent the browser's default behavior when files are dropped outside the dropzone (e.g., opening the file in a new tab). - `acceptedFileEntries` - type: `FileEntry[] | undefined` - `defaultAcceptedFileEntries` - type: `FileEntry[] | undefined` - `onAcceptedFileEntriesChange` - type: `((fileEntries: FileEntry[]) => void) | undefined` - `onFileReject` - type: `((rejections: FileRejection[]) => void) | undefined` - `onFileAccept` - type: `((acceptedEntries: FileEntry[], helpers: { updateFileEntryStatus: (id: string, details: FileStatusDetails) => void; }) => void) | undefined` ### `AttachmentInput` \[#attachmentinput] - `children` - type: `((context: UseFileUploadContext) => React.ReactNode) | undefined` - `onRetry` - type: `((fileEntry: FileEntry, helpers: Pick) => void) | undefined` ### `AttachmentDropzone` \[#attachmentdropzone] - `children` - type: `((context: UseFileUploadContext) => React.ReactNode) | undefined` - `onRetry` - type: `((fileEntry: FileEntry, helpers: Pick) => void) | undefined` ### `AttachmentInputItem` \[#attachmentinputitem] - `onRetry` - type: `(() => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `type` - type: `"general" | "image" | undefined` - default: `"general"` - `fileEntry` - type: `FileEntry` - required: `true` ## Usage \[#usage] ### 기본 사용법 \[#기본-사용법] `AttachmentField` 안에 `AttachmentInput`, `AttachmentDropzone`, `AttachmentInputReorderable`, `AttachmentDropzoneReorderable` 중 하나를 조합하여 사용합니다. ```tsx import { AttachmentField, AttachmentInput, AttachmentDropzone, } from "seed-design/ui/attachment-field"; ; ; ``` ```tsx import { AttachmentField } from "seed-design/ui/attachment-field"; import { AttachmentInputReorderable, AttachmentDropzoneReorderable, } from "seed-design/ui/attachment-field-reorderable"; ; ; ``` ### Item 직접 구성하기 \[#item-직접-구성하기] `AttachmentInput`, `AttachmentDropzone`, `AttachmentInputReorderable`, `AttachmentDropzoneReorderable`은 `children`을 render prop으로 사용합니다. `acceptedFileEntries`을 활용하여 `AttachmentInputItem`을 직접 렌더링할 수 있습니다. 이때 `FileEntry` 타입이 제공하는 `id`를 `key`로 활용하는 것을 권장합니다. `children`을 제공하지 않는 경우 자동으로 `acceptedFileEntries`에 등록된 파일을 `AttachmentInputItem`으로 렌더링합니다. ```tsx import { AttachmentField, AttachmentInput, AttachmentInputItem, } from "seed-design/ui/attachment-field"; {({ acceptedFileEntries }) => acceptedFileEntries.map((entry) => ( )) } ; ``` ## Uploading Files \[#uploading-files] ### Trigger \[#trigger] `AttachmentInput`를 사용하면 trigger(업로드 버튼)가 포함된 레이아웃을 사용할 수 있습니다. ```tsx import type { FileEntry } from "@seed-design/react/primitive"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; const defaultAcceptedFileEntries: FileEntry[] = [ { id: "1", file: new File(["hello"], "document.pdf", { type: "application/pdf" }), status: "success", }, ]; export default function AttachmentFieldTriggerExample() { return ( ); } ``` ### Dropzone \[#dropzone] `AttachmentDropzone`을 사용하면 드래그 앤 드롭 영역이 포함된 레이아웃을 사용할 수 있습니다. ```tsx import { VStack } from "@seed-design/react"; import type { FileEntry } from "@seed-design/react/primitive"; import { AttachmentField, AttachmentDropzone } from "seed-design/ui/attachment-field"; const defaultAcceptedFileEntries: FileEntry[] = [ { id: "1", file: new File(["hello"], "document.pdf", { type: "application/pdf" }), status: "success", }, ]; export default function AttachmentFieldDropzone() { return ( ); } ``` ### Listening to Accepted File Changes \[#listening-to-accepted-file-changes] `acceptedFileEntries`는 유효성 검사를 마친 파일의 목록입니다. `onAcceptedFileEntriesChange` 콜백으로 `acceptedFileEntries`에 등록된 파일 변경 이벤트를 감지할 수 있습니다. ```tsx import { VStack, Text } from "@seed-design/react"; import { useState } from "react"; import type { FileStatusDetails } from "@seed-design/react/primitive"; import { AttachmentField, AttachmentInput, AttachmentInputItem, } from "seed-design/ui/attachment-field"; function simulateUpload( _file: File, id: string, updateFileEntryStatus: (id: string, details: FileStatusDetails) => void, ) { updateFileEntryStatus(id, { status: "uploading", progress: 0 }); let progress = 0; const interval = setInterval(() => { progress += 25; if (progress >= 100) { clearInterval(interval); updateFileEntryStatus(id, { status: "success" }); } else { updateFileEntryStatus(id, { status: "uploading", progress }); } }, 500); } export default function AttachmentFieldValueChanges() { const [logs, setLogs] = useState([]); const addLog = (message: string) => { setLogs((prev) => [...prev, `[${new Date().toLocaleTimeString()}] ${message}`]); }; return ( {logs.length === 0 ? ( 파일을 추가하거나 삭제하면 로그가 표시됩니다. ) : ( logs.map((log, index) => ( {log} )) )} { addLog(`onFileAccept: ${entries.map((e) => e.file.name).join(", ")}`); for (const entry of entries) { simulateUpload(entry.file, entry.id, updateFileEntryStatus); } }} onAcceptedFileEntriesChange={(files) => { addLog( `onAcceptedFileEntriesChange: ${files.map((f) => `${f.file.name} (${f.status})`).join(", ")}`, ); }} onFileReject={(files) => { addLog( `onFileReject: ${files.map((f) => `${f.file.name} (${f.errors.join(", ")})`).join(", ")}`, ); }} > {({ acceptedFileEntries }) => acceptedFileEntries.map((fileEntry) => ( )) } ); } ``` ## Validating Files \[#validating-files] 사용자가 선택한 파일이 `acceptedFileEntries`에 등록되기 전 유효성을 확인할 수 있습니다. 유효하지 않은 파일에 대해 각각 `onFileReject` 콜백이 실행됩니다. 해당 콜백에서 파일과 에러 코드들을 확인하여 에러 메시지를 표시할 수 있습니다. ### Max Files \[#max-files] `maxFiles`로 업로드 가능한 최대 파일 수를 제한할 수 있습니다. 기본값은 `1`입니다. 최대 수에 도달한 경우 trigger 및 dropzone이 비활성화됩니다. 업로드 가능한 파일의 수보다 많은 파일을 선택한 경우 업로드 가능한 파일까지 `acceptedFileEntries`에 등록됩니다. 나머지 파일에 대해서는 각각 `onFileReject` 콜백이 실행됩니다. 이때 reject된 파일의 에러는 `"TOO_MANY_FILES"`입니다. ```tsx import { VStack } from "@seed-design/react"; import type { FileEntry } from "@seed-design/react/primitive"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; const defaultAcceptedFileEntries: FileEntry[] = [ { id: "1", file: new File(["hello"], "document.pdf", { type: "application/pdf" }), status: "success", }, ]; export default function AttachmentFieldMaxFiles() { return ( ); } ``` ### Invalid File Type \[#invalid-file-type] `accept`로 업로드 가능한 파일의 종류를 제한할 수 있습니다. MIME type(`image/png`, `image/*`) 또는 확장자(`.png`, `.jpg, .jpeg`) 형식을 지정할 수 있으며, `string[]`을 전달하는 경우 각 `string` `,`로 join합니다. ``에 등록되는 `accept` 속성을 통해 사용자가 선택할 수 있는 파일의 종류를 제한하는 것은 브라우저 UI에서만 동작하는 편의 기능입니다. 따라서 사용자는 브라우저 파일 선택 다이얼로그의 `모든 파일 보기`와 같은 기능을 통해 제한된 종류의 파일도 선택할 수 있습니다. 이렇게 선택된 파일의 경우 `"INVALID_TYPE"` 에러와 함께 `onFileReject` 콜백이 실행됩니다. 파일 종류 검증이 필요한 경우 해당 검증은 서버에서도 수행되어야 합니다. ```tsx import { useState } from "react"; import { VStack } from "@seed-design/react"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; function getErrorMessage(errorCode: string): string { switch (errorCode) { case "INVALID_TYPE": return "지원하지 않는 파일 형식입니다"; default: return "업로드에 실패했습니다"; } } export default function AttachmentFieldInvalidFileType() { const [errorMessage, setErrorMessage] = useState(); return ( setErrorMessage(undefined)} onFileReject={(files) => { const messages = files.map( ({ file, errors }) => `"${file.name}": ${errors.map(getErrorMessage).join(", ")}`, ); setErrorMessage(messages.join("\n")); }} > ); } ``` ### File Size \[#file-size] `minFileSize`, `maxFileSize` prop을 활용할 수 있습니다. - 사용자가 선택한 파일이 `minFileSize`보다 작은 경우 `"FILE_TOO_SMALL"` 에러와 함께 `onFileReject` 콜백이 실행됩니다. - 사용자가 선택한 파일이 `maxFileSize`보다 큰 경우 `"FILE_TOO_LARGE"` 에러와 함께 `onFileReject` 콜백이 실행됩니다. ```tsx import { useState } from "react"; import { VStack } from "@seed-design/react"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; import { formatBytes } from "seed-design/lib/format-bytes"; const MIN_FILE_SIZE = 1 * 1024; // 1KB const MAX_FILE_SIZE = 10 * 1024; // 10KB function getErrorMessage(errorCode: string): string { switch (errorCode) { case "FILE_TOO_LARGE": return `크기가 ${formatBytes(MAX_FILE_SIZE)}를 초과합니다`; case "FILE_TOO_SMALL": return `크기가 ${formatBytes(MIN_FILE_SIZE)} 미만입니다`; case "TOO_MANY_FILES": return "업로드 가능한 파일 개수를 초과했습니다"; default: return "업로드에 실패했습니다"; } } export default function AttachmentFieldValidation() { const [errorMessage, setErrorMessage] = useState(); return ( setErrorMessage(undefined)} onFileReject={(files) => { const messages = files.map( ({ file, errors }) => `"${file.name}": ${errors.map(getErrorMessage).join(", ")}`, ); setErrorMessage(messages.join("\n")); }} > ); } ``` ### Custom Validation \[#custom-validation] `validate` prop으로 각 파일에 대한 유효성 검사를 직접 추가할 수 있습니다. 커스텀 에러 코드를 반환하여 `onFileReject`에서 에러 종류별로 메시지를 분기할 수 있습니다. ```tsx import { useState } from "react"; import { VStack } from "@seed-design/react"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; function validateFileName(file: File) { const nameWithoutExt = file.name.replace(/\.[^.]+$/, ""); if (nameWithoutExt.length > 8) { return ["FILENAME_TOO_LONG"]; } return null; } export default function AttachmentFieldCustomValidation() { const [errorMessage, setErrorMessage] = useState(); return ( setErrorMessage(undefined)} onFileReject={(files) => { if (files.every((f) => f.errors.includes("FILENAME_TOO_LONG")) === false) { return; } const names = files.map((f) => f.file.name).join(", "); setErrorMessage(`"${names}"은(는) 파일 이름이 8자를 초과합니다.`); }} > ); } ``` ## Managing File Status \[#managing-file-status] `acceptedFileEntries`의 각 항목은 `pending`, `uploading`, `success`, `error`의 status를 가질 수 있습니다. 새로 추가되는 항목의 status 기본값은 `pending`입니다. 파일 선택 직후 외부 업로드 API와 연동하는 경우, 사용자에게 각 파일 항목의 업로딩 상태를 보여줄 수 있습니다. `onFileAccept` 콜백에서 새로 추가된 파일을 받고, 함께 제공되는 `updateFileEntryStatus` 헬퍼를 사용하여 항목의 status를 업데이트합니다. ```tsx import { useCallback } from "react"; import { VStack } from "@seed-design/react"; import type { FileStatusDetails } from "@seed-design/react/primitive"; import { AttachmentField, AttachmentInput, AttachmentInputItem, } from "seed-design/ui/attachment-field"; // 실제 환경에서는 fetch 등으로 교체하세요. async function uploadFile( file: File, onProgress: (progress: number) => void, ): Promise<{ url: string }> { const totalChunks = 5; for (let i = 1; i <= totalChunks; i++) { await new Promise((r) => setTimeout(r, 200 + Math.random() * 300)); onProgress(Math.round((i / totalChunks) * 100)); } if (Math.random() > 0.5) { throw new Error("Network error"); } return { url: `https://example.com/uploads/${file.name}` }; } export default function AttachmentFieldStatus() { const startUpload = useCallback( ( file: File, id: string, updateFileEntryStatus: (id: string, details: FileStatusDetails) => void, ) => { updateFileEntryStatus(id, { status: "uploading", progress: 0 }); uploadFile(file, (progress) => { updateFileEntryStatus(id, { status: "uploading", progress }); }) .then(() => updateFileEntryStatus(id, { status: "success" })) .catch(() => updateFileEntryStatus(id, { status: "error" })); }, [], ); return ( { for (const entry of entries) { startUpload(entry.file, entry.id, updateFileEntryStatus); } }} > {({ acceptedFileEntries, updateFileEntryStatus }) => acceptedFileEntries.map((fileEntry) => ( startUpload(fileEntry.file, fileEntry.id, updateFileEntryStatus)} /> )) } ); } ``` - `uploading`: [ProgressCircle](/react/components/progress-circle)이 표시됩니다. - `progress`를 설정하여 업로드 진행률을 표시할 수 있습니다. - `progress`를 지정하지 않는 경우 [indeterminate](/react/components/progress-circle#indeterminate) 상태로 표시됩니다. - `error`: 재시도 버튼이 표시됩니다. - 클릭 시 `AttachmentInputItem`에 지정한 `onRetry` 콜백이 실행됩니다. 유효성 검증에 성공한 파일 항목은 status와 관계없이 `acceptedFileEntries`에 유지되므로, 네이티브 form 제출 시 포함됩니다. ## Reordering Files \[#reordering-files] `AttachmentInputReorderable` 또는 `AttachmentDropzoneReorderable`을 사용하면 드래그로 파일의 순서를 변경할 수 있습니다. 두 컴포넌트는 `dnd-kit` 의존성 분리를 위해 별도 snippet [`ui:attachment-field-reorderable`](#reorderable)로 제공됩니다. Context를 통해 `reorderFileEntry`가 제공되므로, 필요한 경우 원하는 드래그 앤 드롭 동작을 직접 구현하거나, 이미 프로젝트에서 사용 중인 드래그 앤 드롭 라이브러리와 연동하여 사용할 수 있습니다. ```tsx import type { FileEntry } from "@seed-design/react/primitive"; import { AttachmentField } from "seed-design/ui/attachment-field"; import { AttachmentInputReorderable } from "seed-design/ui/attachment-field-reorderable"; function createMockImageFile(name: string, base64: string): File { const bytes = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)); return new File([bytes], name, { type: "image/png" }); } const defaultAcceptedFileEntries: FileEntry[] = [ { id: "1", file: createMockImageFile( "sunset-landscape.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGN4FcEDAAN+AU+hW/ICAAAAAElFTkSuQmCC", ), status: "success", }, { id: "2", file: createMockImageFile( "city-night.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGOYbPwKAAMNAbHKe2UaAAAAAElFTkSuQmCC", ), status: "success", }, { id: "3", file: createMockImageFile( "morning-coffee.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGN4tZkDAAQwAaYlKXDxAAAAAElFTkSuQmCC", ), status: "success", }, ]; export default function AttachmentFieldReorderableExample() { return ( ); } ``` ## Examples \[#examples] ### Showing Thumbnails \[#showing-thumbnails] 파일 이름 및 크기 대신 이미지 미리보기를 표시하려면 `accept`를 `"image/*"`, `["image/png", "image/jpeg"]` 등으로 설정하여 사용자가 이미지 파일만 선택할 수 있도록 제한합니다. ```tsx import { VStack } from "@seed-design/react"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; export default function AttachmentFieldAcceptImage() { return ( ); } ``` [`"image/heic"`](https://caniuse.com/heif) 등 일부 이미지 형식은 브라우저에 따라 이미지 미리보기가 표시되지 않을 수 있습니다. ### Disabled \[#disabled] `disabled` prop으로 trigger 및 dropzone을 비활성화하여 신규 파일 선택을 차단하고, ``을 `disabled` 처리하여 폼 제출 시 값이 전송되지 않도록 합니다. ```tsx import { VStack } from "@seed-design/react"; import type { FileEntry } from "@seed-design/react/primitive"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; const defaultFiles: FileEntry[] = [ { id: "mock-1", file: new File(["hello"], "document.pdf", { type: "application/pdf" }), status: "success", }, { id: "mock-2", file: new File(["world"], "image.png", { type: "image/png" }), status: "success", }, ]; export default function AttachmentFieldDisabled() { return ( ); } ``` ### Read Only \[#read-only] `readOnly` prop으로 첨부된 파일을 읽기 전용 상태로 표시할 수 있습니다. trigger, dropzone, 파일 제거 버튼, 순서 변경 모두 비활성화되지만 ``의 값은 유지되어 form 제출 시 함께 전송됩니다. ```tsx import { VStack } from "@seed-design/react"; import type { FileEntry } from "@seed-design/react/primitive"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; const defaultFiles: FileEntry[] = [ { id: "mock-1", file: new File(["hello"], "document.pdf", { type: "application/pdf" }), status: "success", }, { id: "mock-2", file: new File(["world"], "image.png", { type: "image/png" }), status: "success", }, ]; export default function AttachmentFieldReadOnly() { return ( ); } ``` ### Controlled \[#controlled] `acceptedFileEntries`와 `onAcceptedFileEntriesChange`를 사용하여 외부에서 파일 목록을 제어할 수 있습니다. ```tsx import { VStack, HStack, Text } from "@seed-design/react"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import type { FileEntry } from "@seed-design/react/primitive"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; export default function AttachmentFieldControlled() { const [acceptedFileEntries, setAcceptedFileEntries] = useState([]); return ( 현재 파일: {JSON.stringify(acceptedFileEntries.map((f) => f.file.name))} setAcceptedFileEntries([])} > 전체 삭제 ); } ``` ### Custom Inset \[#custom-inset] `--seed-attachment-input-extend-x` CSS 변수를 사용하여 스크롤되는 아이템 목록이 레이아웃 바깥으로 빠져나오도록 구성할 수 있습니다. ```tsx import { VStack } from "@seed-design/react"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; import { vars } from "@seed-design/css/vars"; const mockedFiles = Array.from({ length: 8 }, (_, i) => { const file = new File(["file content"], `file${i + 1}.txt`, { type: "text/plain" }); Object.defineProperty(file, "size", { value: 1 }); return file; }); export default function AttachmentFieldCustomInset() { return ( ({ id: `${index}`, file, status: "pending", }))} rootProps={{ style: { "--seed-attachment-input-extend-x": vars.$dimension.spacingX.globalGutter, } as React.CSSProperties, }} > ); } ``` ### Field Integration \[#field-integration] `label`, `description`, `errorMessage` 등의 Field 관련 prop을 사용할 수 있습니다. ```tsx import { Divider, VStack } from "@seed-design/react"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; export default function AttachmentFieldField() { return ( ); } ``` ### Form (Uncontrolled) \[#form-uncontrolled] `acceptedFileEntries`에 등록된 파일이 ``의 `files`로 동기화되므로 `
`과 `name` prop을 사용하여 formData에 포함시킬 수 있습니다. ```tsx import { VStack } from "@seed-design/react"; import { useState, type FormEvent } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; type FieldErrors = { files?: string; }; export default function AttachmentFieldForm() { const [fieldErrors, setFieldErrors] = useState({}); const handleSubmit = (event: FormEvent) => { event.preventDefault(); const formData = new FormData(event.currentTarget); const files = formData.getAll("files") as File[]; if (files.length === 0) { setFieldErrors({ files: "최소 1개의 파일을 업로드해주세요" }); return; } window.alert(`제출된 파일: ${files.map((f) => f.name).join(", ")}`); }; return ( { if (files.length > 0) { setFieldErrors({}); } }} {...(fieldErrors.files && { invalid: true, errorMessage: fieldErrors.files })} > 제출 ); } ``` ### React Hook Form \[#react-hook-form] ```tsx import { HStack, VStack } from "@seed-design/react"; import { useCallback, type FormEvent } from "react"; import { useController, useForm } from "react-hook-form"; import { ActionButton } from "seed-design/ui/action-button"; import type { FileEntry } from "@seed-design/react/primitive"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; interface FormValues { files: FileEntry[]; } export default function AttachmentFieldReactHookForm() { const { handleSubmit, reset, control } = useForm({ reValidateMode: "onSubmit", defaultValues: { files: [], }, }); const { field: { value, onChange, ...field }, fieldState, } = useController({ name: "files", control, rules: { validate: (value) => value.length > 0 || "최소 1개의 파일을 업로드해주세요", }, }); const onValid = useCallback( (data: FormValues) => window.alert(`제출된 파일: ${data.files.map((f) => f.file.name).join(", ")}`), [], ); const onReset = useCallback( (event: FormEvent) => { event.preventDefault(); reset(); }, [reset], ); return ( 초기화 제출 ); } ``` ### Customizing Items \[#customizing-items] Snippet이 제공하는 기본 아이템 구성 외에 추가적인 커스터마이징이 필요한 경우, `@seed-design/react`에서 제공하는 `AttachmentInput.ItemBadge` 등의 요소를 활용하여 직접 아이템을 구성할 수 있습니다. 아래 예시에서는 `AttachmentInput.ItemBadge`를 사용하여 첫 번째 이미지에 "대표사진" 배지를 표시합니다. ```tsx "use client"; import { AttachmentInput as SeedAttachmentInput, Icon, VStack } from "@seed-design/react"; import type { FileEntry } from "@seed-design/react/primitive"; import { IconArrowClockwiseCircularFill, IconXmarkFill } from "@karrotmarket/react-monochrome-icon"; import { AttachmentField, AttachmentInput } from "seed-design/ui/attachment-field"; import { ProgressCircle } from "seed-design/ui/progress-circle"; const LABEL_REMOVE_FILE = "파일 제거"; const LABEL_RETRY = "재시도"; function CustomImageItem({ fileEntry, isCover, onRetry, }: { fileEntry: FileEntry; isCover?: boolean; onRetry?: () => void; }) { return ( {isCover && 대표사진} {(entry) => ( )} {onRetry && ( } /> {LABEL_RETRY} )} } /> ); } export default function AttachmentFieldCustomizingItems() { return ( {({ acceptedFileEntries }) => acceptedFileEntries.map((fileEntry, index) => ( )) } ); } ``` --- file: components/avatar.mdx # Avatar 사용자의 프로필 이미지를 표시하는 컴포넌트입니다. ## Preview ```tsx import { Box, Flex } from "@seed-design/react"; import { Avatar, AvatarBadge } from "seed-design/ui/avatar"; import { IdentityPlaceholder } from "seed-design/ui/identity-placeholder"; export default function AvatarPreview() { return ( } > } /> ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:avatar - pnpm: pnpm dlx @seed-design/cli@latest add ui:avatar - yarn: yarn dlx @seed-design/cli@latest add ui:avatar - bun: bun x @seed-design/cli@latest add ui:avatar ## Props \[#props] ### `Avatar` \[#avatar] - `src` - type: `string | undefined` - `alt` - type: `string | undefined` - `fallback` - type: `React.ReactNode` - `size` - type: `"20" | "24" | "36" | "42" | "48" | "56" | "64" | "80" | "96" | "108" | undefined` - default: `48` - description: - \`20\`: 대표 사용처: 댓글을 남긴 사용자 - \`24\`: 대표 사용처: 답글 프로필 - \`36\`: 대표 사용처: 댓글 프로필 - \`42\`: 대표 사용처: 게시글 상세 내 프로필 - \`48\`: 대표 사용처: 작은 리스트 - \`56\`: 대표 사용처: 큰 리스트 - \`64\`: 대표 사용처: 프로필 상세, 캐러셀 - \`108\`: 대표 사용처: 프로필 수정 - `badgeMask` - type: `"none" | "circle" | "flower" | "shield" | undefined` - default: `"none"` - `onLoadingStatusChange` - type: `((status: ImageLoadingStatus) => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `AvatarBadge` \[#avatarbadge] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `AvatarStack` \[#avatarstack] - `size` - type: `"20" | "24" | "36" | "42" | "48" | "56" | "64" | "80" | "96" | "108" | undefined` - default: `48` ## Examples \[#examples] ### Size \[#size] ```tsx import { Flex } from "@seed-design/react"; import { Avatar } from "seed-design/ui/avatar"; export default function AvatarSize() { return ( ); } ``` ### Badge & Badge Mask \[#badge--badge-mask] ```tsx import { IdentityPlaceholder } from "seed-design/ui/identity-placeholder"; import { Avatar, AvatarBadge } from "seed-design/ui/avatar"; import { Box, HStack } from "@seed-design/react"; export default function () { return ( } > } > 뱃지를 설명하는 대체 텍스트를 제공해야 합니다. } > 뱃지를 설명하는 대체 텍스트를 제공해야 합니다. ); } ``` ### Stack \[#stack] ```tsx import { Avatar, AvatarStack } from "seed-design/ui/avatar"; import { IdentityPlaceholder } from "seed-design/ui/identity-placeholder"; export default function AvatarStackExample() { return ( } /> } /> } /> } /> ); } ``` ### Fallback Image \[#fallback-image] `fallback` prop으로 이미지가 로드되지 않았을 때 보여질 요소를 지정할 수 있습니다. 주로 [IdentityPlaceholder](/react/components/identity-placeholder)를 사용합니다. ```tsx import { Flex } from "@seed-design/react"; import { Avatar } from "seed-design/ui/avatar"; import { IdentityPlaceholder } from "seed-design/ui/identity-placeholder"; export default function AvatarFallbackExample() { return ( } /> } /> ); } ``` --- file: components/badge.mdx # Badge 객체의 속성이나 상태를 시각적으로 표현하는 작은 텍스트 라벨입니다. 사용자의 주의를 끌고 콘텐츠의 빠른 인지와 탐색을 돕기 위해 사용됩니다. ## Preview ```tsx import { Badge } from "@seed-design/react"; export default function BadgePreview() { return 라벨; } ``` ## Usage \[#usage] ```tsx import { Badge } from "@seed-design/react"; ``` ```tsx Badge ``` ## Props \[#props] - `size` - type: `"medium" | "large" | undefined` - default: `"medium"` - `variant` - type: `"solid" | "weak" | "outline" | undefined` - default: `"solid"` - description: - \`weak\`: 반복적인 구조를 가진 환경에서 사용합니다. 배경색이 있는 경우에는 권장하지 않습니다. - \`solid\`: 배경이 복잡하거나 이미지 위에 Badge가 겹치는 경우 사용합니다. - \`outline\`: 중간 정도의 주목도가 필요한 본문 또는 상세 화면에서 사용합니다. - `tone` - type: `"brand" | "critical" | "informative" | "neutral" | "positive" | "warning" | undefined` - default: `"neutral"` - description: - \`neutral\`: 상태가 특별히 없거나, 상태값이 명확하지 않은 초기 상태 - \`informative\`: 베타 기능 안내, 사용자 권한 제한, 정보 기반 메시지 - \`positive\`: 완료, 적용됨, 승인됨, 발행됨, 저장 성공, 검토 통과 - \`warning\`: 만료 임박, 제출 누락, 필수 정보 부족 등 잠재적 문제 상태 - \`critical\`: 검수 거절, 제재 상태, 편집 불가, 유효성 검증 실패 - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Truncating Behavior \[#truncating-behavior] Badge는 긴 텍스트를 잘라내고 생략 부호를 표시합니다. ```tsx import { Badge, VStack } from "@seed-design/react"; export default function BadgeTruncating() { return ( In velit velit deserunt amet veniam incididunt consectetur incididunt Lorem. In velit velit deserunt amet veniam incididunt consectetur incididunt Lorem. ); } ``` ### Tones and Variants \[#tones-and-variants] #### Neutral \[#neutral] ```tsx import { Badge, HStack, VStack } from "@seed-design/react"; export default function BadgeNeutral() { return ( 라벨 라벨 라벨 라벨 라벨 라벨 ); } ``` #### Brand \[#brand] ```tsx import { Badge, HStack, VStack } from "@seed-design/react"; export default function BadgeBrand() { return ( 라벨 라벨 라벨 라벨 라벨 라벨 ); } ``` #### Informative \[#informative] ```tsx import { Badge, HStack, VStack } from "@seed-design/react"; export default function BadgeInformative() { return ( 라벨 라벨 라벨 라벨 라벨 라벨 ); } ``` #### Positive \[#positive] ```tsx import { Badge, HStack, VStack } from "@seed-design/react"; export default function BadgePositive() { return ( 라벨 라벨 라벨 라벨 라벨 라벨 ); } ``` #### Warning \[#warning] ```tsx import { Badge, HStack, VStack } from "@seed-design/react"; export default function BadgeWarning() { return ( 라벨 라벨 라벨 라벨 라벨 라벨 ); } ``` #### Critical \[#critical] ```tsx import { Badge, HStack, VStack } from "@seed-design/react"; export default function BadgeCritical() { return ( 라벨 라벨 라벨 라벨 라벨 라벨 ); } ``` --- file: components/bottom-sheet.mdx # Bottom Sheet 화면 하단에서 올라오는 모달 컴포넌트입니다. 추가 정보나 액션 목록을 제공하면서도 현재 컨텍스트를 유지할 때 사용됩니다. ## Preview ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetPreview = () => { return ( Open {/* If you need to omit padding, pass px={0}. */} Content 확인 ); }; export default BottomSheetPreview; ``` Stackflow와 Bottom Sheet를 함께 사용하는 방법에 대해 알아보세요. ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:bottom-sheet - pnpm: pnpm dlx @seed-design/cli@latest add ui:bottom-sheet - yarn: yarn dlx @seed-design/cli@latest add ui:bottom-sheet - bun: bun x @seed-design/cli@latest add ui:bottom-sheet ## Props \[#props] ### `BottomSheetRoot` \[#bottomsheetroot] - `headerAlign` - type: `"left" | "center" | undefined` - default: `"left"` - `skipAnimation` - type: `boolean | undefined` - default: `false` - `children` - type: `React.ReactNode` - `autoFocus` - type: `boolean | undefined` - `onDrag` - type: `((event: React.PointerEvent, percentageDragged: number) => void) | undefined` - `onAnimationEnd` - type: `((open: boolean) => void) | undefined` - description: Gets triggered after the open or close animation ends, it receives an \`open\` argument with the \`open\` state of the drawer by the time the function was triggered. Useful to revert any state changes for example. - `fixed` - type: `boolean | undefined` - description: When \`true\`, don't move the drawer upwards if there's space, but rather only change it's height so it's fully scrollable when the keyboard is open - `activeSnapPoint` - type: `string | number | null | undefined` - `setActiveSnapPoint` - type: `((snapPoint: number | string | null) => void) | undefined` - `open` - type: `boolean | undefined` - `closeThreshold` - type: `number | undefined` - default: `0.25` - description: Number between 0 and 1 that determines when the drawer should be closed. Example: threshold of 0.5 would close the drawer if the user swiped for 50% of the height of the drawer or more. - `onOpenChange` - type: `((open: boolean, details?: DrawerChangeDetails) => void) | undefined` - `scrollLockTimeout` - type: `number | undefined` - default: `500ms` - description: Duration for which the drawer is not draggable after scrolling content inside of the drawer. - `handleOnly` - type: `boolean | undefined` - default: `false` - description: When \`true\` only allows the drawer to be dragged by the \`\\` component. - `dismissible` - type: `boolean | undefined` - default: `true` - description: When \`false\` dragging, clicking outside, pressing esc, etc. will not close the drawer. Use this in combination with the \`open\` prop, otherwise you won't be able to open/close the drawer. - `onRelease` - type: `((event: React.PointerEvent, open: boolean) => void) | undefined` - `modal` - type: `boolean | undefined` - default: `true` - description: When \`false\` it allows to interact with elements outside of the drawer without closing it. - `nested` - type: `boolean | undefined` - `onClose` - type: `(() => void) | undefined` - `defaultOpen` - type: `boolean | undefined` - default: `false` - description: Opened by default. Still reacts to \`open\` state changes - `repositionInputs` - type: `boolean | undefined` - default: `true when {@link snapPoints} is defined` - description: When \`true\` Vaul will reposition inputs rather than scroll then into view if the keyboard is in the way. Setting it to \`false\` will fall back to the default browser behavior. - `snapToSequentialPoint` - type: `boolean | undefined` - default: `false` - description: Disabled velocity based swiping for snap points. This means that a snap point won't be skipped even if the velocity is high enough. Useful if each snap point in a drawer is equally important. - `container` - type: `HTMLElement | null | undefined` - `snapPoints` - type: `(string | number)[] | undefined` - default: `undefined` - description: Array of snap points to use. Example: snapPoints=\{\["100px", "200px", 1]} will use the snap points 100px, 200px and fully open (1 = 100% of the container). - `fadeFromIndex` - type: `number | undefined` - default: `snapPoints.length - 1` - description: Index of the snap point to start fading from. Example: fadeFromIndex=\{0} will start fading from the first snap point. - `closeOnInteractOutside` - type: `boolean | undefined` - default: `true` - description: Whether to close the drawer when interacting outside of the drawer. - `closeOnEscape` - type: `boolean | undefined` - default: `true` - description: Whether to close the drawer when pressing the escape key. - `lazyMount` - type: `boolean | undefined` - default: `false` - description: Whether to lazy mount the drawer content on first open. - `unmountOnExit` - type: `boolean | undefined` - default: `false` - description: Whether to unmount the drawer content on exit. ### `BottomSheetTrigger` \[#bottomsheettrigger] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `BottomSheetContent` \[#bottomsheetcontent] - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `layerIndex` - type: `number | undefined` - `showCloseButton` - type: `boolean | undefined` - default: `true` - `showHandle` - type: `boolean | undefined` - default: `false` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `BottomSheetBody` \[#bottomsheetbody] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` ### `BottomSheetFooter` \[#bottomsheetfooter] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Trigger \[#trigger] ``는 `asChild` 패턴을 사용해 자식 요소가 BottomSheet를 열 수 있도록 합니다. ``는 `aria-haspopup="dialog"` 속성을 설정하고, `BottomSheet`의 `open` 상태에 따라 `aria-expanded` 속성을 자동으로 설정합니다. 이 속성은 스크린 리더와 같은 보조 기술에 유용합니다. ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetTriggerExample = () => { return ( Open Content 확인 ); }; export default BottomSheetTriggerExample; ``` ### Controlled \[#controlled] Trigger 외의 방식으로 BottomSheet를 열고 닫을 수 있습니다. 이 경우 `open` prop을 사용하여 BottomSheet의 상태를 제어합니다. ```tsx import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, } from "seed-design/ui/bottom-sheet"; const BottomSheetControlled = () => { const [open, setOpen] = useState(false); const scheduleOpen = () => { setTimeout(() => { setOpen(true); }, 1000); }; return ( <> 1초 후 열기 {/* If you need to omit padding, pass px={0}. */} Content 확인 ); }; export default BottomSheetControlled; ``` ### `onOpenChange` Details \[#onopenchange-details] `onOpenChange` 두 번째 인자로 `details`가 제공됩니다. #### `reason` \[#reason] **열릴 때** (`open: true`) - `"trigger"`: `BottomSheetTrigger` (`BottomSheet.Trigger`)로 열림 **닫힐 때** (`open: false`) - `"closeButton"`: `BottomSheet.CloseButton`으로 닫힘 - `"escapeKeyDown"`: ESC 키 사용 - `"interactOutside"`: 외부 영역 클릭 - `"drag"`: 드래그로 닫힘 - `"handleClickOnLastSnapPoint"`: 마지막 스냅 포인트에서 핸들 클릭으로 닫힘 ```tsx import { HStack, Text, VStack } from "@seed-design/react"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const snapPoints = ["200px", "400px", 1]; export default function BottomSheetOnOpenChangeReason() { const [open, setOpen] = useState(false); const [snap, setSnap] = useState(snapPoints[0]); const [openReason, setOpenReason] = useState(null); const [closeReason, setCloseReason] = useState(null); return ( { setOpen(open); (open ? setOpenReason : setCloseReason)(details?.reason ?? null); }} snapPoints={snapPoints} activeSnapPoint={snap} setActiveSnapPoint={setSnap} > 열기 ESC 키를 누르거나, 외부 영역을 클릭하거나, 아래로 스와이프하거나, 핸들을 탭하여 snap point를 순환해보세요. 마지막 열림 이유: {openReason ?? "-"} 마지막 닫힘 이유: {closeReason ?? "-"} ); } ``` ### Header Align \[#header-align] ``에 `headerAlign` prop을 설정하여 title과 description의 정렬을 설정할 수 있습니다. ```tsx import { HStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetHeaderAlign = () => { return ( Left (기본값) Content 확인 Center Content 확인 ); }; export default BottomSheetHeaderAlign; ``` ### Max Height \[#max-height] ``에 `maxHeight` prop을 전달하여 BottomSheet의 최대 높이를 설정할 수 있습니다. ```tsx import { Box, VStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetMaxHeight = () => { return ( Open {/* If you need to omit padding, pass px={0}. */} 확인 ); }; export default BottomSheetMaxHeight; ``` ### Snap Points \[#snap-points] `snapPoints` prop을 사용하여 BottomSheet의 커스텀 스냅 포인트를 설정할 수 있습니다. ```tsx import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const snapPoints = ["300px", "500px", 1]; const BottomSheetSnapPoints = () => { const [snap, setSnap] = useState(snapPoints[0]); return ( Open Content 확인 ); }; export default BottomSheetSnapPoints; ``` ### Fade From Index \[#fade-from-index] ``에 `fadeFromIndex` prop을 전달하여 뒷 배경이 어두워지는 시작 인덱스를 설정할 수 있습니다. ```tsx import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const snapPoints = ["300px", "500px", 1]; const BottomSheetFadeFromIndex = () => { const [snap, setSnap] = useState(snapPoints[0]); return ( Open Content 확인 ); }; export default BottomSheetFadeFromIndex; ``` ### Show Handle \[#show-handle] ``에 `showHandle` prop을 전달하여 Handle을 표시할 수 있습니다. 기본 값은 `false`입니다. ```tsx import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetShowHandle = () => { const [isSheetOpen, setIsSheetOpen] = useState(false); return ( Open {/* If you need to omit padding, pass px={0}. */} Content setIsSheetOpen(false)}> 닫기 ); }; export default BottomSheetShowHandle; ``` ### Show Close Button \[#show-close-button] ``에 `showCloseButton` prop을 전달하여 닫기 버튼을 표시할 수 있습니다. 기본 값은 `true`입니다. showCloseButton을 `false`로 설정하면 닫기 버튼이 표시되지 않습니다. 이 경우 유저가 BottomSheet를 닫을 수 있는 방법을 제공해야 합니다. ```tsx import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetShowCloseButton = () => { const [isSheetOpen, setIsSheetOpen] = useState(false); return ( Open {/* If you need to omit padding, pass px={0}. */} Content setIsSheetOpen(false)}> 닫기 ); }; export default BottomSheetShowCloseButton; ``` ### Dismissible \[#dismissible] `dismissible` prop을 `false`로 설정하면 closeOnEscape, closeOnInteractOutside, draggable 기능이 비활성화됩니다. 의도적으로 BottomSheet를 닫을 수 없게 하고 싶을 때 사용합니다. 이외에는 유저가 BottomSheet를 닫을 수 있는 방법을 제공해야 합니다. ```tsx import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetDismissible = () => { const [open, setOpen] = useState(false); return ( Open {/* If you need to omit padding, pass px={0}. */} Content setOpen(false)}> 닫기 ); }; export default BottomSheetDismissible; ``` ### With Scroll Fog \[#with-scroll-fog] ``에 [Scroll Fog](/react/components/scroll-fog)를 사용하여 스크롤 힌트 효과를 적용할 수 있습니다. ScrollFog는 항상 효과를 표시하므로, 충분한 padding을 제공해야 합니다. 권장 padding인 하단 `80px`, 상단 `20px`을 유지해야 합니다. ```tsx import { Box, ScrollFog, VStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetMaxHeight = () => { return ( Open {/* If you need to omit padding, pass px={0}. */} 확인 ); }; export default BottomSheetMaxHeight; ``` ### Bottom Inset \[#bottom-inset] ``에 `style` prop을 전달하여 아래 여백을 주기 위해 사용합니다. ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetBottomInset = () => { return ( Open {/* If you need to omit padding, pass px={0}. */} Content 확인 ); }; export default BottomSheetBottomInset; ``` ### Handle Only \[#handle-only] ``에 `handleOnly` prop을 제공하는 경우 쓸어서(swipe) Bottom Sheet를 움직일 수 있는 영역이 핸들 부분으로 제한됩니다. 이 옵션은 ``와 함께 사용할 때만 작동합니다. ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetHandleOnly = () => { return ( Open Content 확인 ); }; export default BottomSheetHandleOnly; ``` ### Skip Animation \[#skip-animation] `skipAnimation` prop을 사용하여 BottomSheet의 enter/exit 애니메이션을 건너뛸 수 있습니다. ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; const BottomSheetSkipAnimation = () => { return ( Open Content 확인 ); }; export default BottomSheetSkipAnimation; ``` --- file: components/callout.mdx # Callout 사용자에게 중요한 정보나 팁을 시각적으로 강조하여 전달하는 메시지 컴포넌트입니다. ## Preview ```tsx import { VStack } from "@seed-design/react"; import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutPreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:callout - pnpm: pnpm dlx @seed-design/cli@latest add ui:callout - yarn: yarn dlx @seed-design/cli@latest add ui:callout - bun: bun x @seed-design/cli@latest add ui:callout ## Props \[#props] ### `Callout` \[#callout] - `prefixIcon` - type: `React.ReactNode` - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - required: `true` - `linkProps` - type: `SeedCallout.LinkProps | undefined` - `tone` - type: `"critical" | "informative" | "neutral" | "positive" | "warning" | "magic" | undefined` - default: `"neutral"` - description: - \`neutral\`: 일반적인 정보를 전달합니다. - \`informative\`: 유용한 정보를 제공합니다. - \`positive\`: 긍정적인 상태를 나타냅니다. - \`warning\`: 주의가 필요한 상태를 나타냅니다. - \`critical\`: 중요한 문제를 나타냅니다. - \`magic\`: AI 기능을 나타냅니다. ### `ActionableCallout` \[#actionablecallout] - `prefixIcon` - type: `React.ReactNode` - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - required: `true` - `tone` - type: `"critical" | "informative" | "neutral" | "positive" | "warning" | "magic" | undefined` - default: `"neutral"` - description: - \`neutral\`: 일반적인 정보를 전달합니다. - \`informative\`: 유용한 정보를 제공합니다. - \`positive\`: 긍정적인 상태를 나타냅니다. - \`warning\`: 주의가 필요한 상태를 나타냅니다. - \`critical\`: 중요한 문제를 나타냅니다. - \`magic\`: AI 기능을 나타냅니다. ### `DismissibleCallout` \[#dismissiblecallout] - `prefixIcon` - type: `React.ReactNode` - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - required: `true` - `linkProps` - type: `SeedCallout.LinkProps | undefined` - `tone` - type: `"critical" | "informative" | "neutral" | "positive" | "warning" | "magic" | undefined` - default: `"neutral"` - description: - \`neutral\`: 일반적인 정보를 전달합니다. - \`informative\`: 유용한 정보를 제공합니다. - \`positive\`: 긍정적인 상태를 나타냅니다. - \`warning\`: 주의가 필요한 상태를 나타냅니다. - \`critical\`: 중요한 문제를 나타냅니다. - \`magic\`: AI 기능을 나타냅니다. - `open` - type: `boolean | undefined` - `defaultOpen` - type: `boolean | undefined` - `onDismiss` - type: `(() => void) | undefined` ## Examples \[#examples] ### Content Layout \[#content-layout] #### Text Only \[#text-only] ```tsx import { VStack } from "@seed-design/react"; import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutTextOnly() { return ( ); } ``` #### With Icon \[#with-icon] ```tsx import { IconCalendarFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutWithIcon() { return ( } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> ); } ``` #### With Title Text \[#with-title-text] ```tsx import { VStack } from "@seed-design/react"; import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutWithTitleText() { return ( ); } ``` #### With Link Label \[#with-link-label] `ActionableCallout`에서는 `linkProps`를 제공하지 않아요. ```tsx import { VStack } from "@seed-design/react"; import { Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutWithLinkLabel() { return ( ); } ``` #### With All \[#with-all] ```tsx import { IconCalendarFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutWithAll() { return ( } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." linkProps={{ children: "시도해 보기" }} /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." linkProps={{ children: "시도해 보기" }} /> ); } ``` ### Customizable Parts \[#customizable-parts] #### Rendering Link Label as Child \[#rendering-link-label-as-child] ```tsx import { VStack } from "@seed-design/react"; import { Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutLinkLabelAsChild() { return ( 시도해 보기 ), }} /> 시도해 보기 ), }} /> ); } ``` ### Tones \[#tones] #### Neutral (Default) \[#neutral-default] ```tsx import { IconCalendarFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutNeutral() { return ( } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> ); } ``` #### Informative \[#informative] ```tsx import { IconCalendarFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutInformative() { return ( } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> ); } ``` #### Positive \[#positive] ```tsx import { IconCalendarFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutPositive() { return ( } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> ); } ``` #### Warning \[#warning] ```tsx import { IconCalendarFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutWarning() { return ( } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> ); } ``` #### Critical \[#critical] ```tsx import { IconCalendarFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutCritical() { return ( } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> } description="기능에 대한 안내 또는 유익한 내용을 전달해요. 콜아웃은 꼭 필요한 경우에만 절제하여 사용해요." /> ); } ``` #### Magic \[#magic] ```tsx import { IconSparkle2 } from "@karrotmarket/react-multicolor-icon"; import { VStack } from "@seed-design/react"; import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout"; export default function CalloutMagic() { return ( } /> } /> } /> ); } ``` --- file: components/checkbox.mdx # Checkbox 사용자가 하나 이상의 옵션을 선택할 수 있게 해주는 컴포넌트입니다. 목록에서 여러 항목을 선택하거나 약관 동의와 같은 선택적 작업에 사용됩니다. ## Preview ```tsx import { VStack } from "@seed-design/react"; import { Checkbox, CheckboxGroup } from "seed-design/ui/checkbox"; export default function CheckboxPreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:checkbox - pnpm: pnpm dlx @seed-design/cli@latest add ui:checkbox - yarn: yarn dlx @seed-design/cli@latest add ui:checkbox - bun: bun x @seed-design/cli@latest add ui:checkbox ## Props \[#props] ### `CheckboxGroup` \[#checkboxgroup] - `label` - type: `React.ReactNode` - `labelWeight` - type: `"bold" | "medium" | undefined` - default: `"medium"` - `indicator` - type: `React.ReactNode` - `showRequiredIndicator` - type: `boolean | undefined` - `description` - type: `React.ReactNode` - `errorMessage` - type: `React.ReactNode` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `Checkbox` \[#checkbox] - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `rootRef` - type: `React.Ref | undefined` - `label` - type: `React.ReactNode` - `weight` - type: `"bold" | "regular" | undefined` - default: `"regular"` - `size` - type: `"medium" | "large" | undefined` - default: `"medium"` - `variant` - type: `"square" | "ghost" | undefined` - default: `"square"` - description: - \`square\`: 필수 선택 항목이고 사용자가 해당 내용을 인지해야 하는 경우 사용합니다. - \`ghost\`: 필수 선택 항목이 아니고, 3개 이하 항목으로 구성되는 경우 사용하는 것을 권장합니다. - `tone` - type: `"brand" | "neutral" | undefined` - default: `"brand"` - `disabled` - type: `boolean | undefined` - `invalid` - type: `boolean | undefined` - `required` - type: `boolean | undefined` - `checked` - type: `boolean | undefined` - `defaultChecked` - type: `boolean | undefined` - `onCheckedChange` - type: `((checked: boolean) => void) | undefined` - `indeterminate` - type: `boolean | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `Checkmark` \[#checkmark] - `variant` - type: `"square" | "ghost" | undefined` - default: `"square"` - description: - \`square\`: 필수 선택 항목이고 사용자가 해당 내용을 인지해야 하는 경우 사용합니다. - \`ghost\`: 필수 선택 항목이 아니고, 3개 이하 항목으로 구성되는 경우 사용하는 것을 권장합니다. - `tone` - type: `"brand" | "neutral" | undefined` - default: `"brand"` - `size` - type: `"medium" | "large" | undefined` - default: `"medium"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Sizes \[#sizes] ```tsx import { HStack } from "@seed-design/react"; import { Checkbox, CheckboxGroup } from "seed-design/ui/checkbox"; export default function CheckboxSize() { return ( ); } ``` ### Tones and Variants \[#tones-and-variants] #### Brand \[#brand] ```tsx import { VStack } from "@seed-design/react"; import { Checkbox, CheckboxGroup } from "seed-design/ui/checkbox"; export default function CheckboxBrand() { return ( ); } ``` #### Neutral \[#neutral] ```tsx import { VStack } from "@seed-design/react"; import { Checkbox, CheckboxGroup } from "seed-design/ui/checkbox"; export default function CheckboxNeutral() { return ( ); } ``` ### Indeterminate \[#indeterminate] ```tsx import { VStack } from "@seed-design/react"; import { Checkbox } from "seed-design/ui/checkbox"; export default function CheckboxIndeterminate() { return ( ); } ``` ### Weights \[#weights] `weight="default"`와 `weight="stronger"`는 더 이상 사용되지 않습니다. 대신 `weight="regular"`와 `weight="bold"`를 사용하세요. ```tsx import { VStack } from "@seed-design/react"; import { Checkbox, CheckboxGroup } from "seed-design/ui/checkbox"; export default function CheckboxWeights() { return ( ); } ``` ### Long Label \[#long-label] ```tsx import { VStack } from "@seed-design/react"; import { Checkbox, CheckboxGroup } from "seed-design/ui/checkbox"; export default function CheckboxLongLabel() { return ( ); } ``` ### Disabled \[#disabled] ```tsx import { VStack } from "@seed-design/react"; import { Checkbox, CheckboxGroup } from "seed-design/ui/checkbox"; export default function CheckboxDisabled() { return ( ); } ``` ### Listening to Value Changes \[#listening-to-value-changes] `onCheckedChange`를 사용하여 체크박스의 선택 상태 변경을 감지할 수 있습니다. 이벤트를 활용해야 하는 경우 `inputProps`를 통해 내부 `` 요소에 직접 이벤트 핸들러를 추가할 수 있습니다. ```tsx import { VStack, Text } from "@seed-design/react"; import { Checkbox } from "seed-design/ui/checkbox"; import { useState } from "react"; export default function CheckboxValueChanges() { const [count, setCount] = useState(0); const [lastValue, setLastValue] = useState(null); return ( { setCount((prev) => prev + 1); setLastValue(checked); }} /> onCheckedChange called: {count} times, last value: {`${lastValue ?? "-"}`} ); } ``` ### Use Cases \[#use-cases] #### React Hook Form \[#react-hook-form] ```tsx import { HStack, VStack } from "@seed-design/react"; import { useCallback, type FormEvent } from "react"; import { useController, useForm, type Control } from "react-hook-form"; import { ActionButton } from "seed-design/ui/action-button"; import { Checkbox, CheckboxGroup } from "seed-design/ui/checkbox"; const POSSIBLE_FRUIT_VALUES = ["apple", "melon", "mango"] as const; type FormValues = Record<(typeof POSSIBLE_FRUIT_VALUES)[number], boolean>; export default function CheckboxReactHookForm() { const { handleSubmit, reset, setValue, control } = useForm({ defaultValues: { apple: false, melon: true, mango: false, }, }); const onValid = useCallback((data: FormValues) => { window.alert(JSON.stringify(data, null, 2)); }, []); const onReset = useCallback( (event: FormEvent) => { event.preventDefault(); reset(); }, [reset], ); return ( {POSSIBLE_FRUIT_VALUES.map((name) => ( ))} 초기화 setValue("mango", true)} > mango 선택 제출 ); } interface CheckboxItemProps { name: keyof FormValues; control: Control; } function CheckboxItem({ name, control }: CheckboxItemProps) { const { field: { value, ...restProps }, fieldState: { invalid }, } = useController({ name, control }); return ( ); } ``` #### Using `Checkmark` \[#using-checkmark] `Checkmark`는 독립적인 체크 마크 컴포넌트로, Checkbox Primitive 컴포넌트와 조합하여 커스텀 레이아웃을 위해 사용할 수 있습니다. ```tsx import { HStack, Text, VStack } from "@seed-design/react"; import { Checkbox } from "@seed-design/react/primitive"; import { Checkmark } from "seed-design/ui/checkbox"; function CustomCheckbox({ children, ...props }: Checkbox.RootProps) { return ( {children} ); } export default function CheckboxCheckmark() { return ( regular medium bold ); } ``` ### Fieldset Integration \[#fieldset-integration] `CheckboxGroup`을 사용하여 여러 체크박스를 그룹화하고 `label`, `description`, `errorMessage` 등의 Fieldset 관련 prop을 사용할 수 있습니다. ```tsx import { ActionButton, HStack, VStack } from "@seed-design/react"; import { useState } from "react"; import { Checkbox, CheckboxGroup } from "seed-design/ui/checkbox"; export default function CheckboxFieldset() { const [firstErrors, setFirstErrors] = useState>({}); const [secondErrors, setSecondErrors] = useState>({}); const handleFirstSubmit = (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget); const fruits = formData.getAll("fruit"); if (fruits.includes("apple")) { setFirstErrors({ apple: "Apple은 선택할 수 없습니다." }); return; } setFirstErrors({}); alert(JSON.stringify(fruits, null, 2)); }; const handleSecondSubmit = (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget); const agreements = formData.getAll("agreement"); const hasTerms = agreements.includes("terms"); const hasPrivacy = agreements.includes("privacy"); if (!hasTerms || !hasPrivacy) { setSecondErrors({ ...(!hasTerms && { terms: "필수 항목에 동의해 주세요." }), ...(!hasPrivacy && { privacy: "필수 항목에 동의해 주세요." }), }); return; } setSecondErrors({}); alert(JSON.stringify(agreements, null, 2)); }; return (
제출
제출
); } ``` --- file: components/chip-tabs.mdx # Chip Tabs Chip 형태로 표현된 탭 컴포넌트입니다. 카테고리나 필터를 선택하여 콘텐츠를 전환할 때 사용됩니다. ## Preview ```tsx import * as React from "react"; import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "seed-design/ui/chip-tabs"; export default function ChipTabsPreview() { const [value, setValue] = React.useState("1"); return ( <> setValue(value)} > 라벨1 라벨2 라벨3 {value === "1" &&
content 1
} {value === "2" &&
content 2
} {value === "3" &&
content 3
} ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:chip-tabs - pnpm: pnpm dlx @seed-design/cli@latest add ui:chip-tabs - yarn: yarn dlx @seed-design/cli@latest add ui:chip-tabs - bun: bun x @seed-design/cli@latest add ui:chip-tabs ## Props \[#props] ### `ChipTabsRoot` \[#chiptabsroot] - `size` - type: `"medium" | "large" | undefined` - default: `"medium"` - `variant` - type: `"neutralSolid" | "neutralOutline" | undefined` - default: `"neutralSolid"` - `contentLayout` - type: `"fill" | "hug" | undefined` - default: `"hug"` - `stickyList` - type: `boolean | undefined` - default: `false` - `orientation` - type: `"horizontal" | "vertical" | undefined` - `value` - type: `string | undefined` - `defaultValue` - type: `string | undefined` - `onValueChange` - type: `((value: string) => void) | undefined` - `lazyMount` - type: `boolean | undefined` - default: `false` - description: If \`true\`, the component will be mounted lazily. - `unmountOnExit` - type: `boolean | undefined` - default: `false` - description: If \`true\`, the component will be unmounted when it's not selected. - `present` - type: `boolean | undefined` - default: `false` - description: If \`true\`, the component will be mounted. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `ChipTabsList` \[#chiptabslist] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `ChipTabsTrigger` \[#chiptabstrigger] - `notification` - type: `boolean | undefined` - `disabled` - type: `boolean | undefined` - `value` - type: `string` - required: `true` ### `ChipTabsCarousel` \[#chiptabscarousel] - `swipeable` - type: `boolean | undefined` - `autoHeight` - type: `boolean | undefined` - `loop` - type: `boolean | undefined` - `dragThreshold` - type: `number | undefined` - `onSettle` - type: `(() => void) | undefined` - `onSwipeStart` - type: `(() => void) | undefined` - description: 스와이프 시작 시 호출됩니다. - `onSwipeEnd` - type: `(() => void) | undefined` - description: 스와이프 종료 시 호출됩니다. ### `ChipTabsContent` \[#chiptabscontent] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `value` - type: `string` - required: `true` ## Examples \[#examples] ### Size=Medium \[#sizemedium] ```tsx import * as React from "react"; import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "seed-design/ui/chip-tabs"; export default function ChipTabsSizeMedium() { const [value, setValue] = React.useState("1"); return ( <> setValue(value)} > 라벨1 라벨2 라벨3 {value === "1" &&
content 1
} {value === "2" &&
content 2
} {value === "3" &&
content 3
} ); } ``` ### Size=Large \[#sizelarge] ```tsx import * as React from "react"; import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "seed-design/ui/chip-tabs"; export default function ChipTabsSizeLarge() { const [value, setValue] = React.useState("1"); return ( <> setValue(value)} > 라벨1 라벨2 라벨3 {value === "1" &&
content 1
} {value === "2" &&
content 2
} {value === "3" &&
content 3
} ); } ``` ### Variant=Neutral Solid \[#variantneutral-solid] ```tsx import * as React from "react"; import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "seed-design/ui/chip-tabs"; export default function ChipTabsVariantNeutralSolid() { const [value, setValue] = React.useState("1"); return ( <> setValue(value)} > 라벨1 라벨2 라벨3 {value === "1" &&
content 1
} {value === "2" &&
content 2
} {value === "3" &&
content 3
} ); } ``` ### Variant=Neutral Outline \[#variantneutral-outline] ```tsx import * as React from "react"; import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "seed-design/ui/chip-tabs"; export default function ChipTabsVariantNeutralOutline() { const [value, setValue] = React.useState("1"); return ( <> setValue(value)} > 라벨1 라벨2 라벨3 {value === "1" &&
content 1
} {value === "2" &&
content 2
} {value === "3" &&
content 3
} ); } ``` ### Notification \[#notification] ```tsx import * as React from "react"; import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "seed-design/ui/chip-tabs"; export default function ChipTabsNotification() { const [value, setValue] = React.useState("1"); return ( <> setValue(value)} > 라벨1 라벨2 라벨3 {value === "1" &&
content 1
} {value === "2" &&
content 2
} {value === "3" &&
content 3
} ); } ``` ### With Scroll Fog \[#with-scroll-fog] 탭이 많아 가로 스크롤이 필요한 경우 [Scroll Fog](/react/components/scroll-fog)를 적용하여 좌우 스크롤 힌트를 제공할 수 있습니다. 권장 padding인 `20px`을 유지해야 합니다. ```tsx import { ScrollFog } from "@seed-design/react"; import { ChipTabsCarousel, ChipTabsList, ChipTabsRoot, ChipTabsTrigger, } from "seed-design/ui/chip-tabs"; export default function ChipTabsWithScrollFog() { return (
{Array.from({ length: 15 }, (_, i) => ( 라벨{i + 1} ))}
); } ``` --- file: components/chip.mdx # Chip 사용자가 선택하거나 입력하는 값을 표시하는 컴포넌트입니다. ## Preview ```tsx import { HStack, VStack } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; export default function ChipPreview() { return ( Button Chip Toggle Chip Radio Chip 1 Radio Chip 2 ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:chip - pnpm: pnpm dlx @seed-design/cli@latest add ui:chip - yarn: yarn dlx @seed-design/cli@latest add ui:chip - bun: bun x @seed-design/cli@latest add ui:chip ## Props \[#props] ### `Chip.Button` \[#chipbutton] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `variant` - type: `"solid" | "outlineStrong" | "outlineWeak" | undefined` - default: `"solid"` - description: - \`solid\`: 기본 스타일입니다. - \`outlineStrong\`: 명확한 구분이 필요한 경우 사용합니다. - \`outlineWeak\`: Selection 사용 시 주목도가 낮은 스타일로 권장됩니다. - `size` - type: `"small" | "medium" | "large" | undefined` - default: `"medium"` - `layout` - type: `"withText" | "iconOnly" | undefined` - default: `"withText"` ### `Chip.Toggle` \[#chiptoggle] - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `rootRef` - type: `React.Ref | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `variant` - type: `"solid" | "outlineStrong" | "outlineWeak" | undefined` - default: `"solid"` - description: - \`solid\`: 기본 스타일입니다. - \`outlineStrong\`: 명확한 구분이 필요한 경우 사용합니다. - \`outlineWeak\`: Selection 사용 시 주목도가 낮은 스타일로 권장됩니다. - `size` - type: `"small" | "medium" | "large" | undefined` - default: `"medium"` - `layout` - type: `"withText" | "iconOnly" | undefined` - default: `"withText"` - `disabled` - type: `boolean | undefined` - `invalid` - type: `boolean | undefined` - `required` - type: `boolean | undefined` - `checked` - type: `boolean | undefined` - `defaultChecked` - type: `boolean | undefined` - `onCheckedChange` - type: `((checked: boolean) => void) | undefined` - `indeterminate` - type: `boolean | undefined` ### `Chip.RadioRoot` \[#chipradioroot] - `disabled` - type: `boolean | undefined` - default: `false` - `invalid` - type: `boolean | undefined` - default: `false` - `name` - type: `string | undefined` - `form` - type: `string | undefined` - `value` - type: `string | undefined` - `defaultValue` - type: `string | undefined` - `onValueChange` - type: `((value: string) => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `Chip.RadioItem` \[#chipradioitem] - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `rootRef` - type: `React.Ref | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `variant` - type: `"solid" | "outlineStrong" | "outlineWeak" | undefined` - default: `"solid"` - description: - \`solid\`: 기본 스타일입니다. - \`outlineStrong\`: 명확한 구분이 필요한 경우 사용합니다. - \`outlineWeak\`: Selection 사용 시 주목도가 낮은 스타일로 권장됩니다. - `size` - type: `"small" | "medium" | "large" | undefined` - default: `"medium"` - `layout` - type: `"withText" | "iconOnly" | undefined` - default: `"withText"` - `value` - type: `string` - required: `true` - `disabled` - type: `boolean | undefined` ## Examples \[#examples] ### Sizes \[#sizes] #### Small \[#small] ```tsx import { HStack, VStack } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; export default function ChipSmall() { return ( Small Button Small Toggle Small Radio 1 Small Radio 2 ); } ``` #### Medium \[#medium] ```tsx import { HStack, VStack } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; export default function ChipMedium() { return ( Medium Button Medium Toggle Medium Radio 1 Medium Radio 2 ); } ``` #### Large \[#large] ```tsx import { HStack, VStack } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; export default function ChipLarge() { return ( Large Button Large Toggle Large Radio 1 Large Radio 2 ); } ``` ### Variants \[#variants] #### Solid \[#solid] ```tsx import { HStack, VStack } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; export default function ChipSolid() { return ( Solid Button Solid Toggle Solid Radio 1 Solid Radio 2 ); } ``` #### Outline Strong \[#outline-strong] ```tsx import { HStack, VStack } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; export default function ChipOutlineStrong() { return ( Outline Strong Button Outline Strong Toggle Outline Strong Radio 1 Outline Strong Radio 2 ); } ``` #### Outline Weak \[#outline-weak] ```tsx import { HStack, VStack } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; export default function ChipOutlineWeak() { return ( Outline Weak Button Outline Weak Toggle Outline Weak Radio 1 Outline Weak Radio 2 ); } ``` ### Prefix Icon \[#prefix-icon] ```tsx import { HStack, VStack, Icon } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; import { IconHeartFill } from "@karrotmarket/react-monochrome-icon"; export default function ChipPrefixIcon() { return ( } /> With Icon Button } /> With Icon Toggle } /> With Icon Radio 1 } /> With Icon Radio 2 ); } ``` ### Suffix Icon \[#suffix-icon] ```tsx import { HStack, VStack, Icon } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; import { IconChevronDownLine } from "@karrotmarket/react-monochrome-icon"; export default function ChipSuffixIcon() { return ( Button with Suffix } /> Toggle with Suffix } /> Radio with Suffix 1 } /> Radio with Suffix 2 } /> ); } ``` ### Icon Only \[#icon-only] ```tsx import { HStack, VStack, Icon } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; import { IconArrowClockwiseCircularLine, IconBellLine, IconBellSlashLine, IconTimer_10Line, IconTimer_3Line, } from "@karrotmarket/react-monochrome-icon"; import { useState } from "react"; export default function ChipIconOnly() { const [checked, setChecked] = useState(false); return ( } /> : } /> } /> } /> ); } ``` ### Prefix Avatar \[#prefix-avatar] [Avatar](/react/components/avatar)와 함께 사용할 수 있습니다. ```tsx import { HStack, VStack } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; import { Avatar } from "seed-design/ui/avatar"; import { IdentityPlaceholder } from "seed-design/ui/identity-placeholder"; export default function ChipPrefixAvatar() { return ( } /> With Avatar Button } /> With Avatar Toggle } /> With Avatar Radio 1 } /> With Avatar Radio 2 ); } ``` ### Listening to Value Changes \[#listening-to-value-changes] - `Chip.Toggle` - `onCheckedChange`를 사용하여 토글 상태 변경을 감지할 수 있습니다. - 이벤트를 활용해야 하는 경우 `inputProps`를 통해 내부 `` 요소에 직접 이벤트 핸들러를 추가할 수 있습니다. - `Chip.RadioRoot` / `Chip.RadioItem` - `Chip.RadioRoot`의 `onValueChange`를 사용하여 라디오 버튼의 선택 값 변경을 감지할 수 있습니다. - 이벤트를 활용해야 하는 경우 `Chip.RadioItem`의 `inputProps`를 통해 내부 `` 요소에 직접 이벤트 핸들러를 추가할 수 있습니다. ```tsx import { HStack, VStack, Text } from "@seed-design/react"; import { Chip } from "seed-design/ui/chip"; import { useState } from "react"; export default function ChipValueChanges() { const [toggleCount, setToggleCount] = useState(0); const [toggleLastValue, setToggleLastValue] = useState(null); const [radioCount, setRadioCount] = useState(0); const [radioLastValue, setRadioLastValue] = useState(null); return ( { setToggleCount((prev) => prev + 1); setToggleLastValue(checked); }} > Toggle Chip onCheckedChange called: {toggleCount} times, last value: {`${toggleLastValue ?? "-"}`} { setRadioCount((prev) => prev + 1); setRadioLastValue(value); }} > Radio 1 Radio 2 onValueChange called: {radioCount} times, last value: {radioLastValue ?? "-"} ); } ``` ## Migrating from ActionChip/ControlChip \[#migrating-from-actionchipcontrolchip] [Action Chip](/react/components/action-chip)과 [Control Chip](/react/components/control-chip)을 대체합니다. ### ActionChip → Chip.Button \[#actionchip--chipbutton] ```tsx // Before import { ActionChip } from "@seed-design/react"; Label // After import { Chip } from "@seed-design/react"; Label ``` ### ControlChip → Chip.Toggle \[#controlchip--chiptoggle] ```tsx // Before import { ControlChip } from "@seed-design/react"; Label // After import { Chip } from "@seed-design/react"; Label ``` --- file: components/concepts/composition.mdx # Composition 컴포넌트를 조합하여 복잡한 UI를 구성하는 방법과 원칙을 설명합니다. ## `as` Prop \[#as-prop] `as` prop은 React 컴포넌트가 렌더링하는 기본 HTML 요소를 변경하는 데 사용됩니다. 컴포넌트의 기능은 유지하면서 기본 요소를 변경할 수 있는 간단한 방법을 제공합니다. ```jsx 단락 텍스트 인라인 텍스트 제목 텍스트 ``` ## `asChild` Prop \[#aschild-prop] `asChild` prop은 컴포넌트의 기능을 자식 요소에 합성하는 데 사용됩니다. 이를 활용해 HTML 태그를 변경하거나, 다른 컴포넌트에 기능을 주입할 수 있습니다. [Radix UI](https://www.radix-ui.com/primitives/docs/utilities/slot)에서 제공하는 asChild 패턴과 동일한 기능을 제공합니다. ### 요소 타입 변경하기 \[#요소-타입-변경하기] ```jsx // FAB는 기본적으로 button 태그를 사용하지만, asChild를 사용해 링크로 변경할 수 있습니다. } /> ``` > 기본 요소 타입을 변경하는 경우, 해당 요소가 접근성을 유지하고 올바르게 작동하는지 확인하는 것은 개발자의 책임입니다. 예를 들어, 버튼을 `div`로 변경하면 더 이상 키보드로 접근할 수 없게 될 수 있습니다. ### React 컴포넌트와 조합하기 \[#react-컴포넌트와-조합하기] ```jsx // 라우터 라이브러리에서 제공하는 Link 컴포넌트에 기능을 합성할 수 있습니다. ``` `asChild`를 사용하여 컴포넌트를 조합할 때는 두 가지 원칙을 따라야 합니다. #### 1. 컴포넌트는 props를 전개해야 합니다 \[#1-컴포넌트는-props를-전개해야-합니다] asChild는 기능을 제공하고 접근성을 유지하기 위한 고유의 props와 이벤트 핸들러를 전달합니다. 컴포넌트가 이러한 props를 지원하지 않으면 제대로 작동하지 않습니다. ```jsx // 잘못된 방법 const MyButton = () => } /> } /> : } /> {isCopied ? "복사됨" : "URL 복사"} } /> } href={href} target="_blank" rel="noreferrer" /> ); } ``` ### `input`s in List Items \[#inputs-in-list-items] `ListSwitchItem`, `ListCheckItem`, `ListRadioItem`을 사용해서 리스트 항목에 `input` 요소를 포함할 수 있습니다. 이때, [Switchmark](/react/components/switch#using-switchmark), [Checkmark](/react/components/checkbox#using-checkmark) 또는 [Radiomark](/react/components/radio-group#using-radiomark)와 같은 컨트롤 요소를 `prefix`나 `suffix` 영역에 넣어 사용합니다. ```tsx import { IconTrashcanLine } from "@karrotmarket/react-monochrome-icon"; import { IconSparkle2 } from "@karrotmarket/react-multicolor-icon"; import { Icon } from "@seed-design/react"; import { List, ListDivider, ListSwitchItem } from "seed-design/ui/list"; import { Switchmark } from "seed-design/ui/switch"; export default function ListSwitch() { return ( } />} suffix={} /> } />} suffix={} defaultChecked /> ); } ``` ```tsx import { Badge, HStack } from "@seed-design/react"; import { List, ListDivider, ListCheckItem } from "seed-design/ui/list"; import { Checkmark } from "seed-design/ui/checkbox"; export default function ListCheckbox() { return ( 알림 수신 동의 권장 } detail="푸시 알림을 받으시겠습니까?" suffix={} defaultChecked /> } title="마케팅 정보 수신 동의" detail="마케팅 정보를 받으시겠습니까?" defaultChecked /> } title="Ghost Variant" /> ); } ``` ```tsx import { RadioGroup } from "@seed-design/react/primitive"; import { List, ListDivider, ListRadioItem } from "seed-design/ui/list"; import { Radiomark } from "seed-design/ui/radio-group"; export default function ListRadio() { return ( } /> } value="option2" title="옵션 2" detail="두 번째 선택지" /> } value="option3" title="옵션 3" detail="세 번째 선택지" /> ); } ``` ### Accessibility \[#accessibility] `List`는 기본적으로 `
    `입니다. `ListCheckItem`와 `ListRadioItem`를 사용하는 경우 `List`에 적절한 role을 부여해야 합니다. ```tsx {/* [!code highlight] */} } title="알림 수신 동의" detail="푸시 알림을 받으시겠습니까?" /> } title="마케팅 정보 수신 동의" detail="마케팅 정보를 받으시겠습니까?" /> ``` ```tsx {/* [!code highlight] */} {/*
    */} } value="짜장" title="짜장" /> } value="짬뽕" title="짬뽕" /> ``` ### Disabled \[#disabled] ```tsx import { IconChevronRightLine, IconPersonCircleLine, IconSlashCircleLine, } from "@karrotmarket/react-monochrome-icon"; import { Divider, Icon, VStack } from "@seed-design/react"; import { RadioGroup } from "@seed-design/react/primitive"; import { List, ListButtonItem, ListCheckItem, ListRadioItem } from "seed-design/ui/list"; import { Checkmark } from "seed-design/ui/checkbox"; import { Radiomark } from "seed-design/ui/radio-group"; export default function ListDisabled() { return ( } />} title="활성화된 ListButtonItem" detail="Cupidatat et pariatur amet." suffix={} />} /> } />} title="활성화된 ListCheckItem" suffix={} /> } />} title="활성화된 ListRadioItem" suffix={} value="foo" /> } />} title="비활성화된 ListButtonItem" detail="Cupidatat et pariatur amet." suffix={} />} /> } />} title="비활성화된 ListCheckItem" suffix={} /> } />} title="비활성화된 ListRadioItem" suffix={} value="foo" /> ); } ``` ### Variants \[#variants] #### Highlighted \[#highlighted] ```tsx import { IconPersonCircleLine } from "@karrotmarket/react-monochrome-icon"; import { Box, Icon, VStack } from "@seed-design/react"; import { useState } from "react"; import { List, ListDivider, ListItem, ListButtonItem } from "seed-design/ui/list"; import { Switch } from "seed-design/ui/switch"; export default function ListHighlighted() { const [highlighted, setHighlighted] = useState(true); return ( } />} title="버튼" detail="Enim aute duis magna mollit aute sit aliquip duis ut tempor sunt." onClick={() => {}} /> } />} title="하이라이트된 버튼" detail="Enim aute duis magna mollit aute sit aliquip duis ut tempor sunt." onClick={() => {}} /> } />} title="하이라이트 및 비활성화된 버튼" detail="Enim aute duis magna mollit aute sit aliquip duis ut tempor sunt." onClick={() => {}} /> } />} title="하이라이트" highlighted={highlighted} /> ); } ``` ### With Bottom Sheet \[#with-bottom-sheet] ```tsx import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; import { ActionButton } from "seed-design/ui/action-button"; import { Checkmark } from "seed-design/ui/checkbox"; import { List, ListCheckItem } from "seed-design/ui/list"; import { PrefixIcon, VStack } from "@seed-design/react"; import { useState } from "react"; import { IconArrowClockwiseCircularFill } from "@karrotmarket/react-monochrome-icon"; const TYPES = ["버스", "지하철", "택시", "자전거", "도보"] as const; export default function ListBottomSheet() { const [isOpen, setIsOpen] = useState(false); const [selectedTypes, setSelectedTypes] = useState<(typeof TYPES)[number][]>([]); return ( BottomSheet 열기
    { e.preventDefault(); setSelectedTypes([]); }} onSubmit={(e) => { e.preventDefault(); setIsOpen(false); }} > {TYPES.map((type) => ( } onCheckedChange={() => { setSelectedTypes((prev) => prev.includes(type) ? prev.filter((t) => t !== type) : [...prev, type], ); }} /> ))} 경로 찾기 } /> 초기화
    ); } ``` ### Alignment \[#alignment] `alignItems` prop으로 `prefix`와 `suffix`의 정렬 방식을 조정할 수 있습니다. ```tsx import { List, ListItem } from "seed-design/ui/list"; import { HStack } from "@seed-design/react"; import { IdentityPlaceholder } from "seed-design/ui/identity-placeholder"; import { Avatar } from "seed-design/ui/avatar"; export default function ListAlignment() { return ( } />} title="Prefix에 Avatar 넣기. Veniam elit velit esse ea incididunt sunt sit aute." detail="Et proident sit ullamco ut voluptate. Voluptate eiusmod occaecat adipisicing quis qui esse." /> } />} title="Prefix에 Avatar 넣고 상단으로 정렬하기. Veniam elit velit esse ea incididunt sunt sit aute." detail="일반적으로 `title`이 길어질 때 `alignItems`를 `flex-start`로 설정합니다." /> ); } ``` ### Border Radius \[#border-radius] `List`에 `itemBorderRadius` prop을 설정하여 active 상태에서 적용되는 리스트 항목의 radius를 조정할 수 있습니다. 리스트가 카드 등 모서리가 둥근 컨테이너 안에 포함되는 경우 유용합니다. ```tsx import { List, ListCheckItem, ListRadioItem } from "seed-design/ui/list"; import { ListHeader } from "seed-design/ui/list-header"; import { Checkmark } from "seed-design/ui/checkbox"; import { Radiomark } from "seed-design/ui/radio-group"; import { HStack, VStack } from "@seed-design/react"; import { RadioGroup } from "@seed-design/react/primitive"; export default function ListBorderRadius() { return ( 카드 borderRadius: r3_5 } /> } /> 카드 borderRadius: 22px } /> } /> ); } ``` ## Customization and Composition \[#customization-and-composition] ### Usage \[#usage-1] @seed-design/react 패키지에서 제공하는 `List`와 `ListHeader` 컴포넌트는 다음과 같은 구조로 사용됩니다. ``` ListHeader List.Root └── List.Item ├── List.Prefix (선택사항) ├── List.Content │ ├── List.Title │ └── List.Detail (선택사항) └── List.Suffix (선택사항) └── List.Item ├── ... ``` ```tsx import { ListHeader, List, Icon } from "@seed-design/react"; 내 정보 } /> 내 프로필 다른 사람들에게 보이는 내 정보를 관리합니다. } /> {/* ... */} ``` - `ListHeader`: 리스트의 제목이나 설명을 표시하는 헤더 역할 - `List.Root`: 모든 리스트 항목을 감싸는 컨테이너 역할 - `List.Item`: 개별 리스트 항목. 클릭 가능한 영역을 정의 - `List.Prefix`: 아이콘, [Avatar](/react/components/avatar), [Checkmark](/react/components/checkbox#using-checkmark) 등을 표시할 수 있는 시작 영역 - `List.Content`: 주요 콘텐츠가 들어가는 중앙 영역 - `List.Title`: 리스트 항목의 제목 - `List.Detail`: 부가 설명이나 세부 정보 - `List.Suffix`: 아이콘, [Action Button](/react/components/action-button), [Toggle Button](/react/components/toggle-button) 등을 표시할 수 있는 끝 영역 ### `asChild` prop으로 적절한 시맨틱 요소와 조합하기 \[#aschild-prop으로-적절한-시맨틱-요소와-조합하기] `asChild` prop에 대해 자세히 알아봅니다. #### Using `asChild` prop in `List.Content` \[#using-aschild-prop-in-listcontent] 리스트 항목 전체 영역을 클릭 가능한 버튼으로 만드는 경우 활용할 수 있는 패턴입니다. 이 경우 `List.Item`에 `asChild` prop을 사용하지 않도록 유의하세요. `List.Prefix` 또는 `List.Suffix`에 버튼을 넣는 경우 `button`이 중첩되는 등 유효하지 않은 HTML이 생성됩니다. ```tsx import { List as SeedList } from "@seed-design/react"; {/* [!code highlight] */} {/* [!code highlight] */} {/* [!code highlight] */} alert("보기 클릭됨")} > 보기 ``` [Snippet](/react/components/concepts/snippet)으로 제공되는 `ListButtonItem` 및 `ListLinkItem`는 이 패턴을 쉽게 구현할 수 있도록 돕습니다. ```tsx import { ListButtonItem } from "seed-design/ui/list"; alert("사용자 클릭됨")} title="사용자" detail="항목 6개" suffix={ alert("보기 클릭됨")} > 보기 } /> ``` #### Using `asChild` prop in `List.Item` \[#using-aschild-prop-in-listitem] 리스트 항목 전체 영역을 `label`로 만들고, `List.Prefix` 또는 `List.Suffix`에 [Switchmark](/react/components/switch#using-switchmark), [Checkmark](/react/components/checkbox#using-checkmark) 또는 [Radiomark](/react/components/radio-group#using-radiomark)를 넣는 경우 활용할 수 있는 패턴입니다. ```tsx import { List as SeedList } from "@seed-design/react"; import { Checkbox } from "@seed-design/react/primitive"; {/* [!code highlight] */} {/* ``` [Snippet](/react/components/concepts/snippet)으로 제공되는 `ListSwitchItem`, `ListCheckItem` 및 `ListRadioItem`는 이 패턴을 쉽게 구현할 수 있도록 돕습니다. ```tsx import { ListCheckItem } from "seed-design/ui/list"; } /> ``` --- file: components/manner-temp-badge.mdx # Manner Temp Badge 매너 온도를 배지 형태로 표현하는 컴포넌트입니다. 콤팩트한 공간에서 사용자의 매너 온도 레벨을 간단히 표시할 때 사용됩니다. ## Preview ```tsx import { VStack } from "@seed-design/react"; import { MannerTempBadge } from "seed-design/ui/manner-temp-badge"; export default function BadgePreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:manner-temp-badge - pnpm: pnpm dlx @seed-design/cli@latest add ui:manner-temp-badge - yarn: yarn dlx @seed-design/cli@latest add ui:manner-temp-badge - bun: bun x @seed-design/cli@latest add ui:manner-temp-badge ## Props \[#props] - `temperature` - type: `number` - required: `true` - description: The manner temperature of the badge. Level will be calculated based on this value. If level is provided, this will be ignored. - `level` - type: `"l1" | "l2" | "l3" | "l4" | "l5" | "l6" | "l7" | "l8" | "l9" | "l10" | undefined` - default: `"l1"` --- file: components/manner-temp.mdx # Manner Temp 사용자의 매너온도를 시각적으로 표현하는 컴포넌트입니다. 신뢰도/매너 정도를 직관적으로 보여주는 데에 사용합니다. ## Preview ```tsx import { VStack } from "@seed-design/react"; import { MannerTemp } from "seed-design/ui/manner-temp"; export default function MannerTempPreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:manner-temp - pnpm: pnpm dlx @seed-design/cli@latest add ui:manner-temp - yarn: yarn dlx @seed-design/cli@latest add ui:manner-temp - bun: bun x @seed-design/cli@latest add ui:manner-temp ## Props \[#props] - `temperature` - type: `number` - required: `true` - description: The manner temperature of the MannerTemp component. Level will be calculated based on this value. If level is provided, this will be ignored. - `level` - type: `"l1" | "l2" | "l3" | "l4" | "l5" | "l6" | "l7" | "l8" | "l9" | "l10" | undefined` - default: `"l1"` --- file: components/menu.mdx # Menu 사용자가 취할 수 있는 선택지나 액션 리스트를 제공하는 컴포넌트입니다. ## Preview ```tsx import { IconPencilLine, IconPlusLine, IconTrashcanLine, } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuContent, MenuGroup, MenuGroupLabel, MenuItem, MenuRoot, MenuTrigger, } from "seed-design/ui/menu"; export default function MenuPreview() { return ( 열기 작업 } /> } /> } /> ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:menu - pnpm: pnpm dlx @seed-design/cli@latest add ui:menu - yarn: yarn dlx @seed-design/cli@latest add ui:menu - bun: bun x @seed-design/cli@latest add ui:menu ## Usage \[#usage] Menu는 아래와 같은 구조로 구성됩니다. ```tsx import { MenuRoot, MenuTrigger, MenuContent, MenuGroup, MenuGroupLabel, MenuItem, } from "seed-design/ui/menu"; ``` ```tsx ... 그룹 제목 ``` - `MenuRoot`: 메뉴의 상태(열림/닫힘)와 위치를 관리합니다. - `MenuTrigger`: 클릭 시 메뉴를 열고 닫는 트리거입니다. - `MenuAnchor`: 메뉴의 위치 기준점만 제공하며, 열고 닫는 동작은 포함하지 않습니다. 트리거 동작을 직접 제어해야 하는 경우에 `MenuTrigger` 대신 사용합니다. - `MenuContent`: 메뉴 항목들을 감싸는 플로팅 컨테이너입니다. - `MenuGroup`: 관련된 항목들을 그룹으로 묶습니다. 모든 `MenuItem`은 `MenuGroup` 안에 있어야 합니다. - `MenuGroupLabel`: 그룹의 제목을 표시합니다. - `MenuItem`: 개별 메뉴 항목입니다. ## Props \[#props] ### `MenuRoot` \[#menuroot] - `size` - type: `"small" | "medium" | "responsive" | undefined` - default: `"medium"` - description: - \`medium\`: 뷰포트 너비와 관계없이 사용할 수 있습니다. - \`small\`: Breakpoint \`lg\` 이상(데스크톱)에서만 사용하고, 모바일에서는 사용하지 않습니다. 정밀한 선택이 가능한 마우스 입력 환경에서 사이즈를 더 작게 만들고자 할 때 사용합니다. - \`responsive\`: 뷰포트 너비에 따라 적용되는 사이즈가 달라집니다. Breakpoint \`lg\` 미만에서는 \`medium\`, \`lg\` 이상에서는 \`small\`로 적용됩니다. - `children` - type: `React.ReactNode` - `disabled` - type: `boolean | undefined` - `placement` - type: `Placement | undefined` - default: `"bottom"` - description: Floating UI placement. - `gutter` - type: `number | undefined` - default: `0` - description: Distance between trigger and floating element. - `overflowPadding` - type: `number | undefined` - default: `8` - description: Virtual padding around viewport edges. - `strategy` - type: `"fixed" | "absolute" | undefined` - default: `"absolute"` - description: Positioning strategy. - `matchReferenceWidth` - type: `boolean | undefined` - default: `false` - description: Whether the floating element's width should match the reference(trigger/anchor)'s width. - `open` - type: `boolean | undefined` - `defaultOpen` - type: `boolean | undefined` - `onOpenChange` - type: `((open: boolean, details?: MenuChangeDetails) => void) | undefined` ### `MenuTrigger` \[#menutrigger] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `MenuAnchor` \[#menuanchor] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `MenuContent` \[#menucontent] - `positionerContainer` - type: `React.RefObject | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `MenuGroup` \[#menugroup] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `MenuGroupLabel` \[#menugrouplabel] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `MenuItem` \[#menuitem] - `prefixIcon` - type: `React.ReactNode` - `label` - type: `React.ReactNode` - required: `true` - `description` - type: `React.ReactNode` - `suffixIcon` - type: `React.ReactNode` - `tone` - type: `"critical" | "neutral" | undefined` - default: `"neutral"` - description: - \`neutral\`: 일반적인 작업을 수행하는 기본 아이템입니다. - \`critical\`: 데이터 삭제와 같이 되돌릴 수 없는 작업을 수행하는 아이템입니다. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `onClick` - type: `React.MouseEventHandler | undefined` - description: Called when the item is activated (click or keyboard). Not called when the item is disabled. - `disabled` - type: `boolean | undefined` - `size` - type: `"small" | "medium" | "responsive" | undefined` - default: `"medium"` - description: - \`medium\`: 뷰포트 너비와 관계없이 사용할 수 있습니다. - \`small\`: Breakpoint \`lg\` 이상(데스크톱)에서만 사용하고, 모바일에서는 사용하지 않습니다. 정밀한 선택이 가능한 마우스 입력 환경에서 사이즈를 더 작게 만들고자 할 때 사용합니다. - \`responsive\`: 뷰포트 너비에 따라 적용되는 사이즈가 달라집니다. Breakpoint \`lg\` 미만에서는 \`medium\`, \`lg\` 이상에서는 \`small\`로 적용됩니다. - `typeaheadLabel` - type: `string | undefined` - description: Overrides the text label to use when the item is matched during keyboard text navigation. Falls back to the element's text content if not provided. ## Examples \[#examples] ### Trigger \[#trigger] ``는 `aria-haspopup="menu"` 속성을 설정하고, Menu의 `open` 상태에 따라 `aria-expanded` 속성을 자동으로 설정합니다. 이 속성은 스크린 리더와 같은 보조 기술에 유용합니다. ## Preview ```tsx import { IconPencilLine, IconPlusLine, IconTrashcanLine, } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuContent, MenuGroup, MenuGroupLabel, MenuItem, MenuRoot, MenuTrigger, } from "seed-design/ui/menu"; export default function MenuPreview() { return ( 열기 작업 } /> } /> } /> ); } ``` ### Size \[#size] `size`로 MenuRoot의 크기를 정합니다. (default: `medium`) `responsive`는 화면 너비에 따라 size가 자동으로 전환되는 값입니다. 여러 화면 너비를 함께 지원하는 제품에서 `size=responsive`를 사용하여 대응합니다. ```tsx import { IconPencilLine, IconPlusLine } from "@karrotmarket/react-monochrome-icon"; import { HStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuContent, MenuGroup, MenuGroupLabel, MenuItem, MenuRoot, MenuTrigger, } from "seed-design/ui/menu"; export default function MenuSize() { return ( Medium 작업 } /> } /> Small 작업 } /> } /> Responsive 작업 } /> } /> ); } ``` ### With Description \[#with-description] `MenuItem`의 `description` prop을 사용하여 항목에 부가 설명을 추가합니다. ```tsx import { IconArrowUpBracketDownLine, IconPencilLine, IconPlusLine, } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuContent, MenuGroup, MenuItem, MenuRoot, MenuTrigger } from "seed-design/ui/menu"; export default function MenuWithDescription() { return ( 열기 } /> } /> } /> ); } ``` ### Tone \[#tone] `MenuItem`의 `tone` prop으로 항목의 톤을 설정합니다. ```tsx import { IconPencilLine, IconPlusLine, IconTrashcanLine, } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuContent, MenuGroup, MenuItem, MenuRoot, MenuTrigger } from "seed-design/ui/menu"; export default function MenuTone() { return ( 열기 } /> } /> } /> ); } ``` ### Disabled Items \[#disabled-items] `MenuItem`의 `disabled` prop을 사용하여 특정 항목을 비활성화합니다. ```tsx import { IconArrowUpBracketDownLine, IconPencilLine, IconPlusLine, } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuContent, MenuGroup, MenuItem, MenuRoot, MenuTrigger } from "seed-design/ui/menu"; export default function MenuDisabled() { return ( 열기 } /> } disabled /> } /> ); } ``` ### Placement \[#placement] `MenuRoot`의 `placement` prop으로 메뉴의 위치를 설정합니다. 기본값은 `"bottom"`입니다. ```tsx import { IconPencilLine, IconPlusLine } from "@karrotmarket/react-monochrome-icon"; import { Box } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuContent, MenuGroup, MenuItem, MenuRoot, MenuTrigger, type MenuRootProps, } from "seed-design/ui/menu"; function PlacementMenu({ placement }: { placement: NonNullable }) { return ( {placement} } /> } /> ); } export default function MenuPlacement() { return (
    ); } ``` ### Anchor \[#anchor] `MenuAnchor`의 `children`은 Menu가 위치를 잡는 데에만 사용되며, `MenuTrigger`와 달리 클릭으로 열고 닫는 동작이 포함되지 않습니다. `open` prop을 controlled하게 사용하여 열리고 닫힌 상태를 직접 관리해야 합니다. 아래 예시의 경우 `onOpenChange` 콜백에 조건을 추가하여 바깥 영역을 클릭한 경우에도 메뉴가 닫히지 않도록 구현되어 있습니다. ```tsx import { Avatar } from "seed-design/ui/avatar"; import { IdentityPlaceholder } from "seed-design/ui/identity-placeholder"; import { IconPencilLine, IconPlusLine } from "@karrotmarket/react-monochrome-icon"; import { useState } from "react"; import { Switch } from "seed-design/ui/switch"; import { MenuAnchor, MenuContent, MenuGroup, MenuItem, MenuRoot } from "seed-design/ui/menu"; import { HStack } from "@seed-design/react"; export default function MenuAnchorExample() { const [open, setOpen] = useState(false); return ( { if (!nextOpen && details?.reason === "interactOutside") return; setOpen(nextOpen); }} > } /> } /> } /> ); } ``` ### Match Width \[#match-width] `MenuRoot`의 `matchReferenceWidth` prop을 사용하면 메뉴의 너비가 reference 요소(Trigger 또는 Anchor)의 너비에 맞춰집니다. ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { MenuContent, MenuGroup, MenuItem, MenuRoot, MenuTrigger } from "seed-design/ui/menu"; export default function MenuMatchReferenceWidth() { return ( 열기 ); } ``` ### `onOpenChange` Details \[#onopenchange-details] `onOpenChange` 두 번째 인자로 `details`가 제공됩니다. #### `reason` \[#reason] **열릴 때** (`open: true`) - `"trigger"`: `MenuTrigger`로 열림 **닫힐 때** (`open: false`) - `"trigger"`: `MenuTrigger`로 닫힘 - `"escapeKeyDown"`: ESC 키 사용 - `"interactOutside"`: 외부 영역 클릭 - `"itemClick"`: 메뉴 항목 클릭 - `"cascadeDismiss"`: 상위 레이어 닫힘으로 인한 연쇄 닫힘 ```tsx import { IconPencilLine, IconPlusLine } from "@karrotmarket/react-monochrome-icon"; import { HStack, Text, VStack } from "@seed-design/react"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { MenuContent, MenuGroup, MenuItem, MenuRoot, MenuTrigger } from "seed-design/ui/menu"; export default function MenuOpenChangeReason() { const [open, setOpen] = useState(false); const [openReason, setOpenReason] = useState(null); const [closeReason, setCloseReason] = useState(null); return ( { setOpen(open); (open ? setOpenReason : setCloseReason)(details?.reason ?? null); }} > 열기 } /> } /> 마지막 열림 이유: {openReason ?? "-"} 마지막 닫힘 이유: {closeReason ?? "-"} ); } ``` --- file: components/page-banner.mdx # Page Banner 페이지 상단에 위치하며 사용자에게 전체적인 상태나 중요한 메시지를 전달하는 상위 레벨 메시지 컴포넌트입니다. ## Preview ```tsx import { VStack } from "@seed-design/react"; import { ActionablePageBanner, DismissiblePageBanner, PageBanner, } from "seed-design/ui/page-banner"; export default function PageBannerPreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:page-banner - pnpm: pnpm dlx @seed-design/cli@latest add ui:page-banner - yarn: yarn dlx @seed-design/cli@latest add ui:page-banner - bun: bun x @seed-design/cli@latest add ui:page-banner ## Props \[#props] ### `PageBanner` \[#pagebanner] - `prefixIcon` - type: `React.ReactNode` - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - required: `true` - `suffix` - type: `React.ReactNode` - `tone` - type: `"critical" | "informative" | "neutral" | "positive" | "warning" | "magic" | undefined` - default: `"neutral"` - description: - \`magic\`: AI 기능을 나타냅니다. variant=solid와 조합하여 사용하지 않습니다. - `variant` - type: `"weak" | "solid" | undefined` - default: `"weak"` - description: - \`weak\`: 배경색이 연한 스타일입니다. - \`solid\`: 배경색이 진한 스타일입니다. ### `PageBannerButton` \[#pagebannerbutton] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `ActionablePageBanner` \[#actionablepagebanner] - `prefixIcon` - type: `React.ReactNode` - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - required: `true` - `tone` - type: `"critical" | "informative" | "neutral" | "positive" | "warning" | "magic" | undefined` - default: `"neutral"` - description: - \`magic\`: AI 기능을 나타냅니다. variant=solid와 조합하여 사용하지 않습니다. - `variant` - type: `"weak" | "solid" | undefined` - default: `"weak"` - description: - \`weak\`: 배경색이 연한 스타일입니다. - \`solid\`: 배경색이 진한 스타일입니다. ### `DismissiblePageBanner` \[#dismissiblepagebanner] - `prefixIcon` - type: `React.ReactNode` - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - required: `true` - `tone` - type: `"critical" | "informative" | "neutral" | "positive" | "warning" | "magic" | undefined` - default: `"neutral"` - description: - \`magic\`: AI 기능을 나타냅니다. variant=solid와 조합하여 사용하지 않습니다. - `variant` - type: `"weak" | "solid" | undefined` - default: `"weak"` - description: - \`weak\`: 배경색이 연한 스타일입니다. - \`solid\`: 배경색이 진한 스타일입니다. - `open` - type: `boolean | undefined` - `defaultOpen` - type: `boolean | undefined` - `onDismiss` - type: `(() => void) | undefined` ## Examples \[#examples] ### With Button \[#with-button] `PageBanner`의 `suffix` prop에 `PageBannerButton`을 전달하여 버튼을 추가할 수 있습니다. ```tsx import { PageBanner, PageBannerButton } from "seed-design/ui/page-banner"; export default function PageBannerWithButton() { return ( 자세히 보기} /> ); } ``` ### Rendering `PageBannerButton` as a Child \[#rendering-pagebannerbutton-as-a-child] ```tsx import { PageBanner, PageBannerButton } from "seed-design/ui/page-banner"; export default function PageBannerButtonAsChild() { return ( 새 탭에서 열기 } /> ); } ``` ### Tones and Variants \[#tones-and-variants] #### Neutral \[#neutral] ```tsx import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { ActionablePageBanner, DismissiblePageBanner, PageBanner, PageBannerButton, } from "seed-design/ui/page-banner"; export default function PageBannerNeutral() { return (
    } title="미노출" description="사업자 정보를 등록해주세요." suffix={등록하기} /> } title="미노출" description="사업자 정보를 등록해주세요." suffix={등록하기} /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." />
    ); } ``` #### Positive \[#positive] ```tsx import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { ActionablePageBanner, DismissiblePageBanner, PageBanner, PageBannerButton, } from "seed-design/ui/page-banner"; export default function PageBannerPositive() { return (
    } title="미노출" description="사업자 정보를 등록해주세요." suffix={등록하기} /> } title="미노출" description="사업자 정보를 등록해주세요." suffix={등록하기} /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." />
    ); } ``` #### Informative \[#informative] ```tsx import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { ActionablePageBanner, DismissiblePageBanner, PageBanner, PageBannerButton, } from "seed-design/ui/page-banner"; export default function PageBannerInformative() { return (
    } title="미노출" description="사업자 정보를 등록해주세요." suffix={등록하기} /> } title="미노출" description="사업자 정보를 등록해주세요." suffix={등록하기} /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." />
    ); } ``` #### Warning \[#warning] ```tsx import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { ActionablePageBanner, DismissiblePageBanner, PageBanner, PageBannerButton, } from "seed-design/ui/page-banner"; export default function PageBannerWarning() { return (
    } title="미노출" description="사업자 정보를 등록해주세요." suffix={등록하기} /> } title="미노출" description="사업자 정보를 등록해주세요." suffix={등록하기} /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." />
    ); } ``` #### Critical \[#critical] ```tsx import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { ActionablePageBanner, DismissiblePageBanner, PageBanner, PageBannerButton, } from "seed-design/ui/page-banner"; export default function PageBannerCritical() { return (
    } title="미노출" description="사업자 정보를 등록해주세요." suffix={등록하기} /> } title="미노출" description="사업자 정보를 등록해주세요." suffix={등록하기} /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." /> } title="미노출" description="사업자 정보를 등록해주세요." />
    ); } ``` #### Magic \[#magic] `tone="magic"`은 `variant="weak"` 조합으로만 사용할 수 있습니다. ```tsx import { IconSparkle2 } from "@karrotmarket/react-multicolor-icon"; import { VStack } from "@seed-design/react"; import { ActionablePageBanner, DismissiblePageBanner, PageBanner, PageBannerButton, } from "seed-design/ui/page-banner"; export default function PageBannerMagic() { return ( } title="새로운 기능" description="마법 같은 소식이 도착했어요!" suffix={둘러보기} /> } title="새로운 기능" description="마법 같은 소식이 도착했어요!" /> } title="새로운 기능" description="마법 같은 소식이 도착했어요!" /> ); } ``` --- file: components/progress-circle.mdx # Progress Circle 작업이 진행 중임을 알리거나 작업 시간을 시각적으로 나타내는 데 사용됩니다. ## Preview ```tsx import { ProgressCircle } from "seed-design/ui/progress-circle"; export default function ProgressCirclePreview() { return ; } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:progress-circle - pnpm: pnpm dlx @seed-design/cli@latest add ui:progress-circle - yarn: yarn dlx @seed-design/cli@latest add ui:progress-circle - bun: bun x @seed-design/cli@latest add ui:progress-circle ## Props \[#props] - `value` - type: `number | undefined` - description: The current value of the progress. if undefined, it will be indeterminate. - `minValue` - type: `number | undefined` - default: `0` - description: The minimum value allowed of the progress. - `maxValue` - type: `number | undefined` - default: `100` - description: The maximum value allowed of the progress. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `tone` - type: `"inherit" | "staticWhite" | "neutral" | "brand" | undefined` - default: `"neutral"` - description: - \`neutral\`: 가장 보편적으로 사용되며 스타일보다는 로딩 상태의 인식이 더 중요한 경우 사용합니다. - \`brand\`: 사용자 경험의 초기 단계에서 브랜드 컬러를 통해 주요 전환점을 강조할 때 사용합니다. - \`staticWhite\`: 화면 전체를 어둡게 덮는 오버레이(Overlay) 위에 로딩 상태를 표시할 때 사용합니다. - `size` - type: `"inherit" | "24" | "40" | undefined` - default: `40` - description: - \`24\`: 특정 요소 안에서 사용하는 경우 사용합니다. - \`40\`: 주로 전체 페이지 로딩에 사용합니다. ## Examples \[#examples] ### Neutral \[#neutral] ```tsx import { ProgressCircle } from "seed-design/ui/progress-circle"; export default function ProgressCircleNeutral() { return ; } ``` ### Brand \[#brand] ```tsx import { ProgressCircle } from "seed-design/ui/progress-circle"; export default function ProgressCircleBrand() { return ; } ``` ### Static White \[#static-white] ```tsx import { ProgressCircle } from "seed-design/ui/progress-circle"; export default function ProgressCircleStaticWhite() { return (
    ); } ``` ### Size=40 \[#size40] ```tsx import { ProgressCircle } from "seed-design/ui/progress-circle"; export default function ProgressCircle40() { return ; } ``` ### Size=24 \[#size24] ```tsx import { ProgressCircle } from "seed-design/ui/progress-circle"; export default function ProgressCircle24() { return ; } ``` ### Determinate \[#determinate] ```tsx import { ProgressCircle } from "seed-design/ui/progress-circle"; export default function ProgressCircleDeterminate() { return ; } ``` ### Indeterminate \[#indeterminate] ```tsx import { ProgressCircle } from "seed-design/ui/progress-circle"; export default function ProgressCirclePreview() { // if you want to show an indeterminate progress circle, you can pass `undefined` or omit the `value` prop return ; } ``` --- file: components/pull-to-refresh.mdx # Pull To Refresh 사용자가 화면을 아래로 당겨 콘텐츠를 새로고침할 수 있게 해주는 컴포넌트입니다. 모바일 환경에서 최신 콘텐츠를 불러올 때 사용됩니다. ```tsx import { AppBar, AppScreen } from "@seed-design/stackflow"; import { type StaticActivityComponentType } from "@stackflow/react/future"; import { VStack } from "@seed-design/react"; import { PullToRefreshContent, PullToRefreshIndicator, PullToRefreshRoot, } from "seed-design/ui/pull-to-refresh"; declare module "@stackflow/config" { interface Register { ActivityPullToRefreshPreview: {}; } } const ActivityPullToRefreshPreview: StaticActivityComponentType< "ActivityPullToRefreshPreview" > = () => { // AppScreen is imported from @seed-design/stackflow instead of snippet for demo purpose. // AppScreen snippet is integrating PullToRefresh, so it's not necessary to use it here. return ( Pull To Refresh {}} onPtrRefresh={async () => { await new Promise((resolve) => setTimeout(resolve, 1000)); }} > Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam autem deserunt reprehenderit ducimus sunt. Quod laudantium excepturi tempora fuga repellendus accusantium nam maiores? Quas debitis, neque ullam eligendi minus sit? ); }; export default ActivityPullToRefreshPreview; ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:pull-to-refresh - pnpm: pnpm dlx @seed-design/cli@latest add ui:pull-to-refresh - yarn: yarn dlx @seed-design/cli@latest add ui:pull-to-refresh - bun: bun x @seed-design/cli@latest add ui:pull-to-refresh ## Props \[#props] ### `PullToRefreshRoot` \[#pulltorefreshroot] - `threshold` - type: `number | undefined` - default: `44` - description: The threshold value to trigger the refresh. (px) - `displacementMultiplier` - type: `number | undefined` - default: `0.5` - description: The multiplier to calculate displacement from the touch movement. - `onPtrPullStart` - type: `((ctx: PullToRefreshContext) => void) | undefined` - description: Callback when the pull-to-refresh has started to pull. - `onPtrPullMove` - type: `((ctx: PullToRefreshContext) => void) | undefined` - description: Callback when the pull-to-refresh is moving during the pull. - `onPtrPullEnd` - type: `((ctx: PullToRefreshContext) => void) | undefined` - description: Callback when the pull-to-refresh is released. It does not matter if it is ready or not. If you want to handle the refresh, use \`onPtrRefresh\`. - `onPtrReady` - type: `(() => void) | undefined` - description: Callback when the pull-to-refresh is pulled over the threshold. - `onPtrRefresh` - type: `(() => Promise) | undefined` - description: Callback when the pull-to-refresh is released after ready. - `disabled` - type: `boolean | undefined` - default: `false` - description: Whether to disable the pull-to-refresh. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `PullToRefreshIndicator` \[#pulltorefreshindicator] ### `PullToRefreshContent` \[#pulltorefreshcontent] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### PTR in Tabs \[#ptr-in-tabs] ```tsx import { VStack } from "@seed-design/react"; import { type StaticActivityComponentType } from "@stackflow/react/future"; import { AppBar, AppBarMain } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { PullToRefreshContent, PullToRefreshIndicator, PullToRefreshRoot, } from "seed-design/ui/pull-to-refresh"; import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; declare module "@stackflow/config" { interface Register { ActivityPullToRefreshTabs: {}; } } const ActivityPullToRefreshTabs: StaticActivityComponentType<"ActivityPullToRefreshTabs"> = () => { return ( Pull To Refresh Tab 1 Tab 2 {}} onPtrRefresh={async () => { await new Promise((resolve) => setTimeout(resolve, 1000)); }} > Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam autem deserunt reprehenderit ducimus sunt. Quod laudantium excepturi tempora fuga repellendus accusantium nam maiores? Quas debitis, neque ullam eligendi minus sit? PTR is not available in this tab. ); }; export default ActivityPullToRefreshTabs; ``` ### Disabled \[#disabled] `disabled` 속성을 사용하여 PTR를 비활성화할 수 있습니다. ```tsx import { Box, HStack, Text } from "@seed-design/react"; import { useState } from "react"; import { PullToRefreshContent, PullToRefreshIndicator, PullToRefreshRoot, } from "seed-design/ui/pull-to-refresh"; import { Switch } from "seed-design/ui/switch"; const PullToRefreshDisabled = () => { const [disabled, setDisabled] = useState(false); return ( {}} onPtrRefresh={async () => { await new Promise((resolve) => setTimeout(resolve, 1000)); }} disabled={disabled} > Disabled ); }; export default PullToRefreshDisabled; ``` ### Prevent Pull \[#prevent-pull] `PullToRefresh.preventPull` 속성을 사용하여 특정 영역에서 PTR 동작을 방지할 수 있습니다. `user-select: auto;`등을 통해 텍스트 선택이 가능한 영역에서 주로 사용됩니다. ```tsx import { AppBar, AppScreen } from "@seed-design/stackflow"; import { type StaticActivityComponentType } from "@stackflow/react/future"; import { VStack, PullToRefresh, Box } from "@seed-design/react"; import { PullToRefreshContent, PullToRefreshIndicator, PullToRefreshRoot, } from "seed-design/ui/pull-to-refresh"; declare module "@stackflow/config" { interface Register { ActivityPullToRefreshPreventPull: {}; } } const ActivityPullToRefreshPreventPull: StaticActivityComponentType< "ActivityPullToRefreshPreventPull" > = () => { // AppScreen is imported from @seed-design/stackflow instead of snippet for demo purpose. // AppScreen snippet is integrating PullToRefresh, so it's not necessary to use it here. return ( Pull To Refresh {}} onPtrRefresh={async () => { await new Promise((resolve) => setTimeout(resolve, 1000)); }} > 이 영역은 당겨서 새로고침이 가능합니다. Amet in laborum proident fugiat mollit quis aute mollit esse nostrud. Excepteur ea proident ipsum duis. Nulla Lorem pariatur exercitation velit anim. 이 영역은 당겨서 새로고침이 불가능합니다. Aliquip ad amet eu dolore id enim excepteur laboris officia anim in. Irure irure nulla sit eiusmod aliqua sint excepteur amet laboris. 이 영역은 당겨서 새로고침이 가능합니다. Amet in laborum proident fugiat mollit quis aute mollit esse nostrud. Excepteur ea proident ipsum duis. Nulla Lorem pariatur exercitation velit anim. 이 영역은 당겨서 새로고침이 불가능합니다. Aliquip ad amet eu dolore id enim excepteur laboris officia anim in. Irure irure nulla sit eiusmod aliqua sint excepteur amet laboris. ); }; export default ActivityPullToRefreshPreventPull; ``` --- file: components/radio-group.mdx # Radio Group 여러 옵션 중 하나를 선택할 수 있도록 할 때 사용하는 컴포넌트입니다. ## Preview ```tsx import { VStack } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; export default function RadioGroupPreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:radio-group - pnpm: pnpm dlx @seed-design/cli@latest add ui:radio-group - yarn: yarn dlx @seed-design/cli@latest add ui:radio-group - bun: bun x @seed-design/cli@latest add ui:radio-group ## Props \[#props] ### `RadioGroup` \[#radiogroup] - `label` - type: `React.ReactNode` - `labelWeight` - type: `"bold" | "medium" | undefined` - default: `"medium"` - `indicator` - type: `React.ReactNode` - `showRequiredIndicator` - type: `boolean | undefined` - `description` - type: `React.ReactNode` - `errorMessage` - type: `React.ReactNode` - `disabled` - type: `boolean | undefined` - default: `false` - `invalid` - type: `boolean | undefined` - default: `false` - `name` - type: `string | undefined` - `form` - type: `string | undefined` - `value` - type: `string | undefined` - `defaultValue` - type: `string | undefined` - `onValueChange` - type: `((value: string) => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `RadioGroupItem` \[#radiogroupitem] - `label` - type: `React.ReactNode` - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `rootRef` - type: `React.Ref | undefined` - `weight` - type: `"bold" | "regular" | undefined` - default: `"regular"` - `size` - type: `"medium" | "large" | undefined` - default: `"medium"` - `tone` - type: `"brand" | "neutral" | undefined` - default: `"brand"` - `value` - type: `string` - required: `true` - `disabled` - type: `boolean | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `Radiomark` \[#radiomark] - `tone` - type: `"brand" | "neutral" | undefined` - default: `"brand"` - `size` - type: `"medium" | "large" | undefined` - default: `"medium"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Sizes \[#sizes] ```tsx import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; import { VStack } from "@seed-design/react"; export default function RadioGroupSize() { return ( ); } ``` ### Tones \[#tones] #### Brand \[#brand] ```tsx import { VStack } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; export default function RadioGroupBrand() { return ( ); } ``` #### Neutral \[#neutral] ```tsx import { VStack } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; export default function RadioGroupNeutral() { return ( ); } ``` ### Weights \[#weights] ```tsx import { VStack } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; export default function RadioGroupWeights() { return ( ); } ``` ### Long Label \[#long-label] ```tsx import { VStack } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; export default function RadioGroupLongLabel() { return ( ); } ``` ### Disabled \[#disabled] ```tsx import { VStack } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; export default function RadioGroupDisabled() { return ( ); } ``` ### Listening to Value Changes \[#listening-to-value-changes] `RadioGroup`의 `onValueChange`를 사용하여 라디오 버튼의 선택 값 변경을 감지할 수 있습니다. 이벤트를 활용해야 하는 경우 `RadioGroupItem`의 `inputProps`를 통해 내부 `` 요소에 직접 이벤트 핸들러를 추가할 수 있습니다. ```tsx import { VStack, Text } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; import { useState } from "react"; export default function RadioGroupValueChanges() { const [count, setCount] = useState(0); const [lastValue, setLastValue] = useState(null); return ( { setCount((prev) => prev + 1); setLastValue(value); }} > onValueChange called: {count} times, last value: {lastValue ?? "-"} ); } ``` ### Use Cases \[#use-cases] #### React Hook Form \[#react-hook-form] ```tsx import { HStack, VStack } from "@seed-design/react"; import { useCallback, type FormEvent } from "react"; import { useController, useForm } from "react-hook-form"; import { ActionButton } from "seed-design/ui/action-button"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; const POSSIBLE_COLORS = ["red", "blue", "green"] as const; interface FormValues { color: (typeof POSSIBLE_COLORS)[number]; } export default function RadioGroupReactHookForm() { const { handleSubmit, reset, setValue, control } = useForm({ defaultValues: { color: "blue", }, }); const { field } = useController({ name: "color", control }); const onValid = useCallback((data: FormValues) => { window.alert(JSON.stringify(data, null, 2)); }, []); const onReset = useCallback( (event: FormEvent) => { event.preventDefault(); reset(); }, [reset], ); return ( setValue("color", value as FormValues["color"])} aria-label="Color selection" > {POSSIBLE_COLORS.map((color) => ( ))} Submit Reset ); } ``` #### Using `Radiomark` \[#using-radiomark] `Radiomark`는 독립적인 라디오 마크 컴포넌트로, Radio Group Primitive 컴포넌트와 조합하여 커스텀 레이아웃을 위해 사용할 수 있습니다. ```tsx import { HStack, Text, VStack } from "@seed-design/react"; import { Radiomark } from "seed-design/ui/radio-group"; import { RadioGroup } from "@seed-design/react/primitive"; function CustomRadioGroupItem({ children, ...props }: RadioGroup.ItemProps) { return ( {children} ); } export default function RadioGroupRadiomark() { return ( regular medium bold ); } ``` ### RadioGroupField Integration \[#radiogroupfield-integration] `label`, `description`, `errorMessage` 등의 RadioGroupField 관련 prop을 사용할 수 있습니다. ```tsx import { ActionButton, HStack, VStack } from "@seed-design/react"; import { useState } from "react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; export default function RadioGroupRadioGroupField() { const [firstErrorMessage, setFirstErrorMessage] = useState(); const [secondErrorMessage, setSecondErrorMessage] = useState(); const handleFirstSubmit = (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget); const contact = formData.get("contact"); if (contact === "email") { setFirstErrorMessage("이메일은 선택할 수 없습니다."); return; } setFirstErrorMessage(undefined); alert(JSON.stringify({ contact }, null, 2)); }; const handleSecondSubmit = (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget); const option = formData.get("option"); if (option === "option1") { setSecondErrorMessage("옵션 1은 선택할 수 없습니다."); return; } setSecondErrorMessage(undefined); alert(JSON.stringify({ option }, null, 2)); }; return (
    제출
    제출
    ); } ``` --- file: components/reaction-button.mdx # Reaction Button 사용자가 콘텐츠에 대한 반응을 표현할 수 있게 해주는 컴포넌트입니다. 좋아요, 관심있어요 등의 감정적 피드백을 간편하게 제공할 때 사용됩니다. ## Preview ```tsx import { IconFaceSmileCircleFill } from "@karrotmarket/react-monochrome-icon"; import { Count, PrefixIcon } from "@seed-design/react"; import { ReactionButton } from "seed-design/ui/reaction-button"; export default function ReactionButtonPreview() { return ( } /> 도움돼요 1 ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:reaction-button - pnpm: pnpm dlx @seed-design/cli@latest add ui:reaction-button - yarn: yarn dlx @seed-design/cli@latest add ui:reaction-button - bun: bun x @seed-design/cli@latest add ui:reaction-button ## Props \[#props] - `size` - type: `"small" | "xsmall" | undefined` - default: `"small"` - `loading` - type: `boolean | undefined` - default: `false` - description: 버튼에 등록된 비동기 작업이 진행 중임을 사용자에게 알립니다. - `disabled` - type: `boolean | undefined` - default: `false` - description: 버튼의 비활성화 여부를 나타냅니다. - `pressed` - type: `boolean | undefined` - `defaultPressed` - type: `boolean | undefined` - `onPressedChange` - type: `((pressed: boolean) => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Small \[#small] ```tsx import { IconFaceSmileCircleFill } from "@karrotmarket/react-monochrome-icon"; import { Count, PrefixIcon } from "@seed-design/react"; import { ReactionButton } from "seed-design/ui/reaction-button"; export default function ReactionButtonSmall() { return ( } /> 도움돼요 1 ); } ``` ### Xsmall \[#xsmall] ```tsx import { IconFaceSmileCircleFill } from "@karrotmarket/react-monochrome-icon"; import { Count, PrefixIcon } from "@seed-design/react"; import { ReactionButton } from "seed-design/ui/reaction-button"; export default function ReactionButtonXsmall() { return ( } /> 도움돼요 1 ); } ``` ### Disabled \[#disabled] ```tsx import { IconFaceSmileCircleFill } from "@karrotmarket/react-monochrome-icon"; import { PrefixIcon } from "@seed-design/react"; import { ReactionButton } from "seed-design/ui/reaction-button"; export default function ReactionButtonDisabled() { return ( } /> 비활성 ); } ``` ### Loading \[#loading] ```tsx import { IconFaceSmileCircleFill } from "@karrotmarket/react-monochrome-icon"; import { PrefixIcon } from "@seed-design/react"; import { useState } from "react"; import { ReactionButton } from "seed-design/ui/reaction-button"; export default function ReactionButtonLoading() { const [pressed, setPressed] = useState(false); const [loading, setLoading] = useState(false); function handleToggle() { setLoading(true); setTimeout(() => { setLoading(false); setPressed((prev) => !prev); }, 2000); } // 이벤트 핸들링이 필요할 수 있으므로 loading은 disabled를 포함하지 않습니다. 이벤트 발생을 원하지 않는 경우, disabled 속성을 추가해주세요. return ( } /> 시간이 걸리는 토글 ); } ``` --- file: components/result-section.mdx # Result Section 데이터 로딩 결과, 사용자의 액션 완료 여부 등 사용자에 액션에 대한 결과를 제공하는 템플릿입니다. 주로 전체 화면이나 특정 영역을 차지하여 다음 액션을 유도하거나 현재 상황을 안내하는 역할을 합니다. ## Preview ```tsx import { IconDiamond } from "@karrotmarket/react-multicolor-icon"; import { VStack, Icon, Box } from "@seed-design/react"; import { ResultSection } from "seed-design/ui/result-section"; export default function ResultSectionPreview() { return ( } size="x10" /> } title="결과 타이틀" description="부가 설명을 적어주세요" primaryActionProps={{ children: "Primary Action", onClick: () => window.alert("Primary Action Clicked"), }} secondaryActionProps={{ children: "Secondary Action", onClick: () => window.alert("Secondary Action Clicked"), }} /> ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:result-section - pnpm: pnpm dlx @seed-design/cli@latest add ui:result-section - yarn: yarn dlx @seed-design/cli@latest add ui:result-section - bun: bun x @seed-design/cli@latest add ui:result-section ## Props \[#props] - `size` - type: `"medium" | "large" | undefined` - default: `"large"` - `asset` - type: `React.ReactNode` - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `primaryActionProps` - type: `ActionButtonProps | undefined` - `secondaryActionProps` - type: `ActionButtonProps | undefined` ## Examples \[#examples] ### Sizes \[#sizes] #### Large \[#large] ```tsx import { VStack } from "@seed-design/react"; import { ResultSection } from "seed-design/ui/result-section"; export default function ResultSectionLarge() { return ( ); } ``` #### Medium \[#medium] ```tsx import { VStack } from "@seed-design/react"; import { ResultSection } from "seed-design/ui/result-section"; export default function ResultSectionMedium() { return ( ); } ``` ### With CTA & Progress Circle \[#with-cta--progress-circle] ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { AppBar, AppBarMain } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { ProgressCircle } from "seed-design/ui/progress-circle"; import { ResultSection } from "seed-design/ui/result-section"; import { IconExclamationmarkCircleFill } from "@karrotmarket/react-monochrome-icon"; import { Box, Flex, Icon, VStack } from "@seed-design/react"; import { type StaticActivityComponentType } from "@stackflow/react/future"; import { useEffect, useState, type ComponentProps } from "react"; declare module "@stackflow/config" { interface Register { ActivityResultSectionCtaProgressCircle: {}; } } type RefundStatus = "in-progress" | "failed"; const resultSectionProperties = { "in-progress": { title: "환불을 요청하고 있어요", description: "잠시만 기다려주세요", asset: ( ), }, failed: { title: "다시 시도해주세요", description: "환불 요청에 실패했어요", asset: ( } size="x10" color="fg.critical" /> ), }, } satisfies Record>; const ActivityResultSectionCtaProgressCircle: StaticActivityComponentType< "ActivityResultSectionCtaProgressCircle" > = () => { const [refundStatus, setRefundStatus] = useState("in-progress"); useEffect(() => { const timer = setTimeout(() => setRefundStatus("failed"), 3000); return () => clearTimeout(timer); }, []); return ( { setRefundStatus("in-progress"); setTimeout(() => setRefundStatus("failed"), 3000); }} > 다시 시도 ); }; export default ActivityResultSectionCtaProgressCircle; ``` ### Success Example \[#success-example] Result Section 에셋 영역에서 다크 모드와 라이트 모드에서 서로 다른 Lottie 애니메이션을 사용하는 예시입니다. [클라이언트에서 현재 테마 정보 감지하는 방법](/react/getting-started/styling/theming#클라이언트에서-현재-테마-정보-감지하기)을 참고하여 테마에 맞는 Lottie 애니메이션을 Result Section에 적용할 수 있습니다. ```tsx import dynamic from "next/dynamic"; import { VStack, Box } from "@seed-design/react"; import { ResultSection } from "seed-design/ui/result-section"; import { useTheme } from "@/hooks/useTheme"; const Player = dynamic(() => import("@lottiefiles/react-lottie-player").then((mod) => mod.Player), { ssr: false, }); const LOTTIE_URLS = { light: "https://asset-town.krrt.io/production/motion/bd9f3c71-5b81-40b0-8eea-eeebd668edae/c17fa891bb007b9e4e6b281e483b5491cb905703.json", dark: "https://asset-town.krrt.io/production/motion/19bf4654-5286-4def-a651-c674a20ce1ee/89c9e404edc356cf143dab80b627fde01ed8a8fb.json", }; export default function ResultSectionSuccessWithLottie() { const { userColorScheme } = useTheme(); const lottieUrl = LOTTIE_URLS[userColorScheme]; return ( } title="성공했어요" description="요청이 성공적으로 완료되었습니다" primaryActionProps={{ children: "확인", onClick: () => window.alert("확인 클릭"), }} /> ); } ``` --- file: components/scroll-fog.mdx # Scroll Fog 스크롤 가능한 영역에서 사용자에게 추가 콘텐츠가 있음을 시각적으로 알려주는 힌트 역할을 합니다. ## Preview ```tsx import { Box, ScrollFog, VStack } from "@seed-design/react"; export default function ScrollFogPreview() { return (
    {Array.from({ length: 20 }, (_, i) => ( {i + 1} ))}
    ); } ``` 해당 Scroll Fog 컴포넌트는 [Chakra ScrollArea](https://chakra-ui.com/docs/components/scroll-area), [Radix ScrollArea](https://www.radix-ui.com/primitives/docs/components/scroll-area), [Base UI ScrollArea](https://base-ui.com/react/components/scroll-area) 보다는 더 간단한 스크롤 힌트만을 위한 컴포넌트입니다. 이후 스크롤바에 대한 기능이 추가적으로 필요하다면 확장될 수 있습니다. ## Usage \[#usage] ```tsx import { ScrollFog } from "@seed-design/react"; ``` ```tsx
    {/* 스크롤 가능한 콘텐츠 */}
    Content
    ``` ## Props \[#props] ### Scroll Fog \[#scroll-fog] - `placement` - type: `ScrollPlacement[] | undefined` - default: `["top", "bottom"]` - description: Placement of fog effect - `size` - type: `number | undefined` - default: `20` - description: Size of the fog effect in pixels - `sizes` - type: `SizesConfig | undefined` - description: Size of the fog effect for each direction in pixels - `hideScrollBar` - type: `boolean | undefined` - default: `false` ## Examples \[#examples] ### Padding \[#padding] ScrollFog는 항상 fog 효과를 표시하므로, 지정된 방향에 충분한 padding이 필요합니다. - **최소 padding**: 20px을 유지해야 합니다. - **화면 전체 차지하는 세로 스크롤**: 하단 80px, 상단 20px padding 권장 - **가로 스크롤**: 20px 좌우 padding 권장 ```tsx // 상하 스크롤 - 상하 padding 필요
    Content
    // 좌우 스크롤 - 좌우 padding 필요
    Content
    ``` - [Bottom Sheet - 하단 80px, 상단 20px padding](/react/components/bottom-sheet#with-scroll-fog) - [Chip Tabs - 20px 좌우 padding](/react/components/chip-tabs#with-scroll-fog) ### Horizontal Scroll \[#horizontal-scroll] 가로 스크롤이 필요한 경우 `placement={["left", "right"]}`를 사용하여 좌우 fog 효과를 적용할 수 있습니다. 좌우 20px의 padding을 제공하여 컨텐츠가 잘리지 않도록 해야 합니다. ```tsx import { HStack, ScrollFog } from "@seed-design/react"; export default function ScrollFogHorizontal() { return (
    {Array.from({ length: 15 }, (_, i) => (
    {i + 1}
    ))}
    ); } ``` ### Size \[#size] `size` 속성을 이용해 스크롤 효과의 크기를 조절할 수 있습니다. `sizes` 속성을 이용해 각 방향별 크기를 조절할 수 있습니다. ```tsx import { ScrollFog } from "@seed-design/react"; export default function ScrollFogSize() { return (
    {Array.from({ length: 100 }, (_, i) => (
    {i + 1}
    ))}
    ); } ``` ```tsx import { ScrollFog } from "@seed-design/react"; export default function ScrollFogSize() { return (
    {Array.from({ length: 100 }, (_, i) => (
    {i + 1}
    ))}
    ); } ``` ### All Directions \[#all-directions] 2D 스크롤이 필요한 경우 `placement={["top", "bottom", "left", "right"]}`를 사용하여 모든 방향에 fog 효과를 적용할 수 있습니다. ```tsx import { ScrollFog } from "@seed-design/react"; export default function ScrollFogAllDirections() { return (
    {Array.from({ length: 100 }, (_, i) => (
    {i + 1}
    ))}
    ); } ``` ### Hide Scrollbar \[#hide-scrollbar] `hideScrollBar` 속성을 이용해 스크롤바를 숨길 수 있습니다. ```tsx import { ScrollFog } from "@seed-design/react"; export default function ScrollFogHideScrollbar() { return (
    {Array.from({ length: 100 }, (_, i) => (
    {i + 1}
    ))}
    ); } ``` --- file: components/segmented-control.mdx # Segmented Control 여러 옵션 중 하나를 선택하여 관련 콘텐츠를 즉시 필터링하거나 전환할 때 사용하는 컴포넌트입니다. ## Preview ```tsx import { SegmentedControl, SegmentedControlItem } from "seed-design/ui/segmented-control"; export default function SegmentedControlPreview() { return ( Hot New ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:segmented-control - pnpm: pnpm dlx @seed-design/cli@latest add ui:segmented-control - yarn: yarn dlx @seed-design/cli@latest add ui:segmented-control - bun: bun x @seed-design/cli@latest add ui:segmented-control ## Props \[#props] ### `SegmentedControl` \[#segmentedcontrol] `value`와 `defaultValue` 중 적어도 하나를 제공해야 합니다. - `disabled` - type: `boolean | undefined` - `name` - type: `string | undefined` - `form` - type: `string | undefined` - `value` - type: `string | undefined` - `defaultValue` - type: `string | undefined` - `onValueChange` - type: `((value: string) => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `SegmentedControlItem` \[#segmentedcontrolitem] - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `rootRef` - type: `React.Ref | undefined` - `notification` - type: `boolean | undefined` - `value` - type: `string` - required: `true` - `disabled` - type: `boolean | undefined` - `invalid` - type: `boolean | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Disabled \[#disabled] ```tsx import { VStack } from "@seed-design/react"; import { SegmentedControl, SegmentedControlItem } from "seed-design/ui/segmented-control"; export default function SegmentedControlPreview() { return ( Hot New Marinara Alfredo Pesto Carbonara Bolognese ); } ``` ### Notification \[#notification] ```tsx import { ActionButton, VStack } from "@seed-design/react"; import { useState } from "react"; import { SegmentedControl, SegmentedControlItem } from "seed-design/ui/segmented-control"; export default function SegmentedControlNotification() { const [sortBy, setSortBy] = useState("monthly"); const [hasSeenAnnual, setHasSeenAnnual] = useState(false); return ( { setSortBy(value); if (value === "annual") setHasSeenAnnual(true); }} > Monthly Annual Enterprise Custom setHasSeenAnnual(false)} > Reset Notification ); } ``` ### Long Label \[#long-label] ```tsx import { SegmentedControl, SegmentedControlItem } from "seed-design/ui/segmented-control"; export default function SegmentedControlLongLabel() { return ( 가격 높은 순 할인율 높은 순 인기 많은 순 ); } ``` ### Fixed Width \[#fixed-width] `SegmentedControl`의 `style` prop에 `width`를 제공해서 직접 너비를 설정할 수 있습니다. ```tsx import { VStack } from "@seed-design/react"; import { SegmentedControl, SegmentedControlItem } from "seed-design/ui/segmented-control"; export default function SegmentedControlFixedWidth() { return ( New Hot One Way Trip Round Trip Multi-City Journey ); } ``` ### Listening to Value Changes \[#listening-to-value-changes] `SegmentedControl`의 `onValueChange` prop을 사용하여 선택 값 변경을 감지할 수 있습니다. 이벤트를 활용해야 하는 경우 `SegmentedControlItem`의 `inputProps`를 통해 내부 `` 요소에 직접 이벤트 핸들러를 추가할 수 있습니다. ```tsx import { VStack, Text } from "@seed-design/react"; import { SegmentedControl, SegmentedControlItem } from "seed-design/ui/segmented-control"; import { useState } from "react"; export default function SegmentedControlValueChanges() { const [count, setCount] = useState(0); const [lastValue, setLastValue] = useState(null); return ( { setCount((prev) => prev + 1); setLastValue(value); }} > Hot New onValueChange called: {count} times, last value: {lastValue ?? "-"} ); } ``` --- file: components/select-box.mdx # Select Box 명확한 테두리를 가진 컨테이너를 활용하여, 정의된 목록 중 하나 이상의 옵션을 선택하는 UI 요소입니다. ## Preview ```tsx import { HStack } from "@seed-design/react"; import { CheckSelectBox, CheckSelectBoxCheckmark, CheckSelectBoxGroup, RadioSelectBoxItem, RadioSelectBoxRadiomark, RadioSelectBoxRoot, } from "seed-design/ui/select-box"; export default function SelectBoxPreview() { return ( } /> } /> } /> } /> } /> } /> ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:select-box - pnpm: pnpm dlx @seed-design/cli@latest add ui:select-box - yarn: yarn dlx @seed-design/cli@latest add ui:select-box - bun: bun x @seed-design/cli@latest add ui:select-box ## Props \[#props] ### Check Select Box \[#check-select-box] #### `CheckSelectBoxGroup` \[#checkselectboxgroup] - `label` - type: `React.ReactNode` - `labelWeight` - type: `"bold" | "medium" | undefined` - default: `"medium"` - `indicator` - type: `React.ReactNode` - `showRequiredIndicator` - type: `boolean | undefined` - `description` - type: `React.ReactNode` - `errorMessage` - type: `React.ReactNode` - `columns` - type: `number | undefined` - default: `1` - description: Number of columns in the grid layout. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. #### `CheckSelectBox` \[#checkselectbox] - `label` - type: `React.ReactNode` - required: `true` - `description` - type: `React.ReactNode` - `prefixIcon` - type: `React.ReactNode` - `suffix` - type: `React.ReactNode` - `footer` - type: `React.ReactNode` - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `rootRef` - type: `React.Ref | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `defaultChecked` - type: `boolean | undefined` - `disabled` - type: `boolean | undefined` - `layout` - type: `"horizontal" | "vertical" | undefined` - default: `"horizontal"` - `invalid` - type: `boolean | undefined` - `required` - type: `boolean | undefined` - `checked` - type: `boolean | undefined` - `onCheckedChange` - type: `((checked: boolean) => void) | undefined` - `indeterminate` - type: `boolean | undefined` - `footerVisibility` - type: `"when-selected" | "when-not-selected" | "always" | undefined` - default: `"when-selected"` - description: Controls when the footer is visible. #### `CheckSelectBoxCheckmark` \[#checkselectboxcheckmark] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### Radio Select Box \[#radio-select-box] #### `RadioSelectBoxRoot` \[#radioselectboxroot] - `label` - type: `React.ReactNode` - `labelWeight` - type: `"bold" | "medium" | undefined` - default: `"medium"` - `indicator` - type: `React.ReactNode` - `showRequiredIndicator` - type: `boolean | undefined` - `description` - type: `React.ReactNode` - `errorMessage` - type: `React.ReactNode` - `columns` - type: `number | undefined` - default: `1` - description: Number of columns in the grid layout. - `disabled` - type: `boolean | undefined` - default: `false` - `invalid` - type: `boolean | undefined` - default: `false` - `name` - type: `string | undefined` - `form` - type: `string | undefined` - `value` - type: `string | undefined` - `defaultValue` - type: `string | undefined` - `onValueChange` - type: `((value: string) => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. #### `RadioSelectBoxItem` \[#radioselectboxitem] - `label` - type: `React.ReactNode` - required: `true` - `description` - type: `React.ReactNode` - `prefixIcon` - type: `React.ReactNode` - `suffix` - type: `React.ReactNode` - `footer` - type: `React.ReactNode` - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `rootRef` - type: `React.Ref | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `disabled` - type: `boolean | undefined` - `value` - type: `string` - required: `true` - `layout` - type: `"horizontal" | "vertical" | undefined` - default: `"horizontal"` - `footerVisibility` - type: `"when-selected" | "when-not-selected" | "always" | undefined` - default: `"when-selected"` - description: Controls when the footer is visible. #### `RadioSelectBoxRadiomark` \[#radioselectboxradiomark] - `tone` - type: `"brand" | "neutral" | undefined` - default: `"brand"` - `size` - type: `"medium" | "large" | undefined` - default: `"medium"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### React Hook Form \[#react-hook-form] ```tsx import { HStack, VStack } from "@seed-design/react"; import { useCallback, type FormEvent } from "react"; import { useController, useForm, type Control } from "react-hook-form"; import { ActionButton } from "seed-design/ui/action-button"; import { CheckSelectBox, CheckSelectBoxCheckmark, CheckSelectBoxGroup, RadioSelectBoxItem, RadioSelectBoxRadiomark, RadioSelectBoxRoot, } from "seed-design/ui/select-box"; const POSSIBLE_FRUIT_VALUES = ["apple", "melon", "mango"] as const; type CheckFormValues = Record<(typeof POSSIBLE_FRUIT_VALUES)[number], boolean>; interface RadioFormValues { fruit: (typeof POSSIBLE_FRUIT_VALUES)[number]; } export default function SelectBoxReactHookForm() { // CheckSelectBox Form const checkForm = useForm({ defaultValues: { apple: false, melon: true, mango: false }, }); // RadioSelectBox Form const radioForm = useForm({ defaultValues: { fruit: "melon" }, }); const { field: radioField } = useController({ name: "fruit", control: radioForm.control }); const onCheckValid = useCallback((data: CheckFormValues) => { window.alert(`CheckSelectBox:\n${JSON.stringify(data, null, 2)}`); }, []); const onRadioValid = useCallback((data: RadioFormValues) => { window.alert(`RadioSelectBox:\n${JSON.stringify(data, null, 2)}`); }, []); const onCheckReset = useCallback( (event: FormEvent) => { event.preventDefault(); checkForm.reset(); }, [checkForm], ); const onRadioReset = useCallback( (event: FormEvent) => { event.preventDefault(); radioForm.reset(); }, [radioForm], ); return ( {POSSIBLE_FRUIT_VALUES.map((name) => ( ))} 초기화 제출 {POSSIBLE_FRUIT_VALUES.map((value) => ( } /> ))} 초기화 제출 ); } interface CheckSelectBoxItemProps { name: keyof CheckFormValues; control: Control; } function CheckSelectBoxItem({ name, control }: CheckSelectBoxItemProps) { const { field: { value, ...restProps }, fieldState: { invalid }, } = useController({ name, control }); return ( } /> ); } ``` ### Customizing Label \[#customizing-label] ```tsx import { Badge, HStack } from "@seed-design/react"; import { CheckSelectBox, CheckSelectBoxCheckmark, CheckSelectBoxGroup, RadioSelectBoxItem, RadioSelectBoxRadiomark, RadioSelectBoxRoot, } from "seed-design/ui/select-box"; export default function SelectBoxCustomizingLabel() { return ( } /> Melon New } description="Elit cupidatat dolore fugiat enim veniam culpa." suffix={} /> } /> } /> Melon New } description="Elit cupidatat dolore fugiat enim veniam culpa." suffix={} /> } /> ); } ``` ### Listening to Value Changes \[#listening-to-value-changes] `CheckSelectBox`는 `onCheckedChange`를 사용하여 체크박스의 선택 상태 변경을 감지할 수 있습니다. `RadioSelectBoxRoot`는 `onValueChange`를 사용하여 라디오 버튼의 선택 값 변경을 감지할 수 있습니다. ```tsx import { HStack, Text, VStack } from "@seed-design/react"; import { CheckSelectBox, CheckSelectBoxCheckmark, CheckSelectBoxGroup, RadioSelectBoxItem, RadioSelectBoxRadiomark, RadioSelectBoxRoot, } from "seed-design/ui/select-box"; import { useState } from "react"; export default function SelectBoxValueChanges() { const [checkCount, setCheckCount] = useState(0); const [checkLastValue, setCheckLastValue] = useState(null); const [radioCount, setRadioCount] = useState(0); const [radioLastValue, setRadioLastValue] = useState(null); return ( } onCheckedChange={(checked) => { setCheckCount((prev) => prev + 1); setCheckLastValue(checked); }} /> onCheckedChange called: {checkCount} times, last value: {`${checkLastValue ?? "-"}`} { setRadioCount((prev) => prev + 1); setRadioLastValue(value); }} > } /> } /> onValueChange called: {radioCount} times, last value: {radioLastValue ?? "-"} ); } ``` ### Grid Layout (Columns) \[#grid-layout-columns] `columns` prop을 사용하여 여러 열로 배치할 수 있습니다. `columns`가 1보다 크면 하위 요소의 `layout`이 자동으로 `"vertical"`로 설정됩니다. 필요한 경우 `layout` prop을 직접 설정하여 개별 항목의 레이아웃을 오버라이드할 수 있습니다. ```tsx import { IconDiamond, IconIcecreamcone } from "@karrotmarket/react-multicolor-icon"; import { VStack } from "@seed-design/react"; import { CheckSelectBox, CheckSelectBoxCheckmark, CheckSelectBoxGroup, RadioSelectBoxItem, RadioSelectBoxRadiomark, RadioSelectBoxRoot, } from "seed-design/ui/select-box"; export default function SelectBoxColumns() { return ( } label="옵션 1" description="layout=vertical" suffix={} /> } label="옵션 2" description="layout=vertical" suffix={} /> } defaultChecked layout="horizontal" label="layout=horizontal" description="layout을 horizontal로 오버라이드" suffix={} /> } label="옵션 4" description="layout=vertical" suffix={} /> } label="옵션 1" suffix={} /> } label="옵션 2" suffix={} /> } label="layout=horizontal" description="layout을 horizontal로 오버라이드" layout="horizontal" suffix={} /> } label="옵션 4" suffix={} /> } label="옵션 5" suffix={} /> } label="옵션 6" suffix={} /> ); } ``` ### With Suffix \[#with-suffix] `suffix` prop을 사용하여 체크마크, 라디오 마크, 또는 커스텀 요소를 표시할 수 있습니다. `CheckSelectBoxCheckmark`와 `RadioSelectBoxRadiomark`를 사용하거나, 아이콘이나 텍스트 등 자유로운 요소를 전달할 수 있습니다. ```tsx import { IconPersonCircleLine } from "@karrotmarket/react-monochrome-icon"; import { Text, HStack, Box } from "@seed-design/react"; import { CheckSelectBox, CheckSelectBoxCheckmark, CheckSelectBoxGroup, RadioSelectBoxItem, RadioSelectBoxRadiomark, RadioSelectBoxRoot, } from "seed-design/ui/select-box"; export default function SelectBoxWithSuffix() { return ( } /> +1,000원 } /> } /> } /> +1,000원 } /> } /> ); } ``` ### Collapsible Footer \[#collapsible-footer] `footer` prop으로 추가 콘텐츠를 표시할 수 있습니다. `footerVisibility` prop으로 footer의 표시 조건을 제어할 수 있습니다. - `"when-selected"` (기본값): 항목이 선택되었을 때만 표시 - `"when-not-selected"`: 항목이 선택되지 않았을 때만 표시 - `"always"`: 항상 표시 ```tsx import { Box, HStack, Text } from "@seed-design/react"; import { CheckSelectBox, CheckSelectBoxCheckmark, CheckSelectBoxGroup, RadioSelectBoxItem, RadioSelectBoxRadiomark, RadioSelectBoxRoot, } from "seed-design/ui/select-box"; export default function SelectBoxCollapsibleFooter() { return ( } footer={ 선택되었을 때만 보입니다. } /> } footerVisibility="always" footer={ 항상 보입니다. } /> } footerVisibility="when-not-selected" footer={ 선택되지 않았을 때만 보입니다. } /> } footer={ 선택되었을 때만 보입니다. } /> } footerVisibility="always" footer={ 항상 보입니다. } /> } footerVisibility="when-not-selected" footer={ 선택되지 않았을 때만 보입니다. } /> ); } ``` ### Fieldset/RadioGroupField Integration \[#fieldsetradiogroupfield-integration] Fieldset/RadioGroupField 관련 prop을 사용할 수 있습니다. - `label` 및 `labelWeight` - `indicator` 및 `showRequiredIndicator` - `description` 및 `errorMessage` - `disabled`, `invalid`, `name`, `form`: `RadioSelectBoxRoot`에만 지원됩니다. ```tsx import { ActionButton, HStack, VStack, Box, Text } from "@seed-design/react"; import { useState } from "react"; import { CheckSelectBox, CheckSelectBoxCheckmark, CheckSelectBoxGroup, RadioSelectBoxItem, RadioSelectBoxRadiomark, RadioSelectBoxRoot, } from "seed-design/ui/select-box"; export default function SelectBoxFieldset() { const [checkErrors, setCheckErrors] = useState>({}); const [radioErrorMessage, setRadioErrorMessage] = useState(); const handleCheckSubmit = (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget); const fruits = formData.getAll("fruit"); if (fruits.includes("apple")) { setCheckErrors({ apple: "Apple은 선택할 수 없습니다." }); return; } setCheckErrors({}); alert(JSON.stringify(fruits, null, 2)); }; const handleRadioSubmit = (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget); const color = formData.get("color"); if (color === "red") { setRadioErrorMessage("Red는 선택할 수 없습니다."); return; } setRadioErrorMessage(undefined); alert(JSON.stringify({ color }, null, 2)); }; return (
    } footer={ Apple을 선택하고 제출하면 에러 메시지가 표시됩니다. } /> } /> } /> 제출
    } footer={ Red를 선택하고 제출하면 에러 메시지가 표시됩니다. } /> } disabled /> } /> 제출
    ); } ``` --- file: components/side-panel.mdx # Side Panel 화면 좌측 또는 우측 가장자리에서 슬라이드하여 나타나는 패널 컴포넌트입니다. 데스크탑 환경에서 추가 콘텐츠나 네비게이션을 제공할 때 사용됩니다. ## Preview ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { SidePanelBody, SidePanelContent, SidePanelFooter, SidePanelRoot, SidePanelTrigger, } from "seed-design/ui/side-panel"; const SidePanelPreview = () => { return ( Open Side Panel 패널 본문에는 사용자가 확인해야 할 내용이나 추가 입력 폼을 배치할 수 있습니다. 확인 ); }; export default SidePanelPreview; ``` ## Installation \[#installation] ### Default \[#default] 좌/우 가장자리에서 슬라이드되는 기본 Side Panel 컴포넌트를 포함합니다. - npm: npx @seed-design/cli@latest add ui:side-panel - pnpm: pnpm dlx @seed-design/cli@latest add ui:side-panel - yarn: yarn dlx @seed-design/cli@latest add ui:side-panel - bun: bun x @seed-design/cli@latest add ui:side-panel ### Responsive \[#responsive] 뷰포트에 따라 Bottom Sheet로 자동 전환되는 반응형 변형입니다. dependency로 `ui:bottom-sheet`와 `ui:side-panel`이 함께 설치됩니다. - npm: npx @seed-design/cli@latest add ui:responsive-side-panel - pnpm: pnpm dlx @seed-design/cli@latest add ui:responsive-side-panel - yarn: yarn dlx @seed-design/cli@latest add ui:responsive-side-panel - bun: bun x @seed-design/cli@latest add ui:responsive-side-panel ## Props \[#props] ### `SidePanelRoot` \[#sidepanelroot] - `direction` - type: `"left" | "right" | undefined` - default: `"right"` - description: Direction the side panel slides in from. - `size` - type: `"small" | "medium" | "large" | undefined` - default: `"medium"` - `children` - type: `React.ReactNode` - `onAnimationEnd` - type: `((open: boolean) => void) | undefined` - description: Gets triggered after the open or close animation ends, it receives an \`open\` argument with the \`open\` state of the drawer by the time the function was triggered. Useful to revert any state changes for example. - `open` - type: `boolean | undefined` - `onOpenChange` - type: `((open: boolean, details?: DrawerChangeDetails) => void) | undefined` - `dismissible` - type: `boolean | undefined` - default: `true` - description: When \`false\` dragging, clicking outside, pressing esc, etc. will not close the drawer. Use this in combination with the \`open\` prop, otherwise you won't be able to open/close the drawer. - `modal` - type: `boolean | undefined` - default: `true` - description: When \`false\` it allows to interact with elements outside of the drawer without closing it. - `defaultOpen` - type: `boolean | undefined` - default: `false` - description: Opened by default. Still reacts to \`open\` state changes - `closeOnInteractOutside` - type: `boolean | undefined` - default: `true` - description: Whether to close the drawer when interacting outside of the drawer. - `closeOnEscape` - type: `boolean | undefined` - default: `true` - description: Whether to close the drawer when pressing the escape key. - `lazyMount` - type: `boolean | undefined` - default: `false` - description: Whether to lazy mount the drawer content on first open. - `unmountOnExit` - type: `boolean | undefined` - default: `false` - description: Whether to unmount the drawer content on exit. ### `SidePanelTrigger` \[#sidepaneltrigger] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `SidePanelContent` \[#sidepanelcontent] - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `layerIndex` - type: `number | undefined` - `showCloseButton` - type: `boolean | undefined` - default: `true` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `width` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` - `maxWidth` - type: `ResponsiveValue<(string & {}) | "full" | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText"> | undefined` ### `SidePanelBody` \[#sidepanelbody] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `paddingX` - type: `ResponsiveValue<0 | Dimension | "spacingX.betweenChips" | "spacingX.globalGutter" | "spacingY.componentDefault" | "spacingY.navToTitle" | "spacingY.screenBottom" | "spacingY.betweenText" | (string & {})> | undefined` - `height` - type: `ResponsiveValue | undefined` - `maxHeight` - type: `ResponsiveValue | undefined` - `minHeight` - type: `ResponsiveValue | undefined` - `justifyContent` - type: `"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | undefined` - `alignItems` - type: `"flex-start" | "flex-end" | "center" | "stretch" | undefined` ### `SidePanelFooter` \[#sidepanelfooter] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `ResponsiveSidePanelRoot` \[#responsivesidepanelroot] - `children` - type: `React.ReactNode` - `open` - type: `boolean | undefined` - `defaultOpen` - type: `boolean | undefined` - `onOpenChange` - type: `((open: boolean) => void) | undefined` - `sidePanelBreakpoint` - type: `"sm" | "md" | "lg" | "xl" | undefined` - default: `"md"` - description: Breakpoint at and above which the panel renders as a SidePanel; below it, a BottomSheet. Cannot be \`"base"\`, which would always be a SidePanel. - `sidePanelRootProps` - type: `Omit | undefined` - description: Props forwarded to the underlying SidePanel root (at and above the breakpoint). - `bottomSheetRootProps` - type: `Omit | undefined` - description: Props forwarded to the underlying BottomSheet root (below the breakpoint). ## Examples \[#examples] ### Trigger \[#trigger] ``는 `asChild` 패턴을 사용해 자식 요소가 Side Panel을 열 수 있도록 합니다. ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { SidePanelBody, SidePanelContent, SidePanelRoot, SidePanelTrigger, } from "seed-design/ui/side-panel"; const SidePanelTriggerExample = () => { return ( Open Trigger를 클릭하면 현재 화면 위에 Side Panel이 열립니다. ); }; export default SidePanelTriggerExample; ``` ### Controlled \[#controlled] Trigger 외의 방식으로 Side Panel을 열고 닫을 수 있습니다. 이 경우 `open` prop을 사용하여 Side Panel의 상태를 제어합니다. ```tsx import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { SidePanelBody, SidePanelContent, SidePanelFooter, SidePanelRoot, } from "seed-design/ui/side-panel"; const SidePanelControlled = () => { const [open, setOpen] = useState(false); const scheduleOpen = () => { setTimeout(() => { setOpen(true); }, 1000); }; return ( <> 1초 후 열기 외부 상태로 패널을 열고 닫을 때도 본문과 푸터 구조는 동일하게 유지됩니다. 확인 ); }; export default SidePanelControlled; ``` ### Direction \[#direction] ``에 `direction` prop을 설정하여 Side Panel이 열리는 방향을 변경할 수 있습니다. `"left"` 또는 `"right"`를 지원하며, 기본값은 `"right"`입니다. ```tsx import { Flex } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { SidePanelBody, SidePanelContent, SidePanelRoot, SidePanelTrigger, } from "seed-design/ui/side-panel"; const SidePanelDirection = () => { return ( Right 오른쪽 가장자리에서 슬라이드되어 보조 콘텐츠를 표시합니다. Left 왼쪽 가장자리에서 슬라이드되어 탐색이나 설정 영역을 표시합니다. ); }; export default SidePanelDirection; ``` ### Size \[#size] ``에 `size` prop을 설정하여 Side Panel의 너비를 변경할 수 있습니다. `"small"` (480px), `"medium"` (720px, 기본값), `"large"` (960px)를 지원합니다. ```tsx import { Flex } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { SidePanelBody, SidePanelContent, SidePanelRoot, SidePanelTrigger, } from "seed-design/ui/side-panel"; const SidePanelSize = () => { return ( Small (480px) 좁은 패널에 적합한 간결한 콘텐츠를 배치합니다. Medium (720px) 기본 너비로 상세 정보와 주요 액션을 함께 제공합니다. Large (960px) 넓은 패널에서 더 많은 폼 필드나 상세 콘텐츠를 다룹니다. ); }; export default SidePanelSize; ``` ### Footer Layout \[#footer-layout] `SidePanelFooter`는 flex 레이아웃만 제공하며, 버튼 배치는 `VStack`, `HStack` 등으로 직접 구성합니다. 좁은 패널에서는 `VStack`으로 세로로 쌓고, 넓은 패널에서는 `HStack`으로 가로로 정렬할 수 있습니다. ```tsx import { Box, Flex, HStack, VStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { SidePanelBody, SidePanelContent, SidePanelFooter, SidePanelRoot, SidePanelTrigger, } from "seed-design/ui/side-panel"; const SidePanelFooterLayout = () => { return ( Small 확인이 필요한 정보를 간결하게 보여줍니다. 버튼은 패널 너비를 채우며 위에서 아래로 쌓입니다. 확인 취소 Medium 상세 정보와 확인 액션을 함께 제공할 수 있습니다. 버튼은 우측 영역에 가로로 정렬됩니다. 취소 확인 ); }; export default SidePanelFooterLayout; ``` ### Custom Size \[#custom-size] ``에 `width`, `maxWidth` prop을 전달하여 너비를 직접 제어할 수 있습니다. 프리셋 size 대신 뷰포트 기반의 유동적인 너비가 필요할 때 사용합니다. ```tsx import { Flex } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { SidePanelBody, SidePanelContent, SidePanelRoot, SidePanelTrigger, } from "seed-design/ui/side-panel"; const SidePanelCustomSize = () => { return ( width 80vw, max 640px 뷰포트 너비에 따라 커지되 최대 640px까지만 확장됩니다. width 400px 고정 너비가 필요한 작업 패널에 사용할 수 있습니다. ); }; export default SidePanelCustomSize; ``` ### Show Close Button \[#show-close-button] ``에 `showCloseButton` prop을 전달하여 닫기 버튼을 표시할 수 있습니다. 기본 값은 `true`입니다. ```tsx import { Flex } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { SidePanelBody, SidePanelContent, SidePanelRoot, SidePanelTrigger, } from "seed-design/ui/side-panel"; const SidePanelShowCloseButton = () => { return ( 닫기 버튼 있음 기본적으로 닫기 버튼이 표시되어 패널을 바로 닫을 수 있습니다. 닫기 버튼 없음 닫기 버튼을 숨길 때는 본문이나 푸터에 닫을 수 있는 액션을 제공하세요. ); }; export default SidePanelShowCloseButton; ``` ### Dismissible \[#dismissible] `dismissible` prop을 `false`로 설정하면 Escape 키, 외부 클릭으로 닫을 수 없습니다. 의도적으로 Side Panel을 닫을 수 없게 하고 싶을 때 사용합니다. ```tsx import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { SidePanelBody, SidePanelContent, SidePanelFooter, SidePanelRoot, SidePanelTrigger, } from "seed-design/ui/side-panel"; const SidePanelDismissible = () => { const [open, setOpen] = useState(false); return ( 닫기 불가 Side Panel Escape 키, 외부 클릭으로 닫을 수 없습니다. 프로그래밍 방식으로만 닫을 수 있습니다. setOpen(false)}> 닫기 ); }; export default SidePanelDismissible; ``` ### Non-modal \[#non-modal] `modal` prop을 `false`로 설정하면 배경과 상호작용이 가능합니다. Backdrop이 표시되지 않으며, 포커스 트랩이 비활성화됩니다. Trigger를 다시 눌러 닫아야 하는 경우에는 `open` prop으로 상태를 직접 제어합니다. ```tsx import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { SidePanelBody, SidePanelContent, SidePanelRoot, SidePanelTrigger, } from "seed-design/ui/side-panel"; const SidePanelNonModal = () => { const [open, setOpen] = useState(false); return ( { event.preventDefault(); setOpen((open) => !open); }} > Non-modal Side Panel 배경과 상호작용이 가능합니다. Backdrop이 표시되지 않습니다. ); }; export default SidePanelNonModal; ``` ### Responsive \[#responsive-1] `ResponsiveSidePanel`을 사용하면 md 이상에서는 Side Panel, sm 이하에서는 Bottom Sheet로 자동 전환됩니다. 뷰포트를 줄여서 전환 동작을 확인해보세요. `onOpenChange`는 열림 상태만 전달하며, Side Panel 또는 Bottom Sheet에만 적용되는 Root 옵션은 `sidePanelRootProps`, `bottomSheetRootProps`로 전달합니다. ```tsx import { Box, VStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { ResponsiveSidePanelBody, ResponsiveSidePanelContent, ResponsiveSidePanelFooter, ResponsiveSidePanelRoot, ResponsiveSidePanelTrigger, } from "seed-design/ui/responsive-side-panel"; const SidePanelResponsive = () => { return ( Open 본문 영역은 Header/Body/Footer 구조로 동일합니다. md 이상에서는 화면 우측에서 슬라이드되는 Side Panel로, sm 이하에서는 화면 하단에서 슬라이드되는 Bottom Sheet로 표시됩니다. 확인 ); }; export default SidePanelResponsive; ``` ## Keyboard Interactions \[#keyboard-interactions] | Key | Behavior | | ------------- | ------------------------------------------------- | | `Escape` | `dismissible=true`일 때 Side Panel을 닫습니다. | | `Tab` | `modal=true`일 때 Side Panel 내부 요소들 사이로 포커스를 순환합니다. | | `Shift + Tab` | `modal=true`일 때 역방향으로 포커스를 순환합니다. | --- file: components/skeleton.mdx # Skeleton 콘텐츠가 로딩되는 동안 이후 나타날 요소의 윤곽을 미리 보여주어 로딩 시간을 짧게 느끼게 하는 UI 요소입니다. ## Preview ```tsx import { Skeleton, VStack } from "@seed-design/react"; export default function SkeletonPreview() { return ( ); } ``` ## Usage \[#usage] ```tsx import { Skeleton } from "@seed-design/react"; ``` ```tsx ``` ## Props \[#props] - `radius` - type: `"full" | "0" | "8" | "16" | undefined` - default: `8` - description: - \`0\`: 기본값입니다. - \`8\`: 텍스트 콘텐츠에 사용합니다. - \`16\`: 카드 및 썸네일에 사용합니다. - \`full\`: Avatar(원형) 콘텐츠에 사용합니다. - `tone` - type: `"neutral" | "magic" | undefined` - default: `"neutral"` - description: - \`neutral\`: 데이터를 불러오는 일반적인 로딩 경험에 사용합니다. - \`magic\`: AI 기능이 활성화되었을 때 사용합니다. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `height` - type: `ResponsiveValue | undefined` - `width` - type: `ResponsiveValue | undefined` ## Examples \[#examples] ### Radius \[#radius] ```tsx import { Flex, Skeleton } from "@seed-design/react"; export default function SkeletonRadius() { return ( ); } ``` ### Tone \[#tone] ```tsx import { Box, Skeleton, VStack } from "@seed-design/react"; export default function SkeletonTone() { return ( ); } ``` --- file: components/slider.mdx # Slider 지정된 범위 내에서 하나 또는 두 개의 값을 선택해 입력할 수 있는 컴포넌트입니다. ## Preview ```tsx import { Slider } from "seed-design/ui/slider"; export default function SliderPreview() { return "값"} />; } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:slider - pnpm: pnpm dlx @seed-design/cli@latest add ui:slider - yarn: yarn dlx @seed-design/cli@latest add ui:slider - bun: bun x @seed-design/cli@latest add ui:slider ## Props \[#props] - `label` - type: `React.ReactNode` - `labelWeight` - type: `"bold" | "medium" | undefined` - default: `"medium"` - `indicator` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `errorMessage` - type: `React.ReactNode` - `showRequiredIndicator` - type: `boolean | undefined` - `markers` - type: `(number | { value: number; label?: React.ReactNode; align?: SliderMarkerVariantProps["align"]; })[] | undefined` - default: `[]` - `ticks` - type: `number[] | undefined` - default: `[]` - `tickWeight` - type: `"thin" | "thick" | undefined` - default: `"thin"` - `hideRange` - type: `boolean | undefined` - default: `false` - `hideValueIndicator` - type: `boolean | undefined` - default: `false` - `fieldRef` - type: `React.Ref | undefined` - `disabled` - type: `boolean | undefined` - default: `false` - `readOnly` - type: `boolean | undefined` - default: `false` - `invalid` - type: `boolean | undefined` - default: `false` - `name` - type: `string | undefined` - `jumpMultiplier` - type: `number | undefined` - default: `10` - `getAriaValuetext` - type: `((value: number) => string) | undefined` - `getAriaLabel` - type: `((thumbIndex: number) => string) | undefined` - `getAriaLabelledby` - type: `((thumbIndex: number) => string) | undefined` - `getValueIndicatorLabel` - type: `((params: { value: number; thumbIndex: number; }) => React.ReactNode) | undefined` - default: `(params) => params.value` - `valueIndicatorTrigger` - type: `"active" | "hover" | "auto" | undefined` - default: `"auto"` - `dragStartDelayInMilliseconds` - type: `number | undefined` - default: `150` - `min` - type: `number` - required: `true` - `max` - type: `number` - required: `true` - `step` - type: `number | undefined` - default: `1` - `allowedValues` - type: `number[] | undefined` - default: `[]` - description: Values that the slider thumbs can snap to. If not provided, the slider will snap to every step. - `minStepsBetweenThumbs` - type: `number | undefined` - default: `0` - `values` - type: `number[] | undefined` - `defaultValues` - type: `number[] | undefined` - default: `[(min + max) / 2]` - `onValuesChange` - type: `((value: number[]) => void) | undefined` - `onValuesCommit` - type: `((value: number[]) => void) | undefined` - `dir` - type: `"ltr" | "rtl" | undefined` - default: `"ltr"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Basic \[#basic] `min`과 `max`를 설정하여 슬라이더의 값 범위를 지정합니다. ```tsx import { VStack } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; export default function SliderBasic() { return ( "값"} /> "값"} /> ); } ``` ### Steps \[#steps] 특정 간격으로만 값을 선택할 수 있도록 설정할 수 있습니다. ```tsx import { Slider } from "seed-design/ui/slider"; import { useState } from "react"; import { VStack, Text } from "@seed-design/react"; export default function SliderSteps() { const [value, setValue] = useState([50]); return ( "값"} /> {JSON.stringify(value)} ); } ``` ### Allowed Values \[#allowed-values] `allowedValues` prop을 사용하여 슬라이더에서 선택할 수 있는 값을 제한할 수 있습니다. `allowedValues`가 지정된 경우 `step`과 `minStepsBetweenThumbs`은 무시됩니다. ```tsx import { VStack, Text } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; import { useState } from "react"; const ALLOWED_VALUES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]; export default function SliderAllowedValues() { const [values, setValues] = useState([ALLOWED_VALUES[0], ALLOWED_VALUES[2]]); return ( ({ label: value, value }))} getAriaLabel={() => "값"} /> {JSON.stringify(values)} ); } ``` ### With Ticks \[#with-ticks] 슬라이더 트랙에 눈금을 표시할 수 있습니다. #### Thin \[#thin] continuous하다고 느껴질 만큼 step이 충분히 작을 때 주로 사용합니다. ```tsx import { Slider } from "seed-design/ui/slider"; export default function SliderTicksThin() { return ( "값"} /> ); } ``` #### Thick \[#thick] discrete하다고 느껴질 만큼 step이 충분히 클 때 또는 tick에만 thumb이 위치할 수 있을 때 주로 사용합니다. ```tsx import { Slider } from "seed-design/ui/slider"; export default function SliderTicksThick() { return ( "값"} /> ); } ``` ### With Markers \[#with-markers] 슬라이더 아래에 마커를 표시할 수 있습니다. 스크린 리더는 marker를 읽지 않습니다. 특정 값을 선택했을 때 사용자에게 알려야 하는 경우 [`getAriaValuetext` prop](#getariavaluetext)을 사용하여 현재 선택된 값을 설명하는 문구를 함께 제공하세요. ```tsx import { VStack } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; export default function SliderMarkers() { return ( `${value}°C`} getValueIndicatorLabel={({ value }) => `${value}°C`} getAriaLabel={() => "온도"} /> "값"} /> ); } ``` ### Controlled \[#controlled] `values`와 `onValuesChange` props를 사용하여 슬라이더의 상태를 외부에서 제어합니다. ```tsx import { VStack, HStack, Text } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; import { ActionButton } from "seed-design/ui/action-button"; import { useState } from "react"; const DEFAULT_VALUE = [50]; export default function SliderControlled() { const [value, setValue] = useState(DEFAULT_VALUE); return ( "값"} /> {JSON.stringify(value)} setValue([0])} variant="neutralWeak"> Set Min setValue(DEFAULT_VALUE)} variant="neutralWeak"> Reset setValue([100])} variant="neutralWeak"> Set Max ); } ``` ### Listening to Value Changes \[#listening-to-value-changes] - `onValuesChange` - 슬라이더의 값이 변경될 때마다 호출됩니다. 이 prop을 사용하여 슬라이더의 값을 실시간으로 추적할 수 있습니다. - `onValuesCommit` - 슬라이더의 값 변경이 완료되었을 때 호출됩니다. 사용자가 슬라이더 조작을 마치고 손을 뗄 때 값을 확정하는 데 유용합니다. ```tsx import { VStack, Text, HStack } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; import { useState } from "react"; export default function SliderOnValuesCommit() { const [value, setValue] = useState([20]); const [committedValue, setCommittedValue] = useState([20]); return ( "값"} /> Current value: {JSON.stringify(value)} Committed value: {JSON.stringify(committedValue)} ); } ``` ### Disabled \[#disabled] ```tsx import { VStack } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; export default function SliderDisabled() { return ( "값"} /> (thumbIndex === 0 ? "최소값" : "최대값")} /> ); } ``` ### Hide Range \[#hide-range] `hideRange` prop을 사용하여 기본 range 색상을 숨기고 커스텀 스타일을 적용할 수 있습니다. ```tsx import { VStack } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; export default function SliderHideRange() { return ( "값"} /> (thumbIndex === 0 ? "최소값" : "최대값")} /> ); } ``` ### Customizing Value Indicator \[#customizing-value-indicator] #### Value Indicator Label \[#value-indicator-label] `getValueIndicatorLabel` prop을 사용하여 thumb 위에 표시되는 툴팁의 내용을 커스텀할 수 있습니다. Value Indicator는 thumb을 누르고 있을 때만 표시되며 스크린 리더는 Value Indicator의 내용을 읽지 않습니다. Value Indicator에 의미가 있는 내용을 포함하는 경우, [`getAriaValuetext` prop](#getariavaluetext)을 함께 사용하는 것을 권장합니다. ```tsx import { Slider } from "seed-design/ui/slider"; const formatter = new Intl.NumberFormat("ko-KR", { style: "decimal" }); export default function SliderCustomValueIndicatorLabel() { return ( ( <> thumb {thumbIndex}
    {formatter.format(value)} )} getAriaValuetext={formatter.format} getAriaLabel={() => "값"} /> ); } ``` #### Value Indicator Trigger \[#value-indicator-trigger] `valueIndicatorTrigger` prop으로 value indicator가 표시되는 조건을 설정할 수 있습니다. 기본값은 `"auto"`로, 디바이스 환경에 따라 Value Indicator 표시 조건이 달라집니다. - **마우스 환경**: thumb hover 시 Value Indicator가 표시됩니다. `valueIndicatorTrigger="hover"`로 설정하면 항상 이렇게 동작합니다. - **터치 환경**: thumb active 시 Value Indicator가 표시됩니다. `valueIndicatorTrigger="active"`로 설정하면 항상 이렇게 동작합니다. 키보드를 통해 thumb을 포커스하면 `valueIndicatorTrigger` 값에 관계없이 항상 Value Indicator가 표시됩니다. ```tsx import { VStack } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; export default function SliderValueIndicatorTrigger() { return ( "값"} /> "값"} /> "값"} /> ); } ``` #### Hide Value Indicator \[#hide-value-indicator] `hideValueIndicator` prop을 사용하여 thumb 위에 표시되는 Value Indicator 툴팁을 숨길 수 있습니다. ```tsx import { Slider } from "seed-design/ui/slider"; export default function SliderHideValueIndicator() { return ( "값"} /> ); } ``` ### Range Slider \[#range-slider] 두 개의 thumb를 사용하여 범위를 선택할 수 있습니다. ```tsx import { VStack } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; import { useState } from "react"; export default function SliderRange() { const [priceRange, setPriceRange] = useState([20, 80]); return ( (thumbIndex === 0 ? "최소값" : "최대값")} /> ); } ``` #### Minimum Steps Between Thumbs \[#minimum-steps-between-thumbs] 두 thumb 사이의 최소 간격을 설정할 수 있습니다. ```tsx import { VStack, Text } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; import { useState } from "react"; export default function SliderRangeMinSteps() { const [values, setValues] = useState([20, 80]); return ( (thumbIndex === 0 ? "최소값" : "최대값")} /> {JSON.stringify(values)} ); } ``` ### Accessibility \[#accessibility] #### `getAriaValuetext` \[#getariavaluetext] 스크린 리더는 기본적으로 각 thumb이 가리키는 숫자 값(value)을 읽습니다. \**숫자 값만으로 정보를 충분히 전달할 수 없는 경우 `getAriaValuetext` prop을 사용하여 인간 친화적인 설명을 제공하세요.*\* 단위를 추가하거나, 값의 의미를 설명하는 문구를 포함할 수 있습니다. ```tsx import { VStack, Text } from "@seed-design/react"; import { useState } from "react"; import { Slider } from "seed-design/ui/slider"; const days = ["일", "월", "화", "수", "목", "금", "토"]; function getHumanReadableDayOfWeek(value: number) { if (days[value] === undefined) throw new Error("Invalid day value"); return `${days[value]}요일`; } export default function SliderGetAriaValuetext() { const [values, setValues] = useState([1, 3]); return ( ({ label, value }))} ticks={days.slice(1, -1).map((_, index) => index + 1)} tickWeight="thick" values={values} onValuesChange={setValues} getAriaLabel={(thumbIndex) => (thumbIndex === 0 ? "시작" : "종료")} getAriaValuetext={getHumanReadableDayOfWeek} getValueIndicatorLabel={({ value }) => getHumanReadableDayOfWeek(value)} /> values: {JSON.stringify(values)} aria-valuetext: {JSON.stringify(values.map(getHumanReadableDayOfWeek))} ); } ``` #### `getAriaLabel` and `getAriaLabelledby` \[#getarialabel-and-getarialabelledby] `getAriaLabel` 또는 `getAriaLabelledby` prop을 사용하여 각 thumb에 대한 설명을 제공하세요. 스크린 리더 사용자가 각 thumb의 용도를 이해하는 데 도움을 줍니다. **특히, 범위를 선택하는 슬라이더의 경우 각 thumb이 범위에서 어떤 역할을 하는지 설명해야 합니다. (최소-최대, 시작-종료 등)** ```tsx import { VStack, Text, type SliderRootProps } from "@seed-design/react"; import { useState } from "react"; import { Slider } from "seed-design/ui/slider"; const getAriaLabel: NonNullable = (thumbIndex) => thumbIndex === 0 ? "최소값" : "최대값"; export default function SliderGetAriaLabel() { const [values, setValues] = useState([10, 30]); return ( values: {JSON.stringify(values)} aria-label: {JSON.stringify(values.map((_, index) => getAriaLabel(index)))} ); } ``` ### Field Integration \[#field-integration] `label`, `description`, `errorMessage` 등의 Field 관련 prop을 사용할 수 있습니다. ```tsx import { Divider, VStack } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; const markers = [ { value: 0, label: "매우 동의하지 않음" }, { value: 14, label: "매우 동의함" }, ]; export default function SliderField() { return ( `${value} ${markers.find((marker) => marker.value === value)?.label ?? ""}`.trim() } /> ); } ``` ### Use Cases \[#use-cases] #### Form (Uncontrolled) \[#form-uncontrolled] ```tsx import { HStack, VStack } from "@seed-design/react"; import { useCallback, useState, type FormEvent } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { Slider } from "seed-design/ui/slider"; type FieldErrors = { rating?: string; priceRange?: string; }; export default function SliderForm() { const [fieldErrors, setFieldErrors] = useState({}); const handleSubmit = useCallback((event: FormEvent) => { event.preventDefault(); const formData = new FormData(event.currentTarget); const rating = Number(formData.get("rating")); const priceRangeValues = formData.getAll("price-range").map(Number); const newFieldErrors: FieldErrors = {}; if (rating < 3) { newFieldErrors.rating = "평점은 최소 3점 이상이어야 합니다"; } const [min, max] = priceRangeValues; if (max - min < 20) { newFieldErrors["priceRange"] = "가격 범위는 최소 20만원 이상 차이가 나야 합니다"; } setFieldErrors(newFieldErrors); if (Object.keys(newFieldErrors).length > 0) return; window.alert(JSON.stringify({ rating, "price-range": priceRangeValues }, null, 2)); }, []); return (
    `${value}점`} getAriaValuetext={(value) => `${value}점`} showRequiredIndicator {...(fieldErrors.rating && { invalid: true, errorMessage: fieldErrors.rating, })} /> `${value}만원`} getAriaValuetext={(value) => `${value}만원`} getAriaLabel={(index) => (index === 0 ? "최소 가격" : "최대 가격")} showRequiredIndicator {...(fieldErrors["priceRange"] && { invalid: true, errorMessage: fieldErrors["priceRange"], })} /> 제출
    ); } ``` #### React Hook Form \[#react-hook-form] ```tsx import { HStack, VStack } from "@seed-design/react"; import { useCallback, type FormEvent } from "react"; import { useController, useForm } from "react-hook-form"; import { ActionButton } from "seed-design/ui/action-button"; import { Slider } from "seed-design/ui/slider"; interface FormValues { rating: number; priceRange: [number, number]; } export default function SliderReactHookForm() { const { handleSubmit, reset, control } = useForm({ reValidateMode: "onSubmit", defaultValues: { rating: 2.5, priceRange: [20, 80], }, }); const { field: { value: ratingValue, onChange: ratingOnChange, onBlur: __ratingOnBlur, ...ratingField }, fieldState: ratingFieldState, } = useController({ name: "rating", control, rules: { validate: (value) => value >= 3 || "평점은 최소 3점 이상이어야 합니다", }, }); const { field: { value: priceRangeValue, onChange: priceRangeOnChange, onBlur: __priceRangeOnBlur, ...priceRangeField }, fieldState: priceRangeFieldState, } = useController({ name: "priceRange", control, rules: { validate: (value) => { const [min, max] = value; return max - min >= 20 || "가격 범위는 최소 20만원 이상 차이가 나야 합니다"; }, }, }); const onValid = useCallback( (data: FormValues) => window.alert(JSON.stringify(data, null, 2)), [], ); const onReset = useCallback( (event: FormEvent) => { event.preventDefault(); reset(); }, [reset], ); return ( ratingOnChange(values)} markers={[ { value: 0, label: "0점" }, { value: 5, label: "5점" }, ]} getValueIndicatorLabel={({ value }) => `${value}점`} getAriaValuetext={(value) => `${value}점`} showRequiredIndicator {...ratingField} /> `${value}만원`} getAriaValuetext={(value) => `${value}만원`} getAriaLabel={(index) => (index === 0 ? "최소 가격" : "최대 가격")} minStepsBetweenThumbs={2} showRequiredIndicator {...priceRangeField} /> 초기화 제출 ); } ``` ### RTL Support \[#rtl-support] ```tsx import { VStack } from "@seed-design/react"; import { Slider } from "seed-design/ui/slider"; export default function SliderRtl() { return ( `${value}°C`} getValueIndicatorLabel={({ value }) => `${value}°C`} getAriaLabel={() => "온도"} /> "값"} /> ); } ``` --- file: components/snackbar.mdx # Snackbar 화면 하단에 일시적으로 나타나 상태나 결과를 안내하는 컴포넌트입니다. ## Preview ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { Snackbar, SnackbarProvider, useSnackbarAdapter } from "seed-design/ui/snackbar"; function Component() { const adapter = useSnackbarAdapter(); return ( adapter.create({ onClose: () => {}, render: () => {}} />, }) } > 실행 ); } export default function SnackbarPreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:snackbar - pnpm: pnpm dlx @seed-design/cli@latest add ui:snackbar - yarn: yarn dlx @seed-design/cli@latest add ui:snackbar - bun: bun x @seed-design/cli@latest add ui:snackbar ## Props \[#props] ### `Snackbar` \[#snackbar] - `message` - type: `string` - required: `true` - description: 스낵바에 표시할 메시지 - `actionLabel` - type: `string | undefined` - description: 스낵바에 표시할 액션 버튼의 라벨 - `onAction` - type: `(() => void) | undefined` - description: 액션 버튼 클릭 시 호출되는 콜백 - `variant` - type: `"critical" | "positive" | "default" | undefined` - default: `"default"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `SnackbarAdapter.create` Parameters (`CreateSnackbarOptions`) \[#snackbaradaptercreate-parameters-createsnackbaroptions] - `timeout` - type: `number | undefined` - default: `4000` - description: The duration the snackbar will be visible - `removeDelay` - type: `number | undefined` - default: `200` - description: The duration for the snackbar to kept alive before it is removed. Useful for exit transitions. - `onClose` - type: `(() => void) | undefined` - description: Function called when the snackbar has been closed and removed - `render` - type: `() => React.ReactNode` - required: `true` - description: The content to render in the snackbar region - `strategy` - type: `"immediate" | "queued" | undefined` - description: Override the provider-level strategy for this specific snackbar. - \`"immediate"\`: Replace the current snackbar instantly. - \`"queued"\`: Wait in the queue until the current one is dismissed. ### `SnackbarProvider` \[#snackbarprovider] - `children` - type: `React.ReactNode` - required: `true` - `pauseOnInteraction` - type: `boolean | undefined` - default: `true` - description: Whether to pause the toast when interacted with - `strategy` - type: `"immediate" | "queued" | undefined` - default: `"immediate"` - description: How to handle multiple snackbars. - \`"immediate"\`: New snackbar replaces the current one instantly. - \`"queued"\`: New snackbar waits in a queue until the current one is dismissed. ## Examples \[#examples] ### Variants \[#variants] #### Positive \[#positive] ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { Snackbar, SnackbarProvider, useSnackbarAdapter } from "seed-design/ui/snackbar"; function Component() { const adapter = useSnackbarAdapter(); return ( adapter.create({ onClose: () => {}, render: () => ( {}} /> ), }) } > 실행 ); } export default function SnackbarPositive() { return ( ); } ``` #### Critical \[#critical] ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { Snackbar, SnackbarProvider, useSnackbarAdapter } from "seed-design/ui/snackbar"; function Component() { const adapter = useSnackbarAdapter(); return ( adapter.create({ timeout: 50000000, onClose: () => {}, render: () => ( {}} /> ), }) } > 실행 ); } export default function SnackbarNegative() { return ( ); } ``` ### Pause on Interaction \[#pause-on-interaction] 사용자가 Snackbar와 상호작용(hover 및 active) 하는 동안 Snackbar가 timeout으로 인해 dismiss되지 않도록 하려면 `SnackbarProvider`의 `pauseOnInteraction`을 `true`로 설정합니다. - `@seed-design/react@0.1.14`까지: `false`: 명시하지 않는 경우, 상호작용 여부와 관계없이 timeout이 지나면 dismiss됩니다. - 이후 버전: `true`: 명시하지 않는 경우, 사용자가 상호작용하는 동안 timeout이 멈춥니다. ```tsx import { VStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { Snackbar, SnackbarProvider, useSnackbarAdapter } from "seed-design/ui/snackbar"; import { SegmentedControl, SegmentedControlItem } from "seed-design/ui/segmented-control"; import { useState } from "react"; function Component() { const adapter = useSnackbarAdapter(); return ( adapter.create({ onClose: () => {}, render: () => {}} />, }) } > 실행 ); } export default function SnackbarPauseOnInteraction() { const [pauseOnInteraction, setPauseOnInteraction] = useState(true); return ( setPauseOnInteraction(value === "true")} > false true ); } ``` ### Strategy \[#strategy] Snackbar가 이미 표시 중일 때 새로운 Snackbar를 생성하면, 기본적으로 기존 Snackbar를 즉시 교체합니다 (`immediate`). 큐에 넣고 순차적으로 보여주려면 `strategy: "queued"`를 사용합니다. `strategy`는 `SnackbarProvider`에서 기본값을 설정하거나, `create()` 호출 시 개별적으로 지정할 수 있습니다. ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { Snackbar, SnackbarProvider, useSnackbarAdapter } from "seed-design/ui/snackbar"; function Component() { const adapter = useSnackbarAdapter(); return (
    adapter.create({ render: () => , }) } > Immediate (positive) adapter.create({ strategy: "queued", render: () => , }) } > Queued (critical)
    ); } export default function SnackbarStrategy() { return ( ); } ``` ### Avoid Overlap \[#avoid-overlap] `` 컴포넌트를 사용하여 스낵바가 겹치지 않아야 하는 영역을 지정할 수 있습니다. ``의 자식 컴포넌트는 forwardRef(~React 18) 혹은 ref prop(React 19~)으로 ref를 전달받아야 합니다. offset은 다음 상황에서 갱신됩니다. - SnackbarAvoidOverlap의 자식 컴포넌트가 mount될 때 - SnackbarAvoidOverlap의 자식 컴포넌트가 unmount될 때 - SnackbarAvoidOverlap의 자식 컴포넌트의 높이가 변경될 때 - SnackbarRegion의 높이가 변경될 때 ```tsx import { Flex, VStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; import { Snackbar, SnackbarAvoidOverlap, SnackbarProvider, useSnackbarAdapter, } from "seed-design/ui/snackbar"; function Component() { const adapter = useSnackbarAdapter(); return ( adapter.create({ onClose: () => {}, render: () => {}} />, }) } > 실행 Snackbar가 이 영역과 겹치지 않게 조정됩니다. 스크롤은 무시합니다. ); } export default function SnackbarPreview() { return ( ); } ``` --- file: components/swipeable-menu-sheet.mdx # Swipeable Menu Sheet 사용자의 작업과 관련된 선택지를 제공하는 시트 형태의 컴포넌트입니다. ## Preview ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, SwipeableMenuSheetTrigger, } from "seed-design/ui/swipeable-menu-sheet"; const SwipeableMenuSheetPreview = () => { return ( Open } /> } /> } /> } /> } tone="critical" /> ); }; export default SwipeableMenuSheetPreview; ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:swipeable-menu-sheet - pnpm: pnpm dlx @seed-design/cli@latest add ui:swipeable-menu-sheet - yarn: yarn dlx @seed-design/cli@latest add ui:swipeable-menu-sheet - bun: bun x @seed-design/cli@latest add ui:swipeable-menu-sheet ## Props \[#props] ### `SwipeableMenuSheetRoot` \[#swipeablemenusheetroot] - `skipAnimation` - type: `boolean | undefined` - default: `false` - `children` - type: `React.ReactNode` - `onAnimationEnd` - type: `((open: boolean) => void) | undefined` - description: Gets triggered after the open or close animation ends, it receives an \`open\` argument with the \`open\` state of the drawer by the time the function was triggered. Useful to revert any state changes for example. - `open` - type: `boolean | undefined` - `onOpenChange` - type: `((open: boolean, details?: DrawerChangeDetails) => void) | undefined` - `defaultOpen` - type: `boolean | undefined` - default: `false` - description: Opened by default. Still reacts to \`open\` state changes - `closeOnInteractOutside` - type: `boolean | undefined` - default: `true` - description: Whether to close the drawer when interacting outside of the drawer. - `closeOnEscape` - type: `boolean | undefined` - default: `true` - description: Whether to close the drawer when pressing the escape key. - `lazyMount` - type: `boolean | undefined` - default: `false` - description: Whether to lazy mount the drawer content on first open. - `unmountOnExit` - type: `boolean | undefined` - default: `false` - description: Whether to unmount the drawer content on exit. ### `SwipeableMenuSheetTrigger` \[#swipeablemenusheettrigger] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `SwipeableMenuSheetContent` \[#swipeablemenusheetcontent] - `title` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `layerIndex` - type: `number | undefined` - `showCloseButton` - type: `boolean | undefined` - default: `false` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `labelAlign` - type: `"center" | "left" | undefined` - default: `"left"` - description: - \`left\`: 라벨을 왼쪽 정렬합니다. - \`center\`: 라벨을 중앙 정렬합니다. ### `SwipeableMenuSheetGroup` \[#swipeablemenusheetgroup] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `labelAlign` - type: `"center" | "left" | undefined` - default: `"left"` - description: - \`left\`: 라벨을 왼쪽 정렬합니다. - \`center\`: 라벨을 중앙 정렬합니다. ### `SwipeableMenuSheetItem` \[#swipeablemenusheetitem] - `prefixIcon` - type: `React.ReactNode` - `label` - type: `React.ReactNode` - required: `true` - `description` - type: `React.ReactNode` - `tone` - type: `"critical" | "neutral" | undefined` - default: `"neutral"` - description: - \`neutral\`: 일반적인 작업을 수행하는 기본 아이템입니다. - \`critical\`: 데이터 삭제와 같이 되돌릴 수 없는 작업을 수행하는 아이템입니다. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `labelAlign` - type: `"center" | "left" | undefined` - default: `"left"` - description: - \`left\`: 라벨을 왼쪽 정렬합니다. - \`center\`: 라벨을 중앙 정렬합니다. ## Examples \[#examples] ### Trigger \[#trigger] ``는 `aria-haspopup="dialog"` 속성을 설정하고, SwipeableMenuSheet의 `open` 상태에 따라 `aria-expanded` 속성을 자동으로 설정합니다. 이 속성은 스크린 리더와 같은 보조 기술에 유용합니다. `MenuSheet`과 달리, swipe-down 제스처로도 시트를 닫을 수 있어요. 시트 상단의 핸들이 드래그 affordance를 제공합니다. `SwipeableMenuSheetContent`의 `showCloseButton` prop으로 보이는 닫기 버튼을 표시할 수 있으며, 기본값(`false`)에서는 스크린 리더 사용자를 위해 시각적으로 숨겨진 닫기 버튼이 렌더링됩니다. ## Preview ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, SwipeableMenuSheetTrigger, } from "seed-design/ui/swipeable-menu-sheet"; const SwipeableMenuSheetPreview = () => { return ( Open } /> } /> } /> } /> } tone="critical" /> ); }; export default SwipeableMenuSheetPreview; ``` ### Controlled \[#controlled] Trigger 외의 방식으로 SwipeableMenuSheet를 열고 닫을 수 있습니다. 이 경우 `open` prop을 사용하여 SwipeableMenuSheet의 상태를 제어합니다. ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, } from "seed-design/ui/swipeable-menu-sheet"; const SwipeableMenuSheetControlled = () => { const [open, setOpen] = useState(false); const scheduleOpen = () => { setTimeout(() => { setOpen(true); }, 1000); }; return ( <> 1초 후 열기 } /> } /> } /> ); }; export default SwipeableMenuSheetControlled; ``` ### With Title \[#with-title] `SwipeableMenuSheetContent`의 `title` prop을 사용하여 시트 헤더에 제목을 표시합니다. ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, SwipeableMenuSheetTrigger, } from "seed-design/ui/swipeable-menu-sheet"; const SwipeableMenuSheetWithTitle = () => { return ( Open } /> } description="Ut nulla et id dolor labore ullamco irure est id occaecat." /> } description="Ut nulla et id dolor labore ullamco irure est id occaecat." /> } /> } tone="critical" /> ); }; export default SwipeableMenuSheetWithTitle; ``` ### With Title and Description \[#with-title-and-description] `SwipeableMenuSheetContent`의 `description` prop을 사용하여 제목 아래에 부가 설명을 추가합니다. ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, SwipeableMenuSheetTrigger, } from "seed-design/ui/swipeable-menu-sheet"; export default function SwipeableMenuSheetWithTitleAndDescription() { return ( Open } /> } description="Ut nulla et id dolor labore ullamco irure est id occaecat." /> } description="Ut nulla et id dolor labore ullamco irure est id occaecat." /> } /> } tone="critical" /> ); } ``` ### Close Button \[#close-button] `SwipeableMenuSheetContent`의 `showCloseButton` prop으로 시트 하단에 보이는 닫기 버튼을 표시합니다. 기본값은 `false`이며, 이때는 스크린 리더 사용자를 위한 시각적으로 숨겨진 닫기 버튼이 렌더링됩니다. ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, SwipeableMenuSheetTrigger, } from "seed-design/ui/swipeable-menu-sheet"; const SwipeableMenuSheetWithCloseButton = () => { return ( Open } /> } /> } /> ); }; export default SwipeableMenuSheetWithCloseButton; ``` ### Label Align \[#label-align] `SwipeableMenuSheetContent`의 `labelAlign` prop으로 메뉴 항목의 레이블 정렬을 설정합니다. #### `labelAlign="left"` (with `PrefixIcon`) \[#labelalignleft-with-prefixicon] 레이블을 왼쪽 정렬합니다. ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, SwipeableMenuSheetTrigger, } from "seed-design/ui/swipeable-menu-sheet"; const SwipeableMenuSheetWithPrefixIcon = () => { return ( Open } /> } /> } /> } /> } tone="critical" /> ); }; export default SwipeableMenuSheetWithPrefixIcon; ``` #### `labelAlign="center"` (without `PrefixIcon`) \[#labelaligncenter-without-prefixicon] 레이블을 중앙 정렬합니다. (일반적으로, `prefixIcon` 없는 경우) ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, SwipeableMenuSheetTrigger, } from "seed-design/ui/swipeable-menu-sheet"; const SwipeableMenuSheetWithoutPrefixIcon = () => { return ( Open ); }; export default SwipeableMenuSheetWithoutPrefixIcon; ``` #### Overriding `labelAlign` \[#overriding-labelalign] 필요한 경우 `SwipeableMenuSheetContent`에 지정한 `labelAlign`을 `SwipeableMenuSheetGroup` 또는 `SwipeableMenuSheetItem`에 지정한 `labelAlign`으로 덮어쓸 수 있습니다. ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, SwipeableMenuSheetTrigger, } from "seed-design/ui/swipeable-menu-sheet"; const SwipeableMenuSheetOverridingLabelAlign = () => { return ( Open ); }; export default SwipeableMenuSheetOverridingLabelAlign; ``` ### `onOpenChange` Details \[#onopenchange-details] `onOpenChange` 두 번째 인자로 `details`가 제공됩니다. #### `reason` \[#reason] **열릴 때** (`open: true`) - `"trigger"`: `SwipeableMenuSheetTrigger` (`SwipeableMenuSheet.Trigger`)로 열림 **닫힐 때** (`open: false`) - `"closeButton"`: 닫기 버튼(`SwipeableMenuSheet.CloseButton`)이 활성화되어 닫힘 (`showCloseButton`이 `false`인 경우 시각적으로 숨겨진 버튼이며, 주로 스크린 리더 사용자) - `"escapeKeyDown"`: ESC 키 사용 - `"interactOutside"`: 외부 영역 클릭 - `"drag"`: swipe 제스처로 닫힘 ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { HStack, Text, VStack } from "@seed-design/react"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, SwipeableMenuSheetTrigger, } from "seed-design/ui/swipeable-menu-sheet"; export default function SwipeableMenuSheetOnOpenChangeReason() { const [open, setOpen] = useState(false); const [openReason, setOpenReason] = useState(null); const [closeReason, setCloseReason] = useState(null); return ( { setOpen(open); (open ? setOpenReason : setCloseReason)(details?.reason ?? null); }} > 열기 } /> } /> } /> 마지막 열림 이유: {openReason ?? "-"} 마지막 닫힘 이유: {closeReason ?? "-"} ); } ``` ### Skip Animation \[#skip-animation] `skipAnimation` prop을 사용하여 SwipeableMenuSheet의 enter/exit 애니메이션을 건너뛸 수 있습니다. ```tsx import { IconEyeSlashLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { SwipeableMenuSheetContent, SwipeableMenuSheetGroup, SwipeableMenuSheetItem, SwipeableMenuSheetRoot, SwipeableMenuSheetTrigger, } from "seed-design/ui/swipeable-menu-sheet"; const SwipeableMenuSheetSkipAnimation = () => { return ( Open } /> } /> } /> ); }; export default SwipeableMenuSheetSkipAnimation; ``` --- file: components/switch.mdx # Switch 특정 설정 및 상태를 즉시 켜거나 끌 수 있도록 하는 컴포넌트입니다. ## Preview ```tsx import { Switch } from "seed-design/ui/switch"; export default function SwitchPreview() { return ; } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:switch - pnpm: pnpm dlx @seed-design/cli@latest add ui:switch - yarn: yarn dlx @seed-design/cli@latest add ui:switch - bun: bun x @seed-design/cli@latest add ui:switch ## Props \[#props] ### `Switch` \[#switch] - `inputProps` - type: `React.InputHTMLAttributes | undefined` - `rootRef` - type: `React.Ref | undefined` - `label` - type: `React.ReactNode` - `size` - type: `"24" | "16" | "32" | undefined` - default: `32` - `tone` - type: `"brand" | "neutral" | undefined` - default: `"brand"` - `disabled` - type: `boolean | undefined` - `invalid` - type: `boolean | undefined` - `required` - type: `boolean | undefined` - `checked` - type: `boolean | undefined` - `defaultChecked` - type: `boolean | undefined` - `onCheckedChange` - type: `((checked: boolean) => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `Switchmark` \[#switchmark] - `tone` - type: `"brand" | "neutral" | undefined` - default: `"brand"` - `size` - type: `"24" | "16" | "32" | undefined` - default: `32` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Sizes \[#sizes] `size="medium"`과 `size="small"`은 더 이상 사용되지 않습니다. 대신 `size="32"`와 `size="16"`을 사용하세요. ```tsx import { VStack } from "@seed-design/react"; import { Switch } from "seed-design/ui/switch"; export default function SwitchSizes() { return ( ); } ``` ### Tones \[#tones] #### Brand \[#brand] ```tsx import { Switch } from "seed-design/ui/switch"; export default function SwitchBrand() { return ; } ``` #### Neutral \[#neutral] ```tsx import { Switch } from "seed-design/ui/switch"; export default function SwitchNeutral() { return ; } ``` ### Long Label \[#long-label] ```tsx import { VStack } from "@seed-design/react"; import { Switch } from "seed-design/ui/switch"; export default function SwitchLongLabel() { return ( ); } ``` ### Disabled \[#disabled] ```tsx import { VStack } from "@seed-design/react"; import { useState } from "react"; import { Switch } from "seed-design/ui/switch"; export default function SwitchDisabled() { const [disabled, setDisabled] = useState(true); return ( ); } ``` ### Listening to Value Changes \[#listening-to-value-changes] `onCheckedChange`를 사용하여 스위치의 선택 상태 변경을 감지할 수 있습니다. 이벤트를 활용해야 하는 경우 `inputProps`를 통해 내부 `` 요소에 직접 이벤트 핸들러를 추가할 수 있습니다. ```tsx import { VStack, Text } from "@seed-design/react"; import { Switch } from "seed-design/ui/switch"; import { useState } from "react"; export default function SwitchValueChanges() { const [count, setCount] = useState(0); const [lastValue, setLastValue] = useState(null); return ( { setCount((prev) => prev + 1); setLastValue(checked); }} /> onCheckedChange called: {count} times, last value: {`${lastValue ?? "-"}`} ); } ``` ### Use Cases \[#use-cases] #### Using `Switchmark` \[#using-switchmark] `Switchmark`는 레이블을 제외한 스위치 컴포넌트로, 커스텀 레이아웃을 위해 사용할 수 있습니다. ```tsx import { HStack, Text, VStack } from "@seed-design/react"; import { Switch } from "@seed-design/react/primitive"; import { Switchmark } from "seed-design/ui/switch"; function CustomSwitch({ children, ...props }: Switch.RootProps) { return ( {children} ); } export default function () { return ( regular medium bold ); } ``` --- file: components/tabs.mdx # Tabs 한 화면 내에서 콘텐츠를 탭 단위로 구분하여 전환할 수 있는 컴포넌트입니다. ## Preview ```tsx import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsPreview() { return (
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    ); } const Content = (props: React.PropsWithChildren) => { return (
    {props.children}
    ); }; ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:tabs - pnpm: pnpm dlx @seed-design/cli@latest add ui:tabs - yarn: yarn dlx @seed-design/cli@latest add ui:tabs - bun: bun x @seed-design/cli@latest add ui:tabs ## Props \[#props] ### `TabsRoot` \[#tabsroot] - `triggerLayout` - type: `"fill" | "hug" | undefined` - default: `"fill"` - `contentLayout` - type: `"fill" | "hug" | undefined` - default: `"hug"` - `size` - type: `"small" | "medium" | undefined` - default: `"small"` - `stickyList` - type: `boolean | undefined` - default: `false` - `orientation` - type: `"horizontal" | "vertical" | undefined` - `value` - type: `string | undefined` - `defaultValue` - type: `string | undefined` - `onValueChange` - type: `((value: string) => void) | undefined` - `lazyMount` - type: `boolean | undefined` - default: `false` - description: If \`true\`, the component will be mounted lazily. - `unmountOnExit` - type: `boolean | undefined` - default: `false` - description: If \`true\`, the component will be unmounted when it's not selected. - `present` - type: `boolean | undefined` - default: `false` - description: If \`true\`, the component will be mounted. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `TabsList` \[#tabslist] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `TabsTrigger` \[#tabstrigger] - `notification` - type: `boolean | undefined` - `disabled` - type: `boolean | undefined` - `value` - type: `string` - required: `true` ### `TabsCarousel` \[#tabscarousel] - `swipeable` - type: `boolean | undefined` - `autoHeight` - type: `boolean | undefined` - `loop` - type: `boolean | undefined` - `dragThreshold` - type: `number | undefined` - `onSettle` - type: `(() => void) | undefined` - `onSwipeStart` - type: `(() => void) | undefined` - description: 스와이프 시작 시 호출됩니다. - `onSwipeEnd` - type: `(() => void) | undefined` - description: 스와이프 종료 시 호출됩니다. ### `TabsContent` \[#tabscontent] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `value` - type: `string` - required: `true` ## Examples \[#examples] ### Layout Fill (Default) \[#layout-fill-default] ```tsx import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsLayoutFill() { return (
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    ); } const Content = (props: React.PropsWithChildren) => { return (
    {props.children}
    ); }; ``` ### Layout Hug \[#layout-hug] ```tsx import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsLayoutHug() { return (
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    ); } const Content = (props: React.PropsWithChildren) => { return (
    {props.children}
    ); }; ``` ### Size Medium \[#size-medium] ```tsx import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsSizeMedium() { return (
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    ); } const Content = (props: React.PropsWithChildren) => { return (
    {props.children}
    ); }; ``` ### Size Small (Default) \[#size-small-default] ```tsx import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsSizeSmall() { return (
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    ); } const Content = (props: React.PropsWithChildren) => { return (
    {props.children}
    ); }; ``` ### Transition \[#transition] `TabsCarousel` 컴포넌트로 `TabsContent` 컴포넌트들을 감싸면 탭 변경시 컨텐츠가 자연스럽게 전환됩니다. ```tsx import { TabsRoot, TabsList, TabsTrigger, TabsCarousel, TabsContent } from "seed-design/ui/tabs"; export default function TabsTransition() { return (
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    ); } const Content = (props: React.PropsWithChildren) => { return (
    {props.children}
    ); }; ``` ### Swipeable \[#swipeable] `TabsCarousel` 컴포넌트에 `swipeable` 속성을 추가하면 스와이프 제스처로 탭을 이동할 수 있습니다. ```tsx import { TabsRoot, TabsList, TabsTrigger, TabsCarousel, TabsContent } from "seed-design/ui/tabs"; export default function TabsSwipeable() { return (
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    ); } const Content = (props: React.PropsWithChildren) => { return (
    {props.children}
    ); }; ``` ### Carousel Prevent Drag \[#carousel-prevent-drag] `Tabs.carouselPreventDrag`를 사용하면 특정 요소가 드래그 제스처에 반응하지 않도록 설정할 수 있습니다. ```tsx import { Box, Tabs } from "@seed-design/react"; import { TabsCarousel, TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsCarouselPreventDrag() { return ( Tab 1 Tab 2 Scrollable area Lorem ipsum dolor sit, amet consectetur adipisicing elit. Reiciendis ab ex accusantium sit. Amet maxime eum molestiae nemo. Beatae sint omnis aut cumque doloremque fugit perspiciatis rerum possimus, reiciendis eaque? Lorem ipsum dolor sit amet consectetur adipisicing elit. ); } ``` ### Disabled \[#disabled] ```tsx import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsDisabled() { return (
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    ); } const Content = (props: React.PropsWithChildren) => { return (
    {props.children}
    ); }; ``` ### Notification \[#notification] ```tsx import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsNotification() { return (
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    ); } const Content = (props: React.PropsWithChildren) => { return (
    {props.children}
    ); }; ``` ### Sticky List \[#sticky-list] 탭이 전체화면을 차지하고, Tabs.List가 top에 고정되어 있는 경우 사용하는 예시입니다. ```tsx import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsStickyList() { return ( // 600은 화면 높이라고 가정합니다.
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    ); } const Content = (props: React.PropsWithChildren<{ height: string }>) => { const { height, children } = props; return (
    {children}
    ); }; ``` ### Standalone \[#standalone] TabContent를 사용하지 않고, 컨텐츠 영역을 온전히 소유하고 싶을 때 사용하는 예시입니다. 탭에서 제공하는 Swipe 기능을 사용할 수 없습니다. ```tsx import { useState } from "react"; import { TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsStandalone() { const [activeTab, setActiveTab] = useState("1"); return (
    라벨1 라벨2 라벨3 {activeTab === "1" && (
    Content 1
    )} {activeTab === "2" && (
    Content 2
    )} {activeTab === "3" && (
    Content 3
    )}
    ); } const Content = (props: React.PropsWithChildren) => { return (
    {props.children}
    ); }; ``` ### Dynamic Height \[#dynamic-height] 각 탭의 높이가 다를 때, 아래의 컨텐츠를 탭 아래에 바로 맞추기 위해서 사용하는 예시입니다. 탭이 자주 바뀌고, 탭에 네트워크 요청이 많은 경우 캐싱을 잘 고려해주세요. ```tsx import { TabsCarousel, TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsDynamicHeight() { return (
    라벨1 라벨2 라벨3 Content 1 Content 2 Content 3
    아래 컨텐츠
    ); } const Content = (props: React.PropsWithChildren<{ height: string }>) => { const { height, children } = props; return (
    {children}
    ); }; ``` ### Scroll to Top \[#scroll-to-top] ```tsx import { RefObject, useRef, useState } from "react"; import { TabsCarousel, TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; export default function TabsScrollTop() { const [currentTab, setCurrentTab] = useState("1"); const contentRefs: Record> = { "1": useRef(null), "2": useRef(null), }; const handleTriggerClick = (value: string) => { if (value === currentTab) { contentRefs[value].current?.scrollTo({ top: 0, behavior: "smooth" }); } }; return (
    handleTriggerClick("1")} value="1"> 라벨1 handleTriggerClick("2")} value="2"> 라벨2 Content 1 Content 2
    ); } const Content = (props: React.PropsWithChildren<{ height: string }>) => { const { height, children } = props; return (
    {children}
    ); }; ``` --- file: components/tag-group.mdx # Tag Group 아이콘과 텍스트 태그를 수평으로 나열해 여러 속성·상태·메타데이터를 한눈에 보여주는 정보 요약 컴포넌트입니다. ## Preview ```tsx import { IconLocationpinFill } from "@karrotmarket/react-monochrome-icon"; import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; export default function TagGroupPreview() { return ( } label="500m" /> ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:tag-group - pnpm: pnpm dlx @seed-design/cli@latest add ui:tag-group - yarn: yarn dlx @seed-design/cli@latest add ui:tag-group - bun: bun x @seed-design/cli@latest add ui:tag-group ## Props \[#props] ### `TagGroupRoot` \[#taggrouproot] - `separator` - type: `ReactNode` - `size` - type: `"t2" | "t3" | "t4" | undefined` - default: `"t2"` - `truncate` - type: `boolean | undefined` - default: `false` - `weight` - type: `"bold" | "regular" | undefined` - default: `"regular"` - `tone` - type: `"brand" | "neutral" | "neutralSubtle" | undefined` - default: `"neutralSubtle"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### `TagGroupItem` \[#taggroupitem] - `size` - type: `"t2" | "t3" | "t4" | undefined` - default: `"t2"` - `weight` - type: `"bold" | "regular" | undefined` - default: `"regular"` - `tone` - type: `"brand" | "neutral" | "neutralSubtle" | undefined` - default: `"neutralSubtle"` - `flexShrink` - type: `true | 0 | (number & {}) | undefined` - description: If true, flex-shrink will be set to \`1\`. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Sizes \[#sizes] ```tsx import { VStack } from "@seed-design/react"; import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; export default function TagGroupSizes() { return ( ); } ``` ### Weights \[#weights] ```tsx import { VStack } from "@seed-design/react"; import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; export default function TagGroupWeights() { return ( ); } ``` ### Tones \[#tones] ```tsx import { VStack } from "@seed-design/react"; import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; export default function TagGroupTones() { return ( ); } ``` ### With Icons \[#with-icons] ```tsx import { IconLocationpinFill, IconMegaphoneFill } from "@karrotmarket/react-monochrome-icon"; import { VStack } from "@seed-design/react"; import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; export default function TagGroupWithIcons() { return ( } /> } label="서초4동" /> ); } ``` ### Customizing `TagGroupItem` \[#customizing-taggroupitem] `TagGroupRoot`에 지정한 `size`, `tone`, `weight`은 `TagGroupItem`에서 덮어씌울 수 있습니다. 필요한 경우 [컴파운드 컴포넌트](/react/components/concepts/snippet#compound-components)를 직접 사용하여 `TagGroupItem` 내부 요소를 커스터마이징할 수 있습니다. ```tsx import { IconCheckmarkCircleFill, IconHeartFill, IconHorizline2VerticalChatbubbleRectangularRightFill, IconStarFill, } from "@karrotmarket/react-monochrome-icon"; import { Icon, VStack } from "@seed-design/react"; import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; import { TagGroup as SeedTagGroup } from "@seed-design/react"; export default function TagGroupCustomizingItem() { return ( {/* You can use the compound components to customize the items */} } color="fg.brand" /> 4.5 } label="인증됨" /> } label="10" /> } label="3" /> ); } ``` ### Customizing Separators \[#customizing-separators] `TagGroupRoot`에 `separator`를 지정하여 구분 기호를 변경할 수 있습니다. 스크린 리더는 각 태그 항목 사이에 지정된 구분 기호를 읽지 않습니다. 의미가 있는(semantic) 정보를 Separator로 사용하는 것을 피하세요. ```tsx import { VStack } from "@seed-design/react"; import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; export default function TagGroupCustomizingSeparators() { return ( ); } ``` ### Wrapping Behavior \[#wrapping-behavior] 기본적으로 `TagGroupRoot`는 컨테이너 너비를 초과하면 줄바꿈이 발생합니다. `TagGroupRoot`에 `truncate` prop을 지정하면 `TagGroupRoot`가 한 줄로 유지되고, 컨테이너 너비를 초과하는 경우 기본적으로 모든 `TagGroupItem`의 레이블에서 말줄임이 발생할 수 있습니다. `truncate` prop을 사용하면서 특정 아이템이 축소되지 않도록 하려면 해당 `TagGroupItem`에 `flexShrink={0}`을 지정하세요. `TagGroupItem`에 `flexShrink={number}`를 지정하여 컨테이너 너비를 초과할 때 축소되는 우선순위를 조정할 수 있습니다. ```tsx import { IconBellFill, IconLocationpinFill } from "@karrotmarket/react-monochrome-icon"; import { Flex, Text, VStack } from "@seed-design/react"; import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; import type { PropsWithChildren, ReactNode } from "react"; export default function TagGroupWrappingBehavior() { return ( } label="부산광역시 해운대구" /> } label="123 456 789 012 345" /> } label="부산광역시 해운대구" /> } label="123 456 789 012 345" /> } label="부산광역시 해운대구" /> } label="123 456 789 012 345" /> } flexShrink={1} label="부산광역시 해운대구" /> } label="123 456 789 012 345" /> ); } function Wrapper({ title, children }: PropsWithChildren<{ title: ReactNode }>) { return ( {title} {children} ); } ``` --- file: components/text-field-input.mdx # Text Field Input 한 줄 텍스트를 입력받는 컴포넌트입니다. ## Preview ```tsx import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldPreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:text-field - pnpm: pnpm dlx @seed-design/cli@latest add ui:text-field - yarn: yarn dlx @seed-design/cli@latest add ui:text-field - bun: bun x @seed-design/cli@latest add ui:text-field ## Props \[#props] ### `TextField` \[#textfield] - `label` - type: `React.ReactNode` - `labelWeight` - type: `"bold" | "medium" | undefined` - default: `"medium"` - `indicator` - type: `React.ReactNode` - `prefixIcon` - type: `React.ReactNode` - `prefix` - type: `React.ReactNode` - `suffixIcon` - type: `React.ReactNode` - `suffix` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `errorMessage` - type: `React.ReactNode` - `hideCharacterCount` - type: `boolean | undefined` - `maxGraphemeCount` - type: `number | undefined` - `showRequiredIndicator` - type: `boolean | undefined` - `fieldRef` - type: `React.Ref | undefined` - `onValueChange` - type: `((values: { value: string; graphemes: string[]; slicedValue: string; slicedGraphemes: string[]; }) => void) | undefined` - `defaultValue` - type: `string | undefined` - `disabled` - type: `boolean | undefined` - default: `false` - `variant` - type: `"outline" | "underline" | undefined` - default: `"outline"` - description: - \`outline\`: 기본 스타일입니다. - \`underline\`: 화면에 하나의 Input만 있는 경우 사용을 권장합니다. - `size` - type: `"medium" | "responsive" | "large" | undefined` - default: `"large"` - description: - \`large\`: 뷰포트 너비와 관계없이 사용할 수 있습니다. - \`medium\`: Breakpoint \`lg\` 이상(데스크톱)에서만 사용하고, 모바일에서는 사용하지 않습니다. 정밀한 선택이 가능한 마우스 입력 환경에서 사이즈를 더 작게 만들고자 할 때 사용합니다. - \`responsive\`: 뷰포트 너비에 따라 적용되는 사이즈가 달라집니다. Breakpoint \`lg\` 미만에서는 \`large\`, \`lg\` 이상에서는 \`medium\`으로 적용됩니다. - `name` - type: `string | undefined` - `value` - type: `string | undefined` - `invalid` - type: `boolean | undefined` - default: `false` - `readOnly` - type: `boolean | undefined` - default: `false` - `required` - type: `boolean | undefined` - default: `false` ### `TextFieldInput` \[#textfieldinput] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### State \[#state] #### Enabled \[#enabled] ```tsx import { HStack, VStack } from "@seed-design/react"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldInputEnabled() { return ( ); } ``` #### Disabled \[#disabled] ```tsx import { HStack, VStack } from "@seed-design/react"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldInputDisabled() { return ( ); } ``` #### Read Only \[#read-only] ```tsx import { HStack, VStack } from "@seed-design/react"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldInputReadOnly() { return ( ); } ``` ### Size \[#size] `size`로 TextField의 크기를 정합니다. (default: `large`) `responsive`는 화면 너비에 따라 size가 자동으로 전환되는 값입니다. 여러 화면 너비를 함께 지원하는 제품에서 `size=responsive`를 사용하여 대응합니다. ```tsx import { HStack, VStack } from "@seed-design/react"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldInputSize() { return ( ); } ``` ### Customizable Parts \[#customizable-parts] 아이콘만으로 맥락을 전달하려고 할 때 유의하세요. `description` 또는 `label`에 아이콘의 의미를 명확히 설명하는 텍스트를 포함하거나, 아이콘이 스크린 리더에 의해 읽히도록 설정하고 `aria-label`을 제공하세요. #### Prefix \[#prefix] ```tsx import { IconMagnifyingglassLine } from "@karrotmarket/react-monochrome-icon"; import { HStack, VStack } from "@seed-design/react"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldInputPrefix() { return ( } > } > ); } ``` #### Suffix \[#suffix] ```tsx import { IconWonLine } from "@karrotmarket/react-monochrome-icon"; import { HStack, VStack } from "@seed-design/react"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldInputSuffix() { return ( }> }> ); } ``` #### Both Affixes \[#both-affixes] ```tsx import { IconPlusCircleLine, IconWonLine } from "@karrotmarket/react-monochrome-icon"; import { HStack, VStack } from "@seed-design/react"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldInputBothAffixes() { return ( } suffixIcon={} > } suffixIcon={} > ); } ``` #### Indicator \[#indicator] `indicator` 또는 `showRequiredIndicator` prop을 사용할 수 있습니다. ```tsx import { HStack } from "@seed-design/react"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldInputIndicator() { return ( ); } ``` #### Grapheme Count \[#grapheme-count] ```tsx import { TextField, TextFieldInput } from "seed-design/ui/text-field"; import { HStack } from "@seed-design/react"; export default function TextFieldInputGraphemeCount() { return ( ); } ``` 자소 단위로 쪼개진 `value`에 관한 정보를 `onValueChange` 콜백에서 `graphemes`와 `slicedGraphemes`로 제공합니다. 자소 분리는 [unicode-segmenter](https://github.com/cometkim/unicode-segmenter)를 통해 이루어집니다. ```tsx import { TextField, TextFieldInput } from "seed-design/ui/text-field"; import { useState } from "react"; import { Text, VStack } from "@seed-design/react"; export default function TextFieldInputGraphemeControlled() { const [value, setValue] = useState(""); const [graphemes, setGraphemes] = useState([]); return ( { setValue(slicedValue); setGraphemes(slicedGraphemes); }} >
    graphemes.length: {graphemes.length} value.length: {value.length} graphemes: {JSON.stringify(graphemes)} value: {value}
    ); } ``` ### Use Cases \[#use-cases] #### Form (Uncontrolled) \[#form-uncontrolled] ```tsx import { HStack, VStack } from "@seed-design/react"; import { useCallback, useState, type FormEvent } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; type FieldErrors = { name?: string; address?: string; }; export default function TextFieldInputForm() { const [fieldErrors, setFieldErrors] = useState({}); const handleSubmit = useCallback((event: FormEvent) => { event.preventDefault(); const formData = new FormData(event.currentTarget); const name = formData.get("name")?.toString(); const address = formData.get("address")?.toString(); const newFieldErrors: FieldErrors = {}; if (!name) { newFieldErrors.name = "필수 입력 항목입니다"; } if (!address) { newFieldErrors.address = "필수 입력 항목입니다"; } if (address && !address.startsWith("대한민국")) { newFieldErrors.address = "대한민국으로 시작해주세요"; } setFieldErrors(newFieldErrors); if (Object.keys(newFieldErrors).length > 0) return; window.alert(JSON.stringify(Object.fromEntries(formData.entries()), null, 2)); }, []); return (
    제출
    ); } ``` #### React Hook Form \[#react-hook-form] ```tsx import { HStack, VStack } from "@seed-design/react"; import { useCallback, type FormEvent } from "react"; import { useController, useForm } from "react-hook-form"; import { ActionButton } from "seed-design/ui/action-button"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; interface FormValues { name: string; address: string; } export default function TextFieldInputReactHookForm() { const { handleSubmit, reset, control } = useForm({ reValidateMode: "onSubmit", defaultValues: { name: "", address: "", }, }); const { field: { onChange: nameOnChange, ...nameField }, fieldState: nameFieldState, } = useController({ name: "name", control, rules: { required: "필수 입력 항목입니다", }, }); const { field: { onChange: addressOnChange, ...addressField }, fieldState: addressFieldState, } = useController({ name: "address", control, rules: { required: "필수 입력 항목입니다", pattern: { value: /^대한민국/, message: "대한민국으로 시작해주세요" }, }, }); const onValid = useCallback((data: FormValues) => { window.alert(JSON.stringify(data, null, 2)); }, []); const onReset = useCallback( (event: FormEvent) => { event.preventDefault(); reset(); }, [reset], ); return ( nameOnChange(value)} required showRequiredIndicator {...nameField} > addressOnChange(slicedValue)} required showRequiredIndicator {...addressField} > 초기화 제출 ); } ``` #### Number Formatting \[#number-formatting] ```tsx import { useMemo, useState } from "react"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldInputNumberFormatting() { const [value, setValue] = useState("1000"); const formattedValue = useMemo(() => { if (value === "") return value; const number = Number(value.replace(/,/g, "")); if (Number.isNaN(number)) return ""; return number.toLocaleString(); }, [value]); return ( setValue(value)} > ); } ``` #### Slicing \[#slicing] ```tsx import { useState } from "react"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; export default function TextFieldInputSlicing() { const [value, setValue] = useState(""); return ( setValue(slicedValue)} > ); } ``` --- file: components/text-field-textarea.mdx # Text Field Textarea 여러 줄의 긴 텍스트를 입력받고 자동으로 높이를 조절하는 컴포넌트입니다. ## Preview ```tsx import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function MultilineTextFieldPreview() { return ( ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:text-field - pnpm: pnpm dlx @seed-design/cli@latest add ui:text-field - yarn: yarn dlx @seed-design/cli@latest add ui:text-field - bun: bun x @seed-design/cli@latest add ui:text-field ## Props \[#props] ### `TextField` \[#textfield] - `label` - type: `React.ReactNode` - `labelWeight` - type: `"bold" | "medium" | undefined` - default: `"medium"` - `indicator` - type: `React.ReactNode` - `prefixIcon` - type: `React.ReactNode` - `prefix` - type: `React.ReactNode` - `suffixIcon` - type: `React.ReactNode` - `suffix` - type: `React.ReactNode` - `description` - type: `React.ReactNode` - `errorMessage` - type: `React.ReactNode` - `hideCharacterCount` - type: `boolean | undefined` - `maxGraphemeCount` - type: `number | undefined` - `showRequiredIndicator` - type: `boolean | undefined` - `fieldRef` - type: `React.Ref | undefined` - `onValueChange` - type: `((values: { value: string; graphemes: string[]; slicedValue: string; slicedGraphemes: string[]; }) => void) | undefined` - `defaultValue` - type: `string | undefined` - `disabled` - type: `boolean | undefined` - default: `false` - `variant` - type: `"outline" | "underline" | undefined` - default: `"outline"` - description: - \`outline\`: 기본 스타일입니다. - \`underline\`: 화면에 하나의 Input만 있는 경우 사용을 권장합니다. - `size` - type: `"medium" | "responsive" | "large" | undefined` - default: `"large"` - description: - \`large\`: 뷰포트 너비와 관계없이 사용할 수 있습니다. - \`medium\`: Breakpoint \`lg\` 이상(데스크톱)에서만 사용하고, 모바일에서는 사용하지 않습니다. 정밀한 선택이 가능한 마우스 입력 환경에서 사이즈를 더 작게 만들고자 할 때 사용합니다. - \`responsive\`: 뷰포트 너비에 따라 적용되는 사이즈가 달라집니다. Breakpoint \`lg\` 미만에서는 \`large\`, \`lg\` 이상에서는 \`medium\`으로 적용됩니다. - `name` - type: `string | undefined` - `value` - type: `string | undefined` - `invalid` - type: `boolean | undefined` - default: `false` - `readOnly` - type: `boolean | undefined` - default: `false` - `required` - type: `boolean | undefined` - default: `false` ### `TextFieldTextarea` \[#textfieldtextarea] - `autoresize` - type: `boolean | undefined` - default: `true` - description: If true, the textarea will automatically resize based on its content. - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### State \[#state] #### Enabled \[#enabled] ```tsx import { HStack } from "@seed-design/react"; import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function TextFieldTextareaEnabled() { return ( ); } ``` #### Disabled \[#disabled] ```tsx import { HStack } from "@seed-design/react"; import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function TextFieldTextareaDisabled() { return ( ); } ``` #### Read Only \[#read-only] ```tsx import { HStack } from "@seed-design/react"; import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function TextFieldTextareaReadOnly() { return ( ); } ``` ### Size \[#size] `size`로 TextField의 크기를 정합니다. (default: `large`) `responsive`는 화면 너비에 따라 size가 자동으로 전환되는 값입니다. 여러 화면 너비를 함께 지원하는 제품에서 `size=responsive`를 사용하여 대응합니다. ```tsx import { HStack, VStack } from "@seed-design/react"; import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function TextFieldTextareaSize() { return ( ); } ``` ### Sizing \[#sizing] ``에 `height` 관련 스타일을 직접 지정하여 높이를 고정하거나 최소/최대 높이를 설정할 수 있습니다. #### Fixed Height \[#fixed-height] ```tsx import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function TextFieldTextareaSpecifiedHeight() { return ( ); } ``` #### Auto Height with Constraints \[#auto-height-with-constraints] ```tsx import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function TextFieldTextareaConstraints() { return ( ); } ``` ### Customizable Parts \[#customizable-parts] #### Indicator \[#indicator] `indicator` 또는 `showRequiredIndicator` prop을 사용할 수 있습니다. ```tsx import { HStack } from "@seed-design/react"; import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function TextFieldTextareaIndicator() { return ( ); } ``` #### Grapheme Count \[#grapheme-count] ```tsx import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function TextFieldTextareaGraphemeCount() { return ( ); } ``` 자소 단위로 쪼개진 `value`에 관한 정보를 `onValueChange` 콜백에서 `graphemes`와 `slicedGraphemes`로 제공합니다. 자소 분리는 [unicode-segmenter](https://github.com/cometkim/unicode-segmenter)를 통해 이루어집니다. ```tsx import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; import { useState } from "react"; import { Text, VStack } from "@seed-design/react"; export default function TextFieldTextareaGraphemeControlled() { const [value, setValue] = useState(""); const [graphemes, setGraphemes] = useState([]); return ( { setValue(slicedValue); setGraphemes(slicedGraphemes); }} >
    graphemes.length: {graphemes.length} value.length: {value.length} graphemes: {JSON.stringify(graphemes)} value: {value}
    ); } ``` ### Use Cases \[#use-cases] #### Form (Uncontrolled) \[#form-uncontrolled] ```tsx import { HStack, VStack } from "@seed-design/react"; import { useCallback, useState, type FormEvent } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; type FieldErrors = { bio?: string; address?: string; }; export default function TextFieldTextareaForm() { const [fieldErrors, setFieldErrors] = useState({}); const handleSubmit = useCallback((event: FormEvent) => { event.preventDefault(); const formData = new FormData(event.currentTarget); const bio = formData.get("bio")?.toString(); const address = formData.get("address")?.toString(); const newFieldErrors: FieldErrors = {}; if (!bio) { newFieldErrors.bio = "필수 입력 항목입니다"; } if (!address) { newFieldErrors.address = "필수 입력 항목입니다"; } if (address && !address.startsWith("대한민국")) { newFieldErrors.address = "대한민국으로 시작해주세요"; } setFieldErrors(newFieldErrors); if (Object.keys(newFieldErrors).length > 0) return; window.alert(JSON.stringify(Object.fromEntries(formData.entries()), null, 2)); }, []); return (
    제출
    ); } ``` #### React Hook Form \[#react-hook-form] ```tsx import { HStack, VStack } from "@seed-design/react"; import { useCallback, type FormEvent, type KeyboardEvent } from "react"; import { useController, useForm } from "react-hook-form"; import { ActionButton } from "seed-design/ui/action-button"; import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; interface FormValues { bio: string; address: string; } export default function TextFieldTextareaReactHookForm() { const { handleSubmit, reset, control } = useForm({ reValidateMode: "onSubmit", defaultValues: { bio: "", address: "", }, }); const { field: { onChange: bioOnChange, ...bioField }, fieldState: bioFieldState, } = useController({ name: "bio", control, rules: { required: "필수 입력 항목입니다", }, }); const { field: { onChange: addressOnChange, ...addressField }, fieldState: addressFieldState, } = useController({ name: "address", control, rules: { required: "필수 입력 항목입니다", pattern: { value: /^대한민국/, message: "대한민국으로 시작해주세요" }, }, }); const onValid = useCallback( (data: FormValues) => window.alert(JSON.stringify(data, null, 2)), [], ); const onReset = useCallback( (event: FormEvent) => { event.preventDefault(); reset(); }, [reset], ); const onMetaReturn = useCallback( (event: KeyboardEvent) => { if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) { event.preventDefault(); handleSubmit(onValid)(); } }, [handleSubmit, onValid], ); return ( bioOnChange(value)} required showRequiredIndicator {...bioField} > addressOnChange(slicedValue)} required showRequiredIndicator {...addressField} > 초기화 제출 ); } ``` #### Formatting \[#formatting] ```tsx import { useMemo, useState } from "react"; import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function TextFieldTextareaFormatting() { const [value, setValue] = useState(""); const formattedValue = useMemo( () => value .split("") .filter((char) => char !== " ") .join(""), [value], ); return ( setValue(value)} > ); } ``` #### Slicing \[#slicing] ```tsx import { useState } from "react"; import { TextField, TextFieldTextarea } from "seed-design/ui/text-field"; export default function TextFieldTextareaSlicing() { const [value, setValue] = useState(""); return ( setValue(slicedValue)} > ); } ``` --- file: components/toggle-button.mdx # Toggle Button 사용자가 특정 상태를 켜거나 끌 수 있게 해주는 버튼 형태의 컴포넌트입니다. 필터링이나 뷰 전환과 같은 즉각적인 상태 변경에 사용됩니다. ## Preview ```tsx import { useState } from "react"; import { ToggleButton } from "seed-design/ui/toggle-button"; export default function ToggleButtonPreview() { const [pressed, setPressed] = useState(false); return ( {pressed ? "선택됨" : "미선택"} ); } ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:toggle-button - pnpm: pnpm dlx @seed-design/cli@latest add ui:toggle-button - yarn: yarn dlx @seed-design/cli@latest add ui:toggle-button - bun: bun x @seed-design/cli@latest add ui:toggle-button ## Props \[#props] - `variant` - type: `"brandSolid" | "neutralWeak" | undefined` - default: `"brandSolid"` - description: - \`brandSolid\`: 브랜드 컬러로 강조된 스타일입니다. - \`neutralWeak\`: 기본적인 토글 스타일입니다. - `size` - type: `"small" | "xsmall" | undefined` - default: `"small"` - `loading` - type: `boolean | undefined` - default: `false` - description: 버튼에 등록된 비동기 작업이 진행 중임을 사용자에게 알립니다. - `disabled` - type: `boolean | undefined` - default: `false` - description: 버튼의 비활성화 여부를 나타냅니다. - `pressed` - type: `boolean | undefined` - `defaultPressed` - type: `boolean | undefined` - `onPressedChange` - type: `((pressed: boolean) => void) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Brand Solid \[#brand-solid] ```tsx import { useState } from "react"; import { ToggleButton } from "seed-design/ui/toggle-button"; export default function ToggleButtonBrandSolid() { const [pressed, setPressed] = useState(false); return ( {pressed ? "선택됨" : "미선택"} ); } ``` ### Neutral Weak \[#neutral-weak] ```tsx import { useState } from "react"; import { ToggleButton } from "seed-design/ui/toggle-button"; export default function ToggleButtonBrandSolid() { const [pressed, setPressed] = useState(false); return ( {pressed ? "선택됨" : "미선택"} ); } ``` ### Small \[#small] ```tsx import { useState } from "react"; import { ToggleButton } from "seed-design/ui/toggle-button"; export default function ToggleButtonSmall() { const [pressed, setPressed] = useState(false); return ( {pressed ? "선택됨" : "미선택"} ); } ``` ### Xsmall \[#xsmall] ```tsx import { useState } from "react"; import { ToggleButton } from "seed-design/ui/toggle-button"; export default function ToggleButtonXsmall() { const [pressed, setPressed] = useState(false); return ( {pressed ? "선택됨" : "미선택"} ); } ``` ### Prefix Icon \[#prefix-icon] ```tsx import { IconCheckmarkLine, IconPlusLine } from "@karrotmarket/react-monochrome-icon"; import { PrefixIcon } from "@seed-design/react"; import { useState } from "react"; import { ToggleButton } from "seed-design/ui/toggle-button"; export default function ToggleButtonPrefixIcon() { const [pressed, setPressed] = useState(false); return ( : } /> {pressed ? "선택됨" : "미선택"} ); } ``` ### Disabled \[#disabled] ```tsx import { ToggleButton } from "seed-design/ui/toggle-button"; export default function ToggleButtonDisabled() { return 비활성; } ``` ### Loading \[#loading] ```tsx import { useState } from "react"; import { ToggleButton } from "seed-design/ui/toggle-button"; export default function ToggleButtonLoading() { const [pressed, setPressed] = useState(false); const [loading, setLoading] = useState(false); function handleToggle() { setLoading(true); setTimeout(() => { setLoading(false); setPressed((prev) => !prev); }, 2000); } // 이벤트 핸들링이 필요할 수 있으므로 loading은 disabled를 포함하지 않습니다. 이벤트 발생을 원하지 않는 경우, disabled 속성을 추가해주세요. return ( 시간이 걸리는 토글 ); } ``` --- file: developer-tools/codemods/available-transforms.mdx # Available Transforms codemod를 통해 실행 가능한 변환 목록입니다. {/* Auto-generated from `scripts/generate-codemod-docs.ts` */} ## replace-custom-seed-design-typography \[#replace-custom-seed-design-typography] - npm: npx @seed-design/codemod@latest replace-custom-seed-design-typography \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-custom-seed-design-typography \ - yarn: yarn dlx @seed-design/codemod@latest replace-custom-seed-design-typography \ - bun: bun x @seed-design/codemod@latest replace-custom-seed-design-typography \ ```ts title="basic.input.ts" // @ts-nocheck import { style } from "@vanilla-extract/css"; import { vars } from "@seed-design/css"; import { f, reset } from "@/shared/styles"; import { typography, text } from '@/src/styles/tokens'; export const title = style([ typography.h4, { textAlign: "center", color: vars.$scale.color.gray900, margin: 0, }, ]); export const subtitle = style([ f.typography.title2Bold, { marginBottom: "0.375rem", color: vars.$scale.color.gray900, }, ]); export const smallText = style([ typo.caption2Regular, { color: vars.$scale.color.gray700, }, ]); export const largeTitle = style([ typo.bodyL1Regular, { color: vars.$scale.color.gray900, }, ]); export const button = style([ typography['$semantic.label2Regular'], { width: '100%', paddingTop: rem(14), paddingRight: rem(16), paddingBottom: rem(14), paddingLeft: rem(16), }, ]); export const day = style([ { display: 'flex', flex: 1, alignItems: 'center', justifyContent: 'center', height: rem(30), }, typography['$semantic.caption1Regular'], ]); // Additional test cases for various mappings export const titleBold = style([ typography.title3Bold, { fontWeight: 'bold', }, ]); export const subtitleRegular = style([ f.typography.subtitle2Regular, { color: vars.$scale.color.gray800, }, ]); export const bodyText = style([ typo.bodyM1Regular, { lineHeight: 1.5, }, ]); export const labelText = style([ typography['$semantic.label1Bold'], { textTransform: 'uppercase', }, ]); export const captionBold = style([ typography['$semantic.caption2Bold'], { fontSize: '12px', }, ]); // Test deprecated tokens with alternative export const deprecatedBody = style([ typo.bodyL2Regular, { margin: '8px 0', }, ]); export const deprecatedLabel = style([ typography['$semantic.label6Regular'], { opacity: 0.8, }, ]); // Test multiple token choices (should use first one) export const headerText = style([ typography.h4, { textAlign: 'center', }, ]); export const text = style([ text['$semantic.bodyL2Bold'], ]); ``` ```ts title="basic.output.ts" // @ts-nocheck import { style } from "@vanilla-extract/css"; import { vars } from "@seed-design/css"; import { f, reset } from "@/shared/styles"; import { typography, text } from '@/src/styles/tokens'; export const title = style([ typography.t10Bold, { textAlign: "center", color: vars.$scale.color.gray900, margin: 0, }, ]); export const subtitle = style([ f.typography.t7Bold, { marginBottom: "0.375rem", color: vars.$scale.color.gray900, }, ]); export const smallText = style([ typo.t2Regular, { color: vars.$scale.color.gray700, }, ]); export const largeTitle = style([ typo.articleBody, { color: vars.$scale.color.gray900, }, ]); export const button = style([ typography.t5Regular, { width: '100%', paddingTop: rem(14), paddingRight: rem(16), paddingBottom: rem(14), paddingLeft: rem(16), }, ]); export const day = style([ { display: 'flex', flex: 1, alignItems: 'center', justifyContent: 'center', height: rem(30), }, typography.t3Regular, ]); // Additional test cases for various mappings export const titleBold = style([ typography.t6Bold, { fontWeight: 'bold', }, ]); export const subtitleRegular = style([ f.typography.t4Regular, { color: vars.$scale.color.gray800, }, ]); export const bodyText = style([ typo.t5Regular, { lineHeight: 1.5, }, ]); export const labelText = style([ typography.t6Bold, { textTransform: 'uppercase', }, ]); export const captionBold = style([ typography.t2Bold, { fontSize: '12px', }, ]); // Test deprecated tokens with alternative export const deprecatedBody = style([ typo.t4Regular, { margin: '8px 0', }, ]); export const deprecatedLabel = style([ typography.t1Regular, { opacity: 0.8, }, ]); // Test multiple token choices (should use first one) export const headerText = style([ typography.t10Bold, { textAlign: 'center', }, ]); export const text = style([ text.t4Bold, ]); ``` ## replace-tailwind-typography \[#replace-tailwind-typography] - npm: npx @seed-design/codemod@latest replace-tailwind-typography \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-tailwind-typography \ - yarn: yarn dlx @seed-design/codemod@latest replace-tailwind-typography \ - bun: bun x @seed-design/codemod@latest replace-tailwind-typography \ ```tsx title="basic.input.tsx" // @ts-nocheck export function BasicExample() { return (
    Primary Background

    {formState.content}

    Hover Primary Low Background
    Focus Primary Low Background
    Focus Primary Low Background

    {description}

    Text with prefix
    Another text with prefix

    Title with prefix

    ); } ``` ```tsx title="basic.output.tsx" // @ts-nocheck export function BasicExample() { return (
    Primary Background

    {formState.content}

    Hover Primary Low Background
    Focus Primary Low Background
    Focus Primary Low Background

    {description}

    Text with prefix
    Another text with prefix

    Title with prefix

    ); } ```
    ## replace-custom-imported-typography-variable \[#replace-custom-imported-typography-variable] Sprout에서 SEED로 마이그레이션 시 import된 타이포그래피 변수를 변환합니다. ### 기능 \[#기능] - 타이포그래피 관련 import 문을 찾아 변수명을 변환합니다. - 변수가 사용된 모든 위치(템플릿 리터럴, 객체 속성 등)를 찾아 업데이트합니다. - typography.mjs의 매핑 정보에 따라 Sprout 타이포그래피 변수를 SEED로 변환합니다. - 같은 SEED 토큰으로 변환되는 중복 import를 제거합니다 (예: subtitle1Regular, bodyM1Regular가 모두 t5Regular로 변환되면 t5Regular는 한 번만 import). - 별칭(alias)으로 import된 변수명은 유지합니다 (예: `bodyM1Regular as customTypo`에서 `t5Regular as customTypo`로 변환). * npm: npx @seed-design/codemod@latest replace-custom-imported-typography-variable \ * pnpm: pnpm dlx @seed-design/codemod@latest replace-custom-imported-typography-variable \ * yarn: yarn dlx @seed-design/codemod@latest replace-custom-imported-typography-variable \ * bun: bun x @seed-design/codemod@latest replace-custom-imported-typography-variable \ ```ts title="basic.input.ts" // @ts-nocheck import { subtitle1Regular, subtitle2Regular, h4, title1Bold, title2Bold, bodyL1Regular, bodyL2Regular, caption1Regular, caption2Regular, label5Regular, } from '@src/constants/typography' import { bodyM1Regular as customTypo } from '@karrot/typography' const S_StoreRequestTitle = styled.h1` ${subtitle1Regular}; margin: 0 0 0.25rem; color: ${vars.$scale.color.gray900}; text-align: center; ` const S_StoreRequestText = styled.p` ${subtitle2Regular}; margin: 0; color: ${vars.$scale.color.gray600}; text-align: center; ` // 추가 테스트 케이스 // screenTitle 매핑 테스트 const S_ScreenHeader = styled.h1` ${h4}; margin-bottom: 1rem; ` // 일반 매핑 테스트 const S_Title = styled.h2` ${title1Bold}; color: ${vars.$scale.color.gray900}; ` // 여러 테스트 케이스를 담은 컴포넌트 const Card = styled.div` // title2Bold 매핑 테스트 h3 { ${title2Bold}; margin-bottom: 8px; } // bodyL1Regular 매핑 테스트 p.description { ${bodyL1Regular}; color: ${vars.$scale.color.gray800}; } // 대체 토큰으로 매핑되는 케이스 p.content { ${bodyL2Regular}; margin: 0; } // 캡션 스타일 p.caption { ${caption1Regular}; color: ${vars.$scale.color.gray600}; } // 작은 텍스트 small { ${caption2Regular}; color: ${vars.$scale.color.gray500}; } // 라벨 스타일 label { ${label5Regular}; margin-right: 4px; } ` // 다른 모듈에서 가져온 타이포그래피 처리 (별칭 유지해야 함) const S_CustomContent = styled.div` ${customTypo}; color: ${vars.$scale.color.gray700}; ` // 변수로 사용하는 경우 const titleStyle = title1Bold; const textStyle = subtitle1Regular; ``` ```ts title="basic.output.ts" // @ts-nocheck import { t5Regular, t4Regular, t10Bold, t9Bold, t7Bold, articleBody, t3Regular, t2Regular, t1Regular, } from '@src/constants/typography'; import { t5Regular as customTypo } from '@karrot/typography' const S_StoreRequestTitle = styled.h1` ${t5Regular}; margin: 0 0 0.25rem; color: ${vars.$scale.color.gray900}; text-align: center; ` const S_StoreRequestText = styled.p` ${t4Regular}; margin: 0; color: ${vars.$scale.color.gray600}; text-align: center; ` // 추가 테스트 케이스 // screenTitle 매핑 테스트 const S_ScreenHeader = styled.h1` ${t10Bold}; margin-bottom: 1rem; ` // 일반 매핑 테스트 const S_Title = styled.h2` ${t9Bold}; color: ${vars.$scale.color.gray900}; ` // 여러 테스트 케이스를 담은 컴포넌트 const Card = styled.div` // title2Bold 매핑 테스트 h3 { ${t7Bold}; margin-bottom: 8px; } // bodyL1Regular 매핑 테스트 p.description { ${articleBody}; color: ${vars.$scale.color.gray800}; } // 대체 토큰으로 매핑되는 케이스 p.content { ${t4Regular}; margin: 0; } // 캡션 스타일 p.caption { ${t3Regular}; color: ${vars.$scale.color.gray600}; } // 작은 텍스트 small { ${t2Regular}; color: ${vars.$scale.color.gray500}; } // 라벨 스타일 label { ${t1Regular}; margin-right: 4px; } ` // 다른 모듈에서 가져온 타이포그래피 처리 (별칭 유지해야 함) const S_CustomContent = styled.div` ${customTypo}; color: ${vars.$scale.color.gray700}; ` // 변수로 사용하는 경우 const titleStyle = t9Bold; const textStyle = t5Regular; ``` ## replace-custom-seed-design-vars \[#replace-custom-seed-design-vars] - npm: npx @seed-design/codemod@latest replace-custom-seed-design-vars \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-custom-seed-design-vars \ - yarn: yarn dlx @seed-design/codemod@latest replace-custom-seed-design-vars \ - bun: bun x @seed-design/codemod@latest replace-custom-seed-design-vars \ ```ts title="basic.input.ts" // @ts-nocheck import { vars } from '@/shared/style/vars'; export const date = style({ ...vars.typography.caption1Regular, color: vars.color.gray600, }); export const color1 = style({ color: vars.color.yellow500, }); export const color2 = style({ color: vars.color.blue500, }); export const color3 = style({ color: vars.color.red500, }); export const title = style({ ...vars.typography.bodyM1Bold, color: vars.color.primary, }); export const subtitle = style({ ...vars.typography.bodyM2Regular, color: vars.color.secondary, }); // 배경색 케이스 export const cardBackground = style({ backgroundColor: vars.color.paperSheet, padding: '16px', }); // 테두리 케이스 export const divider = style({ borderTop: `1px solid ${vars.color.divider1}`, marginTop: '8px', marginBottom: '8px', }); // 상태 색상 케이스 export const successMessage = style({ ...vars.typography.bodyM1Bold, color: vars.color.success, backgroundColor: vars.color.successLow, padding: '12px', borderRadius: '4px', }); export const warningMessage = style({ ...vars.typography.bodyM1Bold, color: vars.color.warning, backgroundColor: vars.color.warningLow, padding: '12px', borderRadius: '4px', }); export const errorMessage = style({ ...vars.typography.bodyM1Bold, color: vars.color.danger, backgroundColor: vars.color.dangerLow, padding: '12px', borderRadius: '4px', }); // 다양한 타이포그래피 케이스 export const heading = style({ ...vars.typography.h4, color: vars.color.inkText, }); export const body = style({ ...vars.typography.bodyL1Regular, color: vars.color.inkTextLow, }); export const label = style({ ...vars.typography.label3Bold, color: vars.color.grayActive, }); // 알파 색상 케이스 export const overlay = style({ backgroundColor: vars.color.overlayDim, position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, }); // 복합 케이스와 상호작용 상태 export const button = style({ ...vars.typography.label2Bold, backgroundColor: vars.color.primary, color: vars.color.onPrimary, borderRadius: '4px', padding: '8px 16px', border: 'none', cursor: 'pointer', ':hover': { backgroundColor: vars.color.primaryHover, }, ':active': { backgroundColor: vars.color.primaryPressed, }, }); export const secondaryButton = style({ ...vars.typography.label2Bold, backgroundColor: vars.color.secondaryLow, color: vars.color.secondary, borderRadius: '4px', padding: '8px 16px', border: 'none', cursor: 'pointer', ':hover': { backgroundColor: vars.color.grayHover, }, ':active': { backgroundColor: vars.color.grayPressed, }, }); // 중첩 경로 케이스 export const floatingPanel = style({ backgroundColor: vars.color.paperFloating, boxShadow: `0 2px 8px ${vars.color.overlayLow}`, padding: '20px', borderRadius: '8px', }); export const dialogPanel = style({ backgroundColor: vars.color.paperDialog, padding: '24px', boxShadow: `0 4px 16px ${vars.color.overlayDim}`, borderRadius: '12px', }); // 정적 색상 케이스 export const staticColors = style({ color: vars.color.staticBlack, backgroundColor: vars.color.staticWhite, border: `1px solid ${vars.color.staticBlackAlpha200}`, }); // 정적 색상 케이스 (명시적으로 static 프리픽스 테스트) export const staticExplicitColors = style({ color: vars.color.staticBlack, backgroundColor: vars.color.staticWhite, boxShadow: `0 2px 4px ${vars.color.staticBlackAlpha500}`, outline: `1px solid ${vars.color.staticBlackAlpha200}`, }); export const specialBackground = style({ background: `linear-gradient(to bottom, ${vars.color.staticWhiteAlpha50}, ${vars.color.staticWhiteAlpha200})`, backdropFilter: 'blur(8px)', }); // 복합 스타일 및 다중 속성 케이스 export const multiProperty = style({ ...vars.typography.title2Bold, color: vars.color.accent, borderLeft: `4px solid ${vars.color.accent}`, borderRight: `4px solid ${vars.color.accent}`, boxShadow: `inset 0 0 0 1px ${vars.color.divider2}, 0 2px 4px ${vars.color.overlayLow}`, padding: '16px', backgroundColor: vars.color.paperDefault, }); // 믹스인 스타일 케이스 export const mixinStyle = { primary: { background: vars.color.primary, color: vars.color.onPrimary, }, secondary: { background: vars.color.secondary, }, accent: { background: vars.color.accent, color: vars.color.onGrayOverlay50, }, }; // 특수 속성 및 스케일 색상 케이스 export const scaleColors = style({ fill: vars.color.carrot500, stroke: vars.color.gray400, stopColor: vars.color.blue400, floodColor: vars.color.red400, lightingColor: vars.color.green400, }); // 특수 타이포그래피 케이스 export const specialTypographies = { title: vars.typography.title1Bold, subtitle: vars.typography.subtitle1Regular, label: vars.typography.label5Regular, caption: vars.typography.caption2Bold, }; // 다양한 경로를 가진 컬러 토큰 export const colorContexts = style({ // 일반 색상 color: vars.color.primaryLow, // 배경 색상 backgroundColor: vars.color.paperBackground, // 테두리 색상 borderColor: vars.color.divider3, // 호버 색상 ':hover': { backgroundColor: vars.color.primaryLowHover, borderColor: vars.color.primaryLowActive, }, }); // 복잡한 다중 경로와 중첩된 객체 export const nestedStyleObject = { button: { default: { backgroundColor: vars.color.paperAccent, color: vars.color.inkText, border: `1px solid ${vars.color.carrot300}`, }, hover: { backgroundColor: vars.color.carrot100, borderColor: vars.color.carrot400, }, active: { backgroundColor: vars.color.carrot200, color: vars.color.inkTextLow, }, }, panel: { header: { ...vars.typography.subtitle2Bold, color: vars.color.blue600, borderBottom: `1px solid ${vars.color.blue200}`, }, body: { ...vars.typography.bodyM2Regular, color: vars.color.inkTextLow, backgroundColor: vars.color.blue50, }, footer: { backgroundColor: vars.color.blue100, borderTop: `1px solid ${vars.color.blue200}`, }, }, }; // 테두리 속성 매핑 테스트 export const borderMapping = style({ // stroke 토큰으로 매핑될 속성 borderColor: vars.color.divider2, // stroke 토큰이 없어서 fg 토큰으로 대체되는 케이스 borderTopColor: vars.color.primary, borderBottomColor: vars.color.accent, // stroke 또는 fg 토큰으로 대체 boxShadow: `0 0 0 1px ${vars.color.primary}, 0 2px 4px ${vars.color.overlayLow}`, // 복합 속성 border: `1px solid ${vars.color.secondary}`, }); ``` ```ts title="basic.output.ts" // @ts-nocheck import { vars } from "@/shared/style/vars"; export const date = style({ ...vars.typography.t3Regular, color: vars.color.palette.gray700, }); export const color1 = style({ color: vars.color.palette.yellow700, }); export const color2 = style({ color: vars.color.palette.blue600, }); export const color3 = style({ color: vars.color.palette.red700, }); export const title = style({ ...vars.typography.t5Bold, color: vars.color.fg.brand, }); export const subtitle = style({ ...vars.typography.t4Regular, color: vars.color.palette.gray900, }); // 배경색 케이스 export const cardBackground = style({ backgroundColor: vars.color.bg.layerFloating, padding: "16px", }); // 테두리 케이스 export const divider = style({ borderTop: `1px solid ${vars.color.stroke.neutralSubtle}`, marginTop: "8px", marginBottom: "8px", }); // 상태 색상 케이스 export const successMessage = style({ ...vars.typography.t5Bold, color: vars.color.fg.positive, backgroundColor: vars.color.bg.positiveWeak, padding: "12px", borderRadius: "4px", }); export const warningMessage = style({ ...vars.typography.t5Bold, color: vars.color.bg.warningSolid, backgroundColor: vars.color.bg.warningWeak, padding: "12px", borderRadius: "4px", }); export const errorMessage = style({ ...vars.typography.t5Bold, color: vars.color.fg.critical, backgroundColor: vars.color.bg.criticalWeak, padding: "12px", borderRadius: "4px", }); // 다양한 타이포그래피 케이스 export const heading = style({ ...vars.typography.t10Bold, color: vars.color.fg.neutral, }); export const body = style({ ...vars.typography.articleBody, color: vars.color.fg.neutralSubtle, }); export const label = style({ ...vars.typography.t4Bold, color: vars.color.fg.neutralMuted, }); // 알파 색상 케이스 export const overlay = style({ backgroundColor: vars.color.bg.overlay, position: "absolute", top: 0, left: 0, right: 0, bottom: 0, }); // 복합 케이스와 상호작용 상태 export const button = style({ ...vars.typography.t5Bold, backgroundColor: vars.color.bg.brandSolid, color: vars.color.palette.staticWhite, borderRadius: "4px", padding: "8px 16px", border: "none", cursor: "pointer", ":hover": { backgroundColor: vars.color.bg.brandSolidPressed, }, ":active": { backgroundColor: vars.color.bg.brandSolidPressed, }, }); export const secondaryButton = style({ ...vars.typography.t5Bold, backgroundColor: vars.color.bg.neutralWeak, color: vars.color.palette.gray900, borderRadius: "4px", padding: "8px 16px", border: "none", cursor: "pointer", ":hover": { backgroundColor: vars.color.bg.neutralWeakPressed, }, ":active": { backgroundColor: vars.color.bg.neutralWeakPressed, }, }); // 중첩 경로 케이스 export const floatingPanel = style({ backgroundColor: vars.color.bg.layerFloating, boxShadow: `0 2px 8px ${vars.color.bg.overlayMuted}`, padding: "20px", borderRadius: "8px", }); export const dialogPanel = style({ backgroundColor: vars.color.bg.layerFloating, padding: "24px", boxShadow: `0 4px 16px ${vars.color.bg.overlay}`, borderRadius: "12px", }); // 정적 색상 케이스 export const staticColors = style({ color: vars.color.palette.staticBlack, backgroundColor: vars.color.palette.staticWhite, border: `1px solid ${vars.color.palette.staticBlackAlpha200}`, }); // 정적 색상 케이스 (명시적으로 static 프리픽스 테스트) export const staticExplicitColors = style({ color: vars.color.palette.staticBlack, backgroundColor: vars.color.palette.staticWhite, boxShadow: `0 2px 4px ${vars.color.palette.staticBlackAlpha500}`, outline: `1px solid ${vars.color.palette.staticBlackAlpha200}`, }); export const specialBackground = style({ background: `linear-gradient(to bottom, ${vars.color.palette.staticWhiteAlpha50}, ${vars.color.palette.staticWhiteAlpha200})`, backdropFilter: "blur(8px)", }); // 복합 스타일 및 다중 속성 케이스 export const multiProperty = style({ ...vars.typography.t7Bold, color: vars.color.fg.informative, borderLeft: `4px solid ${vars.color.fg.informative}`, borderRight: `4px solid ${vars.color.fg.informative}`, boxShadow: `inset 0 0 0 1px ${vars.color.stroke.neutral}, 0 2px 4px ${vars.color.bg.overlayMuted}`, padding: "16px", backgroundColor: vars.color.bg.layerDefault, }); // 믹스인 스타일 케이스 export const mixinStyle = { primary: { background: vars.color.bg.brandSolid, color: vars.color.palette.staticWhite, }, secondary: { background: vars.color.palette.gray900, }, accent: { background: vars.color.bg.informativeSolid, color: vars.color.stroke.onImage, }, }; // 특수 속성 및 스케일 색상 케이스 export const scaleColors = style({ fill: vars.color.palette.carrot600, stroke: vars.color.palette.gray500, stopColor: vars.color.palette.blue400, floodColor: vars.color.palette.red600, lightingColor: vars.color.palette.green500, }); // 특수 타이포그래피 케이스 export const specialTypographies = { title: vars.typography.t9Bold, subtitle: vars.typography.t5Regular, label: vars.typography.t1Regular, caption: vars.typography.t2Bold, }; // 다양한 경로를 가진 컬러 토큰 export const colorContexts = style({ // 일반 색상 color: vars.color.palette.carrot100, // 배경 색상 backgroundColor: vars.color.bg.layerBasement, // 테두리 색상 borderColor: vars.color.palette.gray400, // 호버 색상 ":hover": { backgroundColor: vars.color.palette.carrot200, borderColor: vars.color.palette.carrot100, }, }); // 복잡한 다중 경로와 중첩된 객체 export const nestedStyleObject = { button: { default: { backgroundColor: vars.color.palette.carrot100, color: vars.color.fg.neutral, border: `1px solid ${vars.color.palette.carrot400}`, }, hover: { backgroundColor: vars.color.palette.carrot200, borderColor: vars.color.palette.carrot500, }, active: { backgroundColor: vars.color.palette.carrot300, color: vars.color.fg.neutralSubtle, }, }, panel: { header: { ...vars.typography.t4Bold, color: vars.color.palette.blue600, borderBottom: `1px solid ${vars.color.palette.blue300}`, }, body: { ...vars.typography.t4Regular, color: vars.color.fg.neutralSubtle, backgroundColor: vars.color.palette.blue100, }, footer: { backgroundColor: vars.color.palette.blue200, borderTop: `1px solid ${vars.color.palette.blue300}`, }, }, }; // 테두리 속성 매핑 테스트 export const borderMapping = style({ // stroke 토큰으로 매핑될 속성 borderColor: vars.color.stroke.neutral, // stroke 토큰이 없어서 fg 토큰으로 대체되는 케이스 borderTopColor: vars.color.fg.brand, borderBottomColor: vars.color.fg.informative, // stroke 또는 fg 토큰으로 대체 boxShadow: `0 0 0 1px ${vars.color.bg.brandSolid}, 0 2px 4px ${vars.color.bg.overlayMuted}`, // 복합 속성 border: `1px solid ${vars.color.palette.gray900}`, }); ``` ## replace-stitches-styled-typography \[#replace-stitches-styled-typography] - npm: npx @seed-design/codemod@latest replace-stitches-styled-typography \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-stitches-styled-typography \ - yarn: yarn dlx @seed-design/codemod@latest replace-stitches-styled-typography \ - bun: bun x @seed-design/codemod@latest replace-stitches-styled-typography \ ```tsx title="basic.input.tsx" // @ts-nocheck const ContentText = styled('p', { ...userSelectText, overflow: 'hidden', whiteSpace: 'pre-wrap', wordBreak: 'break-word', color: '$gray900', variants: { size: { medium: { $text: 'bodyM1Regular', }, large: { $text: 'bodyL1Regular', }, xlarge: { $text: "h4", }, }, }, }); const ContentText2 = styled('p', { ...userSelectText, overflow: 'hidden', whiteSpace: 'pre-wrap', wordBreak: 'break-word', color: '$gray900', variants: { size: { medium: { $typography: 'bodyM1Regular', }, large: { $typography: 'bodyL1Regular', }, xlarge: { $typography: "h4", }, }, }, }); ``` ```tsx title="basic.output.tsx" // @ts-nocheck const ContentText = styled('p', { ...userSelectText, overflow: 'hidden', whiteSpace: 'pre-wrap', wordBreak: 'break-word', color: '$gray900', variants: { size: { medium: { $text: "t5Regular", }, large: { $text: "articleBody", }, xlarge: { $text: "t10Bold", }, }, }, }); const ContentText2 = styled('p', { ...userSelectText, overflow: 'hidden', whiteSpace: 'pre-wrap', wordBreak: 'break-word', color: '$gray900', variants: { size: { medium: { $typography: "t5Regular", }, large: { $typography: "articleBody", }, xlarge: { $typography: "t10Bold", }, }, }, }); ``` ## replace-stitches-theme-color \[#replace-stitches-theme-color] - npm: npx @seed-design/codemod@latest replace-stitches-theme-color \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-stitches-theme-color \ - yarn: yarn dlx @seed-design/codemod@latest replace-stitches-theme-color \ - bun: bun x @seed-design/codemod@latest replace-stitches-theme-color \ ```tsx title="basic.input.tsx" // @ts-nocheck import { theme } from "@src/stitches/stitches.config"; const semanticColors = { primary: theme.colors["primary-semantic"].computedValue, onPrimary: theme.colors["onPrimary-semantic"].computedValue, primaryLow: theme.colors["primaryLow-semantic"].computedValue, success: theme.colors["success-semantic"].computedValue, warning: theme.colors["warning-semantic"].computedValue, danger: theme.colors["danger-semantic"].computedValue, paperDefault: theme.colors["paperDefault-semantic"].computedValue, paperContents: theme.colors["paperContents-semantic"].computedValue, paperDialog: theme.colors["paperDialog-semantic"].computedValue, inkText: theme.colors["inkText-semantic"].computedValue, inkTextLow: theme.colors["inkTextLow-semantic"].computedValue, divider1: theme.colors["divider1-semantic"].computedValue, divider2: theme.colors["divider2-semantic"].computedValue, overlayDim: theme.colors["overlayDim-semantic"].computedValue, accent: theme.colors["accent-semantic"].computedValue, }; const scaleColors = { gray00: theme.colors.gray00.computedValue, gray100: theme.colors.gray100.computedValue, gray200: theme.colors.gray200.computedValue, gray300: theme.colors.gray300.computedValue, gray400: theme.colors.gray400.computedValue, gray500: theme.colors.gray500.computedValue, gray600: theme.colors.gray600.computedValue, gray700: theme.colors.gray700.computedValue, gray800: theme.colors.gray800.computedValue, gray900: theme.colors.gray900.computedValue, carrot100: theme.colors.carrot100.computedValue, carrot500: theme.colors.carrot500.computedValue, carrot900: theme.colors.carrot900.computedValue, carrotAlpha50: theme.colors.carrotAlpha50.computedValue, carrotAlpha100: theme.colors.carrotAlpha100.computedValue, carrotAlpha200: theme.colors.carrotAlpha200.computedValue, blue300: theme.colors.blue300.computedValue, blue600: theme.colors.blue600.computedValue, red500: theme.colors.red500.computedValue, }; const staticColors = { white: theme.colors["white-static"].computedValue, black: theme.colors["black-static"].computedValue, blackAlpha200: theme.colors["blackAlpha200-static"].computedValue, whiteAlpha200: theme.colors["whiteAlpha200-static"].computedValue, gray900: theme.colors["gray900-static"].computedValue, carrot50: theme.colors["carrot50-static"].computedValue, carrot800: theme.colors["carrot800-static"].computedValue, blue50: theme.colors["blue50-static"].computedValue, blue800: theme.colors["blue800-static"].computedValue, red50: theme.colors["red50-static"].computedValue, red800: theme.colors["red800-static"].computedValue, }; const Component = () => { return (
    안내사항 중요
    ); }; const Dialog = ({ isOpen, onClose }) => { const styles = { overlay: { backgroundColor: theme.colors["overlayDim-semantic"].computedValue, }, container: { backgroundColor: theme.colors["paperDialog-semantic"].computedValue, borderColor: theme.colors.gray300.computedValue, }, header: { borderBottom: `1px solid ${theme.colors["divider1-semantic"].computedValue}`, }, title: { color: theme.colors.gray900.computedValue, }, content: { color: theme.colors.gray700.computedValue, backgroundColor: theme.colors["paperContents-semantic"].computedValue, }, footer: { borderTop: `1px solid ${theme.colors["divider2-semantic"].computedValue}`, }, closeButton: { color: theme.colors.gray500.computedValue, }, submitButton: { backgroundColor: theme.colors.blue600.computedValue, color: theme.colors["white-static"].computedValue, }, }; return isOpen ? (

    다이얼로그 제목

    다이얼로그 내용
    ) : null; }; export { semanticColors, scaleColors, staticColors, Component, Dialog }; ``` ```tsx title="basic.output.tsx" // @ts-nocheck import { theme } from "@src/stitches/stitches.config"; const semanticColors = { primary: theme.colors["bg-brand-solid"].computedValue, onPrimary: theme.colors["palette-static-white"].computedValue, primaryLow: theme.colors["palette-carrot-100"].computedValue, success: theme.colors["bg-positive-solid"].computedValue, warning: theme.colors["bg-warning-solid"].computedValue, danger: theme.colors["bg-critical-solid"].computedValue, paperDefault: theme.colors["bg-layer-default"].computedValue, paperContents: theme.colors["bg-layer-fill"].computedValue, paperDialog: theme.colors["bg-layer-floating"].computedValue, inkText: theme.colors["fg-neutral"].computedValue, inkTextLow: theme.colors["fg-neutral-subtle"].computedValue, divider1: theme.colors["stroke-neutral-subtle"].computedValue, divider2: theme.colors["stroke-neutral-muted"].computedValue, overlayDim: theme.colors["bg-overlay"].computedValue, accent: theme.colors["bg-informative-solid"].computedValue, }; const scaleColors = { gray00: theme.colors["palette-gray-00"].computedValue, gray100: theme.colors["palette-gray-200"].computedValue, gray200: theme.colors["palette-gray-300"].computedValue, gray300: theme.colors["palette-gray-400"].computedValue, gray400: theme.colors["palette-gray-500"].computedValue, gray500: theme.colors["palette-gray-600"].computedValue, gray600: theme.colors["palette-gray-700"].computedValue, gray700: theme.colors["palette-gray-800"].computedValue, gray800: theme.colors["palette-gray-900"].computedValue, gray900: theme.colors["palette-gray-1000"].computedValue, carrot100: theme.colors["palette-carrot-200"].computedValue, carrot500: theme.colors["palette-carrot-600"].computedValue, carrot900: theme.colors["palette-carrot-800"].computedValue, carrotAlpha50: theme.colors["palette-carrot-100"].computedValue, carrotAlpha100: theme.colors["palette-carrot-200"].computedValue, carrotAlpha200: theme.colors["palette-carrot-200"].computedValue, blue300: theme.colors["palette-blue-400"].computedValue, blue600: theme.colors["palette-blue-600"].computedValue, red500: theme.colors["palette-red-700"].computedValue, }; const staticColors = { white: theme.colors["palette-static-white"].computedValue, black: theme.colors["palette-static-black"].computedValue, blackAlpha200: theme.colors["palette-static-black-alpha-500"].computedValue, whiteAlpha200: theme.colors["palette-static-white-alpha-300"].computedValue, gray900: theme.colors["palette-static-black"].computedValue, carrot50: theme.colors["palette-carrot-100"].computedValue, carrot800: theme.colors["palette-carrot-700"].computedValue, blue50: theme.colors["palette-blue-100"].computedValue, blue800: theme.colors["palette-blue-700"].computedValue, red50: theme.colors["palette-red-100"].computedValue, red800: theme.colors["palette-red-700"].computedValue, }; const Component = () => { return (
    안내사항 중요
    ); }; const Dialog = ({ isOpen, onClose }) => { const styles = { overlay: { backgroundColor: theme.colors["bg-overlay"].computedValue, }, container: { backgroundColor: theme.colors["bg-layer-floating"].computedValue, borderColor: theme.colors["palette-gray-400"].computedValue, }, header: { borderBottom: `1px solid ${theme.colors["stroke-neutral-subtle"].computedValue}`, }, title: { color: theme.colors["palette-gray-1000"].computedValue, }, content: { color: theme.colors["palette-gray-800"].computedValue, backgroundColor: theme.colors["bg-layer-fill"].computedValue, }, footer: { borderTop: `1px solid ${theme.colors["stroke-neutral-muted"].computedValue}`, }, closeButton: { color: theme.colors["palette-gray-600"].computedValue, }, submitButton: { backgroundColor: theme.colors["palette-blue-600"].computedValue, color: theme.colors["palette-static-white"].computedValue, }, }; return isOpen ? (

    다이얼로그 제목

    다이얼로그 내용
    ) : null; }; export { semanticColors, scaleColors, staticColors, Component, Dialog }; ```
    ## replace-css-seed-design-color-variable \[#replace-css-seed-design-color-variable] - npm: npx @seed-design/codemod@latest replace-css-seed-design-color-variable \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-css-seed-design-color-variable \ - yarn: yarn dlx @seed-design/codemod@latest replace-css-seed-design-color-variable \ - bun: bun x @seed-design/codemod@latest replace-css-seed-design-color-variable \ ```css title="basic.input.css" :root[data-seed] { --seed-alert-dialog-backdrop-background: var(--seed-semantic-color-overlay-dim); --seed-alert-dialog-content-background: var(--seed-semantic-color-paper-dialog); --seed-alert-dialog-title-color: var(--seed-scale-color-gray-900); --seed-alert-dialog-description-color: var(--seed-scale-color-gray-900); --seed-box-button-focus-border: 2px solid var(--seed-scale-color-blue-600); box-shadow: 0 0 0 2px var(--seed-scale-color-blue-600); background: linear-gradient(to bottom, var(--seed-scale-color-blue-600), var(--seed-scale-color-blue-700)); } ``` ```css title="basic.output.css" :root[data-seed] { --seed-alert-dialog-backdrop-background: var(--seed-color-bg-overlay); --seed-alert-dialog-content-background: var(--seed-color-bg-layer-floating); --seed-alert-dialog-title-color: var(--seed-color-palette-gray-1000); --seed-alert-dialog-description-color: var(--seed-color-palette-gray-1000); --seed-box-button-focus-border: 2px solid var(--seed-color-palette-blue-600); box-shadow: 0 0 0 2px var(--seed-color-palette-blue-600); background: linear-gradient(to bottom, var(--seed-color-palette-blue-600), var(--seed-color-palette-blue-800)); } ``` ## replace-react-icon \[#replace-react-icon] - npm: npx @seed-design/codemod@latest replace-react-icon \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-react-icon \ - yarn: yarn dlx @seed-design/codemod@latest replace-react-icon \ - bun: bun x @seed-design/codemod@latest replace-react-icon \ ```tsx title="basic.input.tsx" // @ts-nocheck import { IconSellRegular, IconListFill, IconAddFill as AddIconAlias, } from "@seed-design/react-icon"; import IconSellFill from "@seed-design/react-icon/IconSellFill"; import IconAddThin from '@karrotmarket/karrot-ui-icon/lib/react/IconAddThin'; import IconCloseThin from '@karrotmarket/karrot-ui-icon/lib/react/IconCloseThin'; import IconCarRegular from '@karrotmarket/karrot-ui-icon/lib/react/IconCarRegular'; function App() { console.log(IconSellRegular); return ( <> ); } ``` ```tsx title="basic.output.tsx" // @ts-nocheck import { IconPlusSquareLine, IconDothorizline3VerticalFill, IconPlusFill as AddIconAlias, } from "@karrotmarket/react-monochrome-icon"; import IconPlusSquareFill from "@karrotmarket/react-monochrome-icon/IconPlusSquareFill"; import IconPlusLine from "@karrotmarket/react-monochrome-icon/IconPlusLine"; import IconXmarkLine from "@karrotmarket/react-monochrome-icon/IconXmarkLine"; import IconCarFrontsideLine from "@karrotmarket/react-monochrome-icon/IconCarFrontsideLine"; function App() { console.log(IconPlusSquareLine); return ( <> ); } ``` ## replace-seed-design-token-vars \[#replace-seed-design-token-vars] - npm: npx @seed-design/codemod@latest replace-seed-design-token-vars \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-seed-design-token-vars \ - yarn: yarn dlx @seed-design/codemod@latest replace-seed-design-token-vars \ - bun: bun x @seed-design/codemod@latest replace-seed-design-token-vars \ ```tsx title="basic.input.tsx" import { vars as legacyVars } from "@seed-design/design-token"; import { vars } from "@seed-design/css/vars"; import { vars as typoVars } from "@seed-design/css/vars/component/typography"; const color = vars.$color.palette.gray600; const typography = typoVars.textStyleT5Bold.enabled.root; const legacyColor = legacyVars.$static.color.staticWhiteAlpha50; ``` ```tsx title="basic.output.tsx" import { vars as legacyVars } from "@seed-design/design-token"; import { vars } from "@seed-design/css/vars"; import { vars as typoVars } from "@seed-design/css/vars/component/typography"; const color = vars.$color.palette.gray600; const typography = typoVars.textStyleT5Bold.enabled.root; const legacyColor = legacyVars.$static.color.staticWhiteAlpha50; ``` ## replace-semantic-stroke-color \[#replace-semantic-stroke-color] 시맨틱 stroke 컬러 토큰을 v0.2.0에 맞춰 마이그레이션하는 transform입니다. ### 목적 \[#목적] @seed-design/css v0.2.0에서 업데이트된 시맨틱 stroke 컬러 토큰을 자동으로 마이그레이션합니다. 이전 버전의 stroke 토큰들을 새로운 네이밍 규칙에 맞게 변환합니다. ### 변환 매핑 \[#변환-매핑] #### 우선 순위 변환 (1차) \[#우선-순위-변환-1차] ⚠️ **중요**: 다음 변환은 반드시 다른 변환보다 먼저 처리됩니다. | 기존 토큰 | 신규 토큰 | 비고 | | --------------------------------- | ---------------------------------- | ------------------ | | `vars.$color.stroke.neutralMuted` | `vars.$color.stroke.neutralSubtle` | 가장 먼저 마이그레이션해야 합니다 | #### 일반 변환 (2차) \[#일반-변환-2차] | 기존 토큰 | 신규 토큰 | | --------------------------------- | ------------------------------------ | | `vars.$color.stroke.onImage` | `vars.$color.stroke.neutralSubtle` | | `vars.$color.stroke.neutral` | `vars.$color.stroke.neutralMuted` | | `vars.$color.stroke.fieldFocused` | `vars.$color.stroke.neutralContrast` | | `vars.$color.stroke.control` | `vars.$color.stroke.neutralWeak` | | `vars.$color.stroke.field` | `vars.$color.stroke.neutralWeak` | | `vars.$color.stroke.brand` | `vars.$color.stroke.brandWeak` | | `vars.$color.stroke.positive` | `vars.$color.stroke.positiveWeak` | | `vars.$color.stroke.informative` | `vars.$color.stroke.informativeWeak` | | `vars.$color.stroke.warning` | `vars.$color.stroke.warningWeak` | | `vars.$color.stroke.critical` | `vars.$color.stroke.criticalWeak` | ### 지원하는 파일 형식 \[#지원하는-파일-형식] - TypeScript (`.ts`) - TypeScript JSX (`.tsx`) ### 변환 예시 \[#변환-예시] #### 기본 사용 \[#기본-사용] ```typescript // Before import { vars } from "@seed-design/css/vars"; const borderColor = vars.$color.stroke.neutralMuted; const outlineColor = vars.$color.stroke.neutral; // After import { vars } from "@seed-design/css/vars"; const borderColor = vars.$color.stroke.neutralSubtle; const outlineColor = vars.$color.stroke.neutralMuted; ``` #### React 컴포넌트에서의 사용 \[#react-컴포넌트에서의-사용] ```tsx // Before const Component = () => (
    ); // After const Component = () => (
    ); ``` ### 우선 순위의 중요성 \[#우선-순위의-중요성] 이 transform은 **2-pass 방식**으로 동작합니다: 1. **1차 변환**: `neutralMuted` → `neutralSubtle`을 먼저 처리 2. **2차 변환**: `neutral` → `neutralMuted` 등 나머지 변환들을 처리 이 순서가 중요한 이유는 연쇄 변환을 방지하기 위해서입니다. 만약 순서가 바뀌면 `neutralMuted`가 `neutralSubtle`로 변환되기 전에 `neutral`이 `neutralMuted`로 변환될 수 있어 예상치 못한 결과가 나올 수 있습니다. ### 변환되지 않는 토큰들 \[#변환되지-않는-토큰들] 다음 토큰들은 이미 SEED 형식이므로 변환되지 않습니다: - `vars.$color.stroke.brandWeak` - `vars.$color.stroke.brandSolid` - `vars.$color.stroke.neutralSubtle` - `vars.$color.stroke.neutralContrast` - `vars.$color.stroke.neutralWeak` - 기타 `-weak`, `-solid` 접미사를 가진 토큰들 ### 로깅 \[#로깅] transform 실행 시 다음과 같은 로그가 생성됩니다: - `.report/replace-semantic-stroke-color-success.log`: 성공한 변환들 - `.report/replace-semantic-stroke-color-issues.log`: 경고 및 실패 - `.report/replace-semantic-stroke-color-debug.log`: 디버그 정보 * npm: npx @seed-design/codemod@latest replace-semantic-stroke-color \ * pnpm: pnpm dlx @seed-design/codemod@latest replace-semantic-stroke-color \ * yarn: yarn dlx @seed-design/codemod@latest replace-semantic-stroke-color \ * bun: bun x @seed-design/codemod@latest replace-semantic-stroke-color \ ```tsx title="basic.input.tsx" // @ts-nocheck import React from "react"; import { vars } from "@seed-design/css/vars"; // 인라인 스타일에서 stroke 토큰 사용 const Component1 = () => (
    ); // 객체 스타일에서 사용 const styles = { container: { borderColor: vars.$color.stroke.control, border: `1px solid ${vars.$color.stroke.field}`, }, button: { borderColor: vars.$color.stroke.brand, outlineColor: vars.$color.stroke.positive, }, input: { borderColor: vars.$color.stroke.informative, "&:focus": { borderColor: vars.$color.stroke.warning, }, }, error: { borderColor: vars.$color.stroke.critical, }, }; // 삼항 연산자에서 사용 const ConditionalComponent = ({ isActive }: { isActive: boolean }) => (
    ); // 새로운 토큰들 (변경되지 않아야 함) const NewTokensComponent = () => (
    ); ``` ```tsx title="basic.output.tsx" // @ts-nocheck import React from "react"; import { vars } from "@seed-design/css/vars"; // 인라인 스타일에서 stroke 토큰 사용 const Component1 = () => (
    ); // 객체 스타일에서 사용 const styles = { container: { borderColor: vars.$color.stroke.neutralWeak, border: `1px solid ${vars.$color.stroke.neutralWeak}`, }, button: { borderColor: vars.$color.stroke.brandWeak, outlineColor: vars.$color.stroke.positiveWeak, }, input: { borderColor: vars.$color.stroke.informativeWeak, "&:focus": { borderColor: vars.$color.stroke.warningWeak, }, }, error: { borderColor: vars.$color.stroke.criticalWeak, }, }; // 삼항 연산자에서 사용 const ConditionalComponent = ({ isActive }: { isActive: boolean }) => (
    ); // 새로운 토큰들 (변경되지 않아야 함) const NewTokensComponent = () => (
    ); ``` ## replace-custom-seed-design-text-component \[#replace-custom-seed-design-text-component] - npm: npx @seed-design/codemod@latest replace-custom-seed-design-text-component \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-custom-seed-design-text-component \ - yarn: yarn dlx @seed-design/codemod@latest replace-custom-seed-design-text-component \ - bun: bun x @seed-design/codemod@latest replace-custom-seed-design-text-component \ ```tsx title="basic.input.tsx" // @ts-nocheck import Text from 'components/Base/Text'; const Component = () => { return (
    {/* 기존 variant prop 케이스 */} 광고 노출 기준 앱 내 최근 활동 이력을 분석하여 이용자의 관심사와 관련성이 높은 게시글을 노출해요. {/* typography prop + color prop 케이스 */} 앱 내 최근 활동 이력을 분석하여 이용자의 관심사와 관련성이 높은 게시글을 노출해요. 지역 선택하기 지표별 성과 {/* 다양한 typography prop 형태 */} 접두사 없는 토큰 또 다른 토큰 {/* 다양한 color prop 형태 */} 시맨틱 컬러 스태틱 컬러 textColor prop {/* 조건부 표현식 케이스 */} 조건부 variant 조건부 color {/* JSX 표현식 컨테이너 케이스 */} 표현식 컨테이너
    ); }; ``` ```tsx title="basic.output.tsx" // @ts-nocheck import { Text } from "@seed-design/react"; const Component = () => { return (
    {/* 기존 variant prop 케이스 */} 광고 노출 기준 앱 내 최근 활동 이력을 분석하여 이용자의 관심사와 관련성이 높은 게시글을 노출해요. {/* typography prop + color prop 케이스 */} 앱 내 최근 활동 이력을 분석하여 이용자의 관심사와 관련성이 높은 게시글을 노출해요. 지역 선택하기 지표별 성과 {/* 다양한 typography prop 형태 */} 접두사 없는 토큰 또 다른 토큰 {/* 다양한 color prop 형태 */} 시맨틱 컬러 스태틱 컬러 textColor prop {/* 조건부 표현식 케이스 */} 조건부 variant 조건부 color {/* JSX 표현식 컨테이너 케이스 */} 표현식 컨테이너
    ); }; ```
    ## replace-seed-design-token-typography-classname \[#replace-seed-design-token-typography-classname] - npm: npx @seed-design/codemod@latest replace-seed-design-token-typography-classname \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-seed-design-token-typography-classname \ - yarn: yarn dlx @seed-design/codemod@latest replace-seed-design-token-typography-classname \ - bun: bun x @seed-design/codemod@latest replace-seed-design-token-typography-classname \ ```tsx title="basic.input.tsx" // @ts-nocheck import { classNames } from "@seed-design/design-token"; import { style } from "@vanilla-extract/css"; const typography = { one: classNames.$semantic.typography.title2Regular, two: classNames.$semantic.typography.label3Regular, three: classNames.$semantic.typography.label5Regular, four: classNames.$semantic.typography.label6Regular, } export const footer = style([ classNames.$semantic.typography.caption1Regular, { padding: "16px", marginTop: "12px", color: vars.$scale.color.gray700, backgroundColor: vars.$semantic.color.paperContents, }, ]); ``` ```tsx title="basic.output.tsx" // @ts-nocheck import { text } from "@seed-design/css/recipes/text"; import { style } from "@vanilla-extract/css"; const typography = { one: text({ textStyle: "t7Regular" }), two: text({ textStyle: "t4Regular" }), three: text({ textStyle: "t1Regular" }), four: text({ textStyle: "t1Regular" }), } export const footer = style([ text({ textStyle: "t3Regular" }), { padding: "16px", marginTop: "12px", color: vars.$scale.color.gray700, backgroundColor: vars.$semantic.color.paperContents, }, ]); ``` ## replace-stitches-styled-color \[#replace-stitches-styled-color] Stitches로 스타일링된 컴포넌트의 색상을 SEED 형식으로 변환해요. ### 변환 내용 \[#변환-내용] - Stitches `styled()` 함수에서 사용된 Sprout 색상 토큰을 SEED 색상 토큰으로 변환합니다. - 모든 색상 관련 스타일 속성(color, backgroundColor, borderColor 등)을 지원합니다. - 상태 변이(variants)에 적용된 색상 변경도 함께 변환합니다. ### 대상 파일 \[#대상-파일] - `.tsx`, `.jsx`, `.ts`, `.js` 파일에서 Stitches 스타일링 사용 부분 ### 주의사항 \[#주의사항] - 기존 코드의 구조와 포맷을 최대한 유지하면서 색상 값만 변경합니다. - 커스텀 색상 값이나 CSS 변수는 변환하지 않습니다. * npm: npx @seed-design/codemod@latest replace-stitches-styled-color \ * pnpm: pnpm dlx @seed-design/codemod@latest replace-stitches-styled-color \ * yarn: yarn dlx @seed-design/codemod@latest replace-stitches-styled-color \ * bun: bun x @seed-design/codemod@latest replace-stitches-styled-color \ ```tsx title="basic.input.tsx" // @ts-nocheck const SemanticColorTestComponent = styled("div", { // Semantic Color 테스트 color: "$primary-semantic", color: "$onPrimary-semantic", color: "$primaryLow-semantic", color: "$secondary-semantic", color: "$secondaryLow-semantic", color: "$success-semantic", color: "$successLow-semantic", color: "$warning-semantic", color: "$warningLow-semantic", color: "$danger-semantic", color: "$dangerLow-semantic", color: "$overlayDim-semantic", color: "$overlayLow-semantic", color: "$paperSheet-semantic", color: "$paperDialog-semantic", color: "$paperFloating-semantic", color: "$paperContents-semantic", color: "$paperDefault-semantic", color: "$paperBackground-semantic", color: "$paperAccent-semantic", color: "$primaryHover-semantic", color: "$primaryPressed-semantic", color: "$primaryLowHover-semantic", color: "$primaryLowActive-semantic", color: "$primaryLowPressed-semantic", color: "$grayHover-semantic", color: "$grayPressed-semantic", color: "$onPrimaryOverlay50-semantic", color: "$onPrimaryOverlay200-semantic", color: "$onPrimaryLowOverlay50-semantic", color: "$onPrimaryLowOverlay100-semantic", color: "$onPrimaryLowOverlay200-semantic", color: "$onGrayOverlay50-semantic", color: "$onGrayOverlay100-semantic", color: "$divider1-semantic", color: "$divider2-semantic", color: "$divider3-semantic", color: "$accent-semantic", color: "$inkText-semantic", color: "$inkTextLow-semantic", color: "$grayActive-semantic", }); const ScaleColorTestComponent = styled("div", { // Scale Color Gray 테스트 color: "$gray00", color: "$gray50", color: "$gray100", color: "$gray200", color: "$gray300", color: "$gray400", color: "$gray500", color: "$gray600", color: "$gray700", color: "$gray800", color: "$gray900", color: "$grayAlpha50", color: "$grayAlpha100", color: "$grayAlpha200", color: "$grayAlpha500", // Scale Color Carrot 테스트 color: "$carrot50", color: "$carrot100", color: "$carrot200", color: "$carrot300", color: "$carrot400", color: "$carrot500", color: "$carrot600", color: "$carrot700", color: "$carrot800", color: "$carrot900", color: "$carrot950", color: "$carrotAlpha50", color: "$carrotAlpha100", color: "$carrotAlpha200", // Scale Color Blue 테스트 color: "$blue50", color: "$blue100", color: "$blue200", color: "$blue300", color: "$blue400", color: "$blue500", color: "$blue600", color: "$blue700", color: "$blue800", color: "$blue900", color: "$blue950", color: "$blueAlpha50", color: "$blueAlpha100", color: "$blueAlpha200", // Scale Color Red 테스트 color: "$red50", color: "$red100", color: "$red200", color: "$red300", color: "$red400", color: "$red500", color: "$red600", color: "$red700", color: "$red800", color: "$red900", color: "$red950", color: "$redAlpha50", color: "$redAlpha100", color: "$redAlpha200", // Scale Color Green 테스트 color: "$green50", color: "$green100", color: "$green200", color: "$green300", color: "$green400", color: "$green500", color: "$green600", color: "$green700", color: "$green800", color: "$green900", color: "$green950", color: "$greenAlpha50", color: "$greenAlpha100", color: "$greenAlpha200", // Scale Color Yellow 테스트 color: "$yellow50", color: "$yellow100", color: "$yellow200", color: "$yellow300", color: "$yellow400", color: "$yellow500", color: "$yellow600", color: "$yellow700", color: "$yellow800", color: "$yellow900", color: "$yellow950", color: "$yellowAlpha50", color: "$yellowAlpha100", color: "$yellowAlpha200", // Scale Color Purple 테스트 color: "$purple50", color: "$purple100", color: "$purple200", color: "$purple300", color: "$purple400", color: "$purple500", color: "$purple600", color: "$purple700", color: "$purple800", color: "$purple900", color: "$purple950", }); const StaticColorTestComponent = styled("div", { // Static Color 테스트 color: "$black-static", color: "$white-static", color: "$gray900-static", color: "$carrot50-static", color: "$carrot800-static", color: "$green50-static", color: "$green800-static", color: "$yellow50-static", color: "$yellow800-static", color: "$red50-static", color: "$red800-static", color: "$blue50-static", color: "$blue800-static", color: "$blackAlpha200-static", color: "$blackAlpha500-static", color: "$whiteAlpha50-static", color: "$whiteAlpha200-static", }); // 복합 속성 테스트 const ComplexPropertyTestComponent = styled("div", { border: "1px solid $gray700", boxShadow: "0 0 10px $overlayDim-semantic", outline: "2px solid $accent-semantic", textDecoration: "underline $danger-semantic", // 네스팅된 속성 테스트 "&:before": { borderBottom: "1px solid $divider1-semantic", background: "$paperContents-semantic", }, // 변형 테스트 variants: { theme: { primary: { background: "$primary-semantic", color: "$onPrimary-semantic", }, secondary: { background: "$secondaryLow-semantic", color: "$inkText-semantic", }, danger: { background: "$danger-semantic", color: "$white-static", }, }, }, }); // 복합 속성 (색상 토큰이 앞에 있는 경우) 테스트 const BorderAndColorTestComponent = styled("div", { color: "$scale-red500 solid 1px", color: "$red600 dashed 2px", color: "$scale-blue400 dotted 3px", color: "$scale-green600 double 4px", color: "$scale-yellow500 groove 2px", outlineColor: "$scale-purple500", }); const ImportantTest = styled("div", { color: "$primary-semantic !important", color: "$onPrimary-semantic !important", color: "$primaryLow-semantic !important", color: "$gray00 !important", color: "$gray50 !important", color: "$gray100 !important", css: { color: "$scale-gray600 !important", borderColor: "$scale-gray400 !important", backgroundColor: "$scale-gray100 !important", }, }); const Li_FieldItem = styled("li", { borderBottom: `${rem(1)} solid $semantic-divider1`, borderBottom: `0.5rem solid $semantic-paperBackground`, border: check ? "1px solid $semantic-divider1" : "0.5rem solid $semantic-paperBackground", }); ``` ```tsx title="basic.output.tsx" // @ts-nocheck const SemanticColorTestComponent = styled("div", { // Semantic Color 테스트 color: "$fg-brand", color: "$palette-static-white", color: "$palette-carrot-100", color: "$palette-gray-900", color: "$bg-neutral-weak", color: "$fg-positive", color: "$bg-positive-weak", color: "$bg-warning-solid", color: "$bg-warning-weak", color: "$fg-critical", color: "$bg-critical-weak", color: "$bg-overlay", color: "$bg-overlay-muted", color: "$bg-layer-floating", color: "$bg-layer-floating", color: "$bg-layer-floating", color: "$bg-layer-fill", color: "$bg-layer-default", color: "$bg-layer-basement", color: "$palette-carrot-100", color: "$bg-brand-solid-pressed", color: "$bg-brand-solid-pressed", color: "$palette-carrot-200", color: "$palette-carrot-100", color: "$palette-carrot-200", color: "$bg-neutral-weak-pressed", color: "$bg-neutral-weak-pressed", color: "$onPrimaryOverlay50-semantic", color: "$onPrimaryOverlay200-semantic", color: "$onPrimaryLowOverlay50-semantic", color: "$onPrimaryLowOverlay100-semantic", color: "$onPrimaryLowOverlay200-semantic", color: "$stroke-neutral-subtle", color: "$onGrayOverlay100-semantic", color: "$stroke-neutral-subtle", color: "$stroke-neutral-muted", color: "$palette-gray-400", color: "$fg-informative", color: "$fg-neutral", color: "$fg-neutral-subtle", color: "$fg-neutral-muted", }); const ScaleColorTestComponent = styled("div", { // Scale Color Gray 테스트 color: "$palette-gray-00", color: "$palette-gray-100", color: "$palette-gray-200", color: "$palette-gray-300", color: "$palette-gray-400", color: "$palette-gray-500", color: "$palette-gray-600", color: "$palette-gray-700", color: "$palette-gray-800", color: "$palette-gray-900", color: "$palette-gray-1000", color: "$palette-gray-200", color: "$palette-gray-300", color: "$palette-gray-500", color: "$palette-gray-700", // Scale Color Carrot 테스트 color: "$palette-carrot-100", color: "$palette-carrot-200", color: "$palette-carrot-300", color: "$palette-carrot-400", color: "$palette-carrot-500", color: "$palette-carrot-600", color: "$palette-carrot-600", color: "$palette-carrot-700", color: "$palette-carrot-700", color: "$palette-carrot-800", color: "$palette-carrot-800", color: "$palette-carrot-100", color: "$palette-carrot-200", color: "$palette-carrot-200", // Scale Color Blue 테스트 color: "$palette-blue-100", color: "$palette-blue-200", color: "$palette-blue-300", color: "$palette-blue-400", color: "$palette-blue-400", color: "$palette-blue-600", color: "$palette-blue-600", color: "$palette-blue-800", color: "$palette-blue-900", color: "$palette-blue-900", color: "$palette-blue-1000", color: "$palette-blue-100", color: "$palette-blue-100", color: "$palette-blue-200", // Scale Color Red 테스트 color: "$palette-red-100", color: "$palette-red-200", color: "$palette-red-300", color: "$palette-red-400", color: "$palette-red-600", color: "$palette-red-700", color: "$palette-red-700", color: "$palette-red-800", color: "$palette-red-900", color: "$palette-red-900", color: "$palette-red-900", color: "$palette-red-100", color: "$palette-red-200", color: "$palette-red-300", // Scale Color Green 테스트 color: "$palette-green-100", color: "$palette-green-200", color: "$palette-green-300", color: "$palette-green-400", color: "$palette-green-500", color: "$palette-green-600", color: "$palette-green-700", color: "$palette-green-800", color: "$palette-green-900", color: "$palette-green-900", color: "$palette-green-900", color: "$palette-green-100", color: "$palette-green-200", color: "$palette-green-200", // Scale Color Yellow 테스트 color: "$palette-yellow-100", color: "$palette-yellow-200", color: "$palette-yellow-300", color: "$palette-yellow-400", color: "$palette-yellow-500", color: "$palette-yellow-700", color: "$palette-yellow-700", color: "$palette-yellow-800", color: "$palette-yellow-800", color: "$palette-yellow-900", color: "$palette-yellow-900", color: "$palette-yellow-100", color: "$palette-yellow-100", color: "$palette-yellow-100", // Scale Color Purple 테스트 color: "$palette-purple-100", color: "$palette-purple-300", color: "$palette-purple-400", color: "$palette-purple-400", color: "$palette-purple-500", color: "$palette-purple-600", color: "$palette-purple-700", color: "$palette-purple-800", color: "$palette-purple-900", color: "$palette-purple-900", color: "$palette-purple-1000", }); const StaticColorTestComponent = styled("div", { // Static Color 테스트 color: "$palette-static-black", color: "$palette-static-white", color: "$palette-static-black", color: "$palette-carrot-100", color: "$palette-carrot-700", color: "$palette-green-100", color: "$palette-green-700", color: "$palette-yellow-100", color: "$palette-yellow-700", color: "$palette-red-100", color: "$palette-red-700", color: "$palette-blue-100", color: "$palette-blue-700", color: "$palette-static-black-alpha-500", color: "$palette-static-black-alpha-700", color: "$palette-static-white-alpha-50", color: "$palette-static-white-alpha-300", }); // 복합 속성 테스트 const ComplexPropertyTestComponent = styled("div", { border: "1px solid $palette-gray-800", boxShadow: "0 0 10px $bg-overlay", outline: "2px solid $fg-informative", textDecoration: "underline $fg-critical", // 네스팅된 속성 테스트 "&:before": { borderBottom: "1px solid $stroke-neutral-subtle", background: "$bg-layer-fill", }, // 변형 테스트 variants: { theme: { primary: { background: "$bg-brand-solid", color: "$palette-static-white", }, secondary: { background: "$bg-neutral-weak", color: "$fg-neutral", }, danger: { background: "$bg-critical-solid", color: "$palette-static-white", }, }, }, }); // 복합 속성 (색상 토큰이 앞에 있는 경우) 테스트 const BorderAndColorTestComponent = styled("div", { color: "$palette-red-700 solid 1px", color: "$palette-red-700 dashed 2px", color: "$palette-blue-400 dotted 3px", color: "$palette-green-700 double 4px", color: "$palette-yellow-700 groove 2px", outlineColor: "$palette-purple-600", }); const ImportantTest = styled("div", { color: "$fg-brand !important", color: "$palette-static-white !important", color: "$palette-carrot-100 !important", color: "$palette-gray-00 !important", color: "$palette-gray-100 !important", color: "$palette-gray-200 !important", css: { color: "$palette-gray-700 !important", borderColor: "$palette-gray-500 !important", backgroundColor: "$palette-gray-200 !important", }, }); const Li_FieldItem = styled("li", { borderBottom: `${rem(1)} solid $stroke-neutral-subtle`, borderBottom: `0.5rem solid $bg-layer-basement`, border: check ? "1px solid $stroke-neutral-subtle" : "0.5rem solid $bg-layer-basement", }); ``` ## replace-custom-text-component-color-prop \[#replace-custom-text-component-color-prop] - npm: npx @seed-design/codemod@latest replace-custom-text-component-color-prop \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-custom-text-component-color-prop \ - yarn: yarn dlx @seed-design/codemod@latest replace-custom-text-component-color-prop \ - bun: bun x @seed-design/codemod@latest replace-custom-text-component-color-prop \ ```tsx title="basic.input.tsx" // @ts-nocheck const Component = () => { return ( <> ); }; ``` ```tsx title="basic.output.tsx" // @ts-nocheck const Component = () => { return ( <> ); }; ``` ## replace-alpha-color \[#replace-alpha-color] SEED 알파 컬러 토큰을 새로운 이름으로 업데이트하는 transform입니다. 이 transform은 다음과 같은 알파 컬러 토큰들을 새로운 값으로 변경합니다: - `staticBlackAlpha50` → `staticBlackAlpha200` - `staticBlackAlpha200` → `staticBlackAlpha500` - `staticBlackAlpha500` → `staticBlackAlpha700` - `staticWhiteAlpha200` → `staticWhiteAlpha300` ### 주의사항 \[#주의사항-1] - 이 transform은 JavaScript/TypeScript 파일과 CSS 파일을 모두 자동으로 처리합니다. - **한 번만 실행**: 동일한 파일에 여러 번 실행하면 연쇄 변환이 발생할 수 있습니다 - **SEED 전용**: 이미 SEED를 사용 중인 프로젝트에서만 사용하세요 - **백업 권장**: 변환 전에 코드를 백업하는 것을 권장합니다 - **순서 중요**: 큰 번호부터 작은 번호 순으로 변환하여 연쇄 변환을 방지합니다 * npm: npx @seed-design/codemod@latest replace-alpha-color \ * pnpm: pnpm dlx @seed-design/codemod@latest replace-alpha-color \ * yarn: yarn dlx @seed-design/codemod@latest replace-alpha-color \ * bun: bun x @seed-design/codemod@latest replace-alpha-color \ ```tsx title="basic.input.tsx" // @ts-nocheck import { vars } from "@seed-design/css/vars"; export const Component = () => { const styles = { overlay: { backgroundColor: vars.$color.palette.staticBlackAlpha50, }, shadow: { backgroundColor: vars.$color.palette.staticBlackAlpha200, }, darkOverlay: { backgroundColor: vars.$color.palette.staticBlackAlpha500, }, lightOverlay: { backgroundColor: vars.$color.palette.staticWhiteAlpha200, }, }; return (
    Light overlay
    Shadow
    Dark overlay
    Light overlay
    Direct usage
    ); }; ``` ```tsx title="basic.output.tsx" // @ts-nocheck import { vars } from "@seed-design/css/vars"; export const Component = () => { const styles = { overlay: { backgroundColor: vars.$color.palette.staticBlackAlpha200, }, shadow: { backgroundColor: vars.$color.palette.staticBlackAlpha500, }, darkOverlay: { backgroundColor: vars.$color.palette.staticBlackAlpha700, }, lightOverlay: { backgroundColor: vars.$color.palette.staticWhiteAlpha300, }, }; return (
    Light overlay
    Shadow
    Dark overlay
    Light overlay
    Direct usage
    ); }; ```
    ## replace-custom-seed-design-color \[#replace-custom-seed-design-color] 커스텀 컬러 토큰을 SEED 형식으로 변환하는 트랜스폼 - npm: npx @seed-design/codemod@latest replace-custom-seed-design-color \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-custom-seed-design-color \ - yarn: yarn dlx @seed-design/codemod@latest replace-custom-seed-design-color \ - bun: bun x @seed-design/codemod@latest replace-custom-seed-design-color \ ```ts title="basic.input.ts" // @ts-nocheck import { style } from "@vanilla-extract/css"; import { color, backgroundColor, background } from "@/shared/styles"; export const container = style({ backgroundColor: color.gray100, color: color.gray900, borderColor: color.carrot500, }); export const box = style({ background: color.gray50, color: color.red500, border: `1px solid ${color.gray300}`, }); export const alert = style({ backgroundColor: color.red50, color: color.red900, }); export const button = style({ backgroundColor: background.gray100, color: color.gray900, }); export const highlight = style({ color: color.carrot500, backgroundColor: backgroundColor.primary, }); export const primary = style({ color: color.primary, }); ``` ```ts title="basic.output.ts" // @ts-nocheck import { style } from "@vanilla-extract/css"; import { color, backgroundColor, background } from "@/shared/styles"; export const container = style({ backgroundColor: color.palette.gray200, color: color.palette.gray1000, borderColor: color.palette.carrot600, }); export const box = style({ background: color.palette.gray100, color: color.palette.red700, border: `1px solid ${color.palette.gray400}`, }); export const alert = style({ backgroundColor: color.palette.red100, color: color.palette.red900, }); export const button = style({ backgroundColor: background.palette.gray200, color: color.palette.gray1000, }); export const highlight = style({ color: color.palette.carrot600, backgroundColor: backgroundColor.bg.brandSolid, }); export const primary = style({ color: color.fg.brand, }); ``` ## replace-custom-color-to-seed-design-vars-color \[#replace-custom-color-to-seed-design-vars-color] - npm: npx @seed-design/codemod@latest replace-custom-color-to-seed-design-vars-color \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-custom-color-to-seed-design-vars-color \ - yarn: yarn dlx @seed-design/codemod@latest replace-custom-color-to-seed-design-vars-color \ - bun: bun x @seed-design/codemod@latest replace-custom-color-to-seed-design-vars-color \ ```ts title="basic.input.ts" // @ts-nocheck import { color, bg, typography, mode } from '@/src/styles/tokens'; export const container = style({ backgroundColor: color['$semantic.paperDefault'], color: color['$scale.gray900'], borderColor: color['$scale.carrot500'], }); export const box = style({ background: color['$scale.gray50'], color: color['$scale.red500'], border: `1px solid ${color['$scale.gray300']}`, }); export const alert = style({ backgroundColor: color['$scale.red50'], color: color['$scale.red900'], }); export const button = style({ backgroundColor: color['$scale.gray100'], color: color['$scale.gray900'], }); export const highlight = style({ color: color['$scale.carrot500'], backgroundColor: color['$semantic.primary'], }); export const primary = style({ color: color['$semantic.primary'], }); // Additional test cases for various mappings export const semanticColors = style({ backgroundColor: color['$semantic.primary'], color: color['$semantic.inkText'], borderColor: color['$semantic.divider1'], }); export const scaleColors = style({ background: color['$scale.gray100'], color: color['$scale.carrot600'], border: `1px solid ${color['$scale.blue500']}`, }); export const staticColors = style({ backgroundColor: color['$static.staticWhite'], color: color['$static.staticBlack'], borderColor: color['$static.staticBlackAlpha200'], }); // Test deprecated tokens with alternative export const deprecatedColors = style({ backgroundColor: color['$semantic.paperContents'], color: color['$semantic.inkTextLow'], }); // Test multiple token choices (should use first one) export const multipleChoices = style({ backgroundColor: color['$semantic.success'], color: color['$semantic.warning'], }); export const icon = { color: color['$scale.gray600'], bg: bg['$scale.gray100'], }; ``` ```ts title="basic.output.ts" // @ts-nocheck import { color, bg, typography, mode } from "@/src/styles/tokens"; export const container = style({ backgroundColor: color["bg.layerDefault"], color: color["palette.gray1000"], borderColor: color["palette.carrot600"], }); export const box = style({ background: color["palette.gray100"], color: color["palette.red700"], border: `1px solid ${color["palette.gray400"]}`, }); export const alert = style({ backgroundColor: color["palette.red100"], color: color["palette.red900"], }); export const button = style({ backgroundColor: color["palette.gray200"], color: color["palette.gray1000"], }); export const highlight = style({ color: color["palette.carrot600"], backgroundColor: color["bg.brandSolid"], }); export const primary = style({ color: color["fg.brand"], }); // Additional test cases for various mappings export const semanticColors = style({ backgroundColor: color["bg.brandSolid"], color: color["fg.neutral"], borderColor: color["stroke.neutralSubtle"], }); export const scaleColors = style({ background: color["palette.gray200"], color: color["palette.carrot600"], border: `1px solid ${color["palette.blue600"]}`, }); export const staticColors = style({ backgroundColor: color["palette.staticWhite"], color: color["palette.staticBlack"], borderColor: color["palette.staticBlackAlpha500"], }); // Test deprecated tokens with alternative export const deprecatedColors = style({ backgroundColor: color["bg.layerFill"], color: color["fg.neutralSubtle"], }); // Test multiple token choices (should use first one) export const multipleChoices = style({ backgroundColor: color["bg.positiveSolid"], color: color["bg.warningSolid"], }); export const icon = { color: color["palette.gray700"], bg: bg["palette.gray200"], }; ``` ## replace-css-seed-design-typography-variable \[#replace-css-seed-design-typography-variable] - npm: npx @seed-design/codemod@latest replace-css-seed-design-typography-variable \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-css-seed-design-typography-variable \ - yarn: yarn dlx @seed-design/codemod@latest replace-css-seed-design-typography-variable \ - bun: bun x @seed-design/codemod@latest replace-css-seed-design-typography-variable \ ```css title="basic.input.css" /* Radio Label */ .seed-radio-group[data-size="small"] [data-part="radio-label"] { margin-inline-start: var(--seed-radio-small-label-margin-inline-start); margin-block-start: var(--seed-radio-small-label-margin-block-start); font-size: var(--seed-semantic-typography-label4-regular-font-size); font-weight: var(--seed-semantic-typography-label4-regular-font-weight); line-height: var(--seed-semantic-typography-label4-regular-line-height); letter-spacing: var(--seed-semantic-typography-label4-regular-letter-spacing); } .seed-radio-group[data-size="medium"] [data-part="radio-label"] { margin-inline-start: var(--seed-radio-medium-label-margin-inline-start); margin-block-start: var(--seed-radio-medium-label-margin-block-start); font-size: var(--seed-semantic-typography-label3-regular-font-size); font-weight: var(--seed-semantic-typography-label3-regular-font-weight); line-height: var(--seed-semantic-typography-label3-regular-line-height); letter-spacing: var(--seed-semantic-typography-label3-regular-letter-spacing); } .seed-radio-group[data-size="large"] [data-part="radio-label"] { margin-inline-start: var(--seed-radio-large-label-margin-inline-start); margin-block-start: var(--seed-radio-large-label-margin-block-start); font-size: var(--seed-semantic-typography-label2-regular-font-size); font-weight: var(--seed-semantic-typography-label2-regular-font-weight); line-height: var(--seed-semantic-typography-label2-regular-line-height); letter-spacing: var(--seed-semantic-typography-label2-regular-letter-spacing); } ``` ```css title="basic.output.css" /* Radio Label */ .seed-radio-group[data-size="small"] [data-part="radio-label"] { margin-inline-start: var(--seed-radio-small-label-margin-inline-start); margin-block-start: var(--seed-radio-small-label-margin-block-start); font-size: var(--seed-font-size-t2); font-weight: var(--seed-font-weight-regular); line-height: var(--seed-line-height-t2); letter-spacing: normal; } .seed-radio-group[data-size="medium"] [data-part="radio-label"] { margin-inline-start: var(--seed-radio-medium-label-margin-inline-start); margin-block-start: var(--seed-radio-medium-label-margin-block-start); font-size: var(--seed-font-size-t4); font-weight: var(--seed-font-weight-regular); line-height: var(--seed-line-height-t4); letter-spacing: normal; } .seed-radio-group[data-size="large"] [data-part="radio-label"] { margin-inline-start: var(--seed-radio-large-label-margin-inline-start); margin-block-start: var(--seed-radio-large-label-margin-block-start); font-size: var(--seed-font-size-t5); font-weight: var(--seed-font-weight-regular); line-height: var(--seed-line-height-t5); letter-spacing: normal; } ``` ## replace-tailwind-color \[#replace-tailwind-color] Tailwind CSS 색상 클래스를 SEED 형식으로 변환해요. ### 변환 내용 \[#변환-내용-1] - Sprout의 색상 클래스 네이밍을 SEED 형식으로 변환합니다. - `text-`, `bg-`, `border-` 등의 접두사를 가진 Tailwind 색상 클래스를 변환합니다. - 색상 강도(shade)에 따른 변환 매핑을 적용합니다. ### 변환 예시 \[#변환-예시-1] - `text-carrot-500` → `text-static-orange-base` - `bg-salmon-100` → `bg-static-red-light` - `border-navy-900` → `border-static-blue-dark` ### 대상 파일 \[#대상-파일-1] - `.tsx`, `.jsx`, `.ts`, `.js`, `.html`, `.css` 파일에서 Tailwind 클래스를 사용하는 부분 ### 주의사항 \[#주의사항-2] - className 문자열, 템플릿 리터럴, 배열 형태 모두 지원합니다. - Tailwind 동적 클래스 조합(`clsx`, `cx`, `classnames` 등)도 변환됩니다. * npm: npx @seed-design/codemod@latest replace-tailwind-color \ * pnpm: pnpm dlx @seed-design/codemod@latest replace-tailwind-color \ * yarn: yarn dlx @seed-design/codemod@latest replace-tailwind-color \ * bun: bun x @seed-design/codemod@latest replace-tailwind-color \ ```tsx title="basic.input.tsx" // @ts-nocheck export function BackgroundExample() { return (
    Primary Background
    Primary Low Background
    Hover Primary Low Background
    Focus Primary Low Background
    Active Primary Low Background
    Scale Background
    Scale Carrot Low Background
    Scale Carrot Mid Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Static Black Background
    Static White Background
    Static Gray900 Background
    ); } export function SeedBackgroundExample() { return (
    Primary Background
    Primary Low Background
    Hover Primary Low Background
    Focus Primary Low Background
    Active Primary Low Background
    Scale Background
    Scale Carrot Low Background
    Scale Carrot Mid Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Static Black Background
    Static White Background
    Static Gray900 Background
    ); } // Class Variance Authority Example import { cva } from "class-variance-authority"; // Button component with variants const buttonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors", { variants: { variant: { default: "bg-primary text-staticWhite hover:bg-primaryLow", destructive: "bg-danger text-staticWhite hover:bg-dangerLow", outline: "border border-gray300 bg-staticWhite text-gray900 hover:bg-gray100", secondary: "bg-seed-secondaryLow text-seed-secondary hover:bg-seed-gray200", ghost: "text-seed-primary hover:bg-seed-primaryLow", link: "text-seed-primary underline-offset-4 hover:underline", }, size: { default: "h-10 px-4 py-2", sm: "h-9 rounded-md px-3", lg: "h-11 rounded-md px-8", icon: "h-10 w-10", }, }, defaultVariants: { variant: "default", size: "default", }, } ); ``` ```tsx title="basic.output.tsx" // @ts-nocheck export function BackgroundExample() { return (
    Primary Background
    Primary Low Background
    Hover Primary Low Background
    Focus Primary Low Background
    Active Primary Low Background
    Scale Background
    Scale Carrot Low Background
    Scale Carrot Mid Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Static Black Background
    Static White Background
    Static Gray900 Background
    ); } export function SeedBackgroundExample() { return (
    Primary Background
    Primary Low Background
    Hover Primary Low Background
    Focus Primary Low Background
    Active Primary Low Background
    Scale Background
    Scale Carrot Low Background
    Scale Carrot Mid Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Scale Carrot High Background
    Static Black Background
    Static White Background
    Static Gray900 Background
    ); } // Class Variance Authority Example import { cva } from "class-variance-authority"; // Button component with variants const buttonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors", { variants: { variant: { default: "bg-bg-brand-solid text-palette-static-white hover:bg-palette-carrot-100", destructive: "bg-bg-critical-solid text-palette-static-white hover:bg-bg-critical-weak", outline: "border border-palette-gray-400 bg-palette-static-white text-palette-gray-1000 hover:bg-palette-gray-200", secondary: "bg-bg-neutral-weak text-palette-gray-900 hover:bg-palette-gray-300", ghost: "text-fg-brand hover:bg-palette-carrot-100", link: "text-fg-brand underline-offset-4 hover:underline", }, size: { default: "h-10 px-4 py-2", sm: "h-9 rounded-md px-3", lg: "h-11 rounded-md px-8", icon: "h-10 w-10", }, }, defaultVariants: { variant: "default", size: "default", }, } ); ```
    --- file: developer-tools/codemods/introduction.mdx # Introduction @seed-design/codemod는 Sprout에서 SEED로 마이그레이션하기 위한 코드 변환 도구예요. ## 사용 방법 \[#사용-방법] - npm: npx @seed-design/codemod@latest \ \ \<옵션> - pnpm: pnpm dlx @seed-design/codemod@latest \ \ \<옵션> - yarn: yarn dlx @seed-design/codemod@latest \ \ \<옵션> - bun: bun x @seed-design/codemod@latest \ \ \<옵션> 사용 가능한 transform 목록을 확인하려면 다음 명령어를 실행해요. - npm: npx @seed-design/codemod@latest --list - pnpm: pnpm dlx @seed-design/codemod@latest --list - yarn: yarn dlx @seed-design/codemod@latest --list - bun: bun x @seed-design/codemod@latest --list 사용 가능한 변환 목록을 확인합니다. 스크립트 실행을 위해서는 **Node.js 20.19.0 이상**의 버전이 필요해요. 버전 요구사항을 만족시키지 않으면 안내 문구가 표시돼요. nvm을 사용한다면 codemod 실행을 위해 Node.js 버전을 잠시 변경할 수 있어요. ```shell nvm install 20.19 ``` ## 옵션 \[#옵션] - `--list` - 사용 가능한 transform 목록을 보여줘요. - `--log` - 로그를 파일로 저장해요. - `./`에 `combined.log`와 `warnings.log` 파일이 생성돼요. - `--parser` - jscodeshift가 사용할 파서를 지정해요 - `babel` | `babylon` | `flow` | `ts` | `tsx` - 기본값: `tsx` - 예시: `--parser=babel` - `--extensions` - 변환할 파일 확장자를 지정해요. - 지정하지 않으면 `<경로>` 안의 `js,jsx,ts,tsx` 파일을 변환해요. (`d.ts`는 제외) - 예시: `--extensions="ts,tsx"` - `--ignore-config` - 변환하지 않을 파일 패턴이 정의된 파일을 지정해요. - 예시: `--ignore-config=".gitignore"` --- file: developer-tools/figma-integration/codegen.mdx # Codegen Figma 디자인으로부터 React 코드를 생성하는 플러그인을 제공합니다. ## 설치 \[#설치] [SEED Design Codegen](https://www.figma.com/community/plugin/1469592983561656263) 플러그인을 설치합니다. ## 사용법 \[#사용법] Figma 디자인을 열고, Dev mode에서 코드 생성을 원하는 디자인 요소를 선택합니다. Figma Codegen Codegen 플러그인은 작은 단위의 코드 생성 및 작업에 적합합니다. 복잡한 작업은 [MCP](/react/ai-integration/mcp)를 활용하는 것을 권장합니다. ## 관련 문서 \[#관련-문서] - [Figma Learn](https://help.figma.com/hc/en-us/articles/15023202277399-Use-code-snippets-in-Dev-Mode) --- file: getting-started/cli/commands.mdx # Commands @seed-design/cli 명령어를 안내해요. ## 공통 동작 \[#공통-동작] - `add`, `add-all`은 `seed-design.json` 설정 파일을 사용해요. - 설정 파일이 없거나 읽을 수 없으면 생성할지 먼저 물어봐요. - 항목 추가 시 필요한 npm 패키지 의존성은 자동으로 설치돼요. - `add`, `add-all` 실행 시 스니펫이 요구하는 seed-design 패키지 버전과 현재 프로젝트 버전이 다르면 경고를 보여줘요. (React: `@seed-design/react`, `@seed-design/css` / Lynx: `@seed-design/lynx-react`, `@seed-design/lynx-css`) - 기존 파일과 내용이 다르면 기본적으로 `덮어쓰기 / 백업 후 교체 / 건너뛰기`를 선택할 수 있어요. - `--on-diff` 옵션을 주면 충돌 처리 방식을 미리 고정할 수 있어요. - 실패 원인을 자세히 확인하려면 `--verbose` 옵션을 사용할 수 있어요. ## init \[#init] `seed-design.json` 파일을 생성하는 명령어예요. `seed-design.json` 파일에 대해 알아봅니다. ### init 명령어 입력하기 \[#init-명령어-입력하기] - npm: npx @seed-design/cli@latest init - pnpm: pnpm dlx @seed-design/cli@latest init - yarn: yarn dlx @seed-design/cli@latest init - bun: bun x @seed-design/cli@latest init ### seed-design 설정 \[#seed-design-설정] ```sh ◇ TypeScript를 사용중이신가요? │ Yes │ ◇ React Server Components를 사용중이신가요? │ No │ ◇ 어떤 프레임워크를 사용하시나요? │ React │ ◇ seed-design 폴더 경로를 입력해주세요. (기본값은 프로젝트 루트에 생성됩니다.) │ ./seed-design │ ◇ 개선을 위해 익명 사용 데이터를 수집할까요? │ Yes ``` ### 옵션 \[#옵션] | 옵션 | 설명 | | ----------------- | ---------------------------------- | | `-c, --cwd ` | 작업 디렉토리. 기본값은 현재 디렉토리 | | `-y, --yes` | 질문 없이 기본값으로 `seed-design.json` 생성 | | `--default` | Deprecated. `--yes`와 동일하게 기본값으로 생성 | | `--verbose` | 실패 시 stack trace 등 상세 오류 정보 출력 | | `-h, --help` | 도움말 출력 | - npm: npx @seed-design/cli@latest init --yes - pnpm: pnpm dlx @seed-design/cli@latest init --yes - yarn: yarn dlx @seed-design/cli@latest init --yes - bun: bun x @seed-design/cli@latest init --yes ## add \[#add] 스니펫을 다운로드하고 프로젝트에 추가하는 명령어예요. 자주 쓰이는 UI 패턴의 재사용을 돕는 스니펫에 대해 알아봅니다. - npm: npx @seed-design/cli@latest add \[...item-ids] - pnpm: pnpm dlx @seed-design/cli@latest add \[...item-ids] - yarn: yarn dlx @seed-design/cli@latest add \[...item-ids] - bun: bun x @seed-design/cli@latest add \[...item-ids] `item-ids`를 생략하면, 다운로드 가능한 항목을 인터랙티브하게 선택할 수 있어요. - npm: npx @seed-design/cli@latest add - pnpm: pnpm dlx @seed-design/cli@latest add - yarn: yarn dlx @seed-design/cli@latest add - bun: bun x @seed-design/cli@latest add ```sh copy ◆ 추가할 항목을 선택해주세요 (스페이스 바로 여러 개 선택 가능) │ ◻ ui:action-button │ ◻ ui:alert-dialog │ ◻ ui:callout │ ◻ ui:checkbox │ ◻ ui:switch │ ◻ ui:text-field │ ◻ ui:tabs │ ◻ (deprecated) ui:old-component │ ◻ ... └ ``` 여러 항목을 직접 지정할 수도 있어요. - npm: npx @seed-design/cli@latest add ui:action-button ui:alert-dialog - pnpm: pnpm dlx @seed-design/cli@latest add ui:action-button ui:alert-dialog - yarn: yarn dlx @seed-design/cli@latest add ui:action-button ui:alert-dialog - bun: bun x @seed-design/cli@latest add ui:action-button ui:alert-dialog ### 옵션 \[#옵션-1] | 옵션 | 설명 | | -------------------------------- | ---------------------------------------------------------------------- | | `-c, --cwd ` | 작업 디렉토리. 기본값은 현재 디렉토리 | | `-u, --baseUrl ` | 레지스트리 base URL | | `--seed-react-version ` | 지정한 SEED React 버전의 레지스트리 사용 (예: `1.2`). `--baseUrl`/`--framework`보다 우선 | | `-f, --framework ` | 대상 프레임워크 (`react` 또는 `lynx`). config의 `framework` 값을 덮어씀 | | `--on-diff ` | 파일 충돌 처리 방식 지정 (`overwrite` 또는 `backup`) | | `-a, --all` | **Deprecated**. 현재는 에러를 출력하고 `add-all` 사용을 안내 | | `--verbose` | 실패 시 stack trace 등 상세 오류 정보 출력 | | `-h, --help` | 도움말 출력 | ### 버전에 맞는 스니펫 받기 \[#버전에-맞는-스니펫-받기] 프로젝트에 설치된 SEED 패키지 버전과 호환되는 스니펫이 필요하면 버전 옵션을 사용하세요. 해당 버전이 배포된 레지스트리 주소를 CLI가 자동으로 찾아줍니다. - npm: npx @seed-design/cli@latest add --seed-react-version 1.2 ui:action-button - pnpm: pnpm dlx @seed-design/cli@latest add --seed-react-version 1.2 ui:action-button - yarn: yarn dlx @seed-design/cli@latest add --seed-react-version 1.2 ui:action-button - bun: bun x @seed-design/cli@latest add --seed-react-version 1.2 ui:action-button 버전 옵션은 base URL과 프레임워크(react)를 함께 결정하므로 `--baseUrl` / `--framework`보다 우선해요. #### baseUrl 직접 지정 \[#baseurl-직접-지정] 레지스트리 주소를 직접 지정하려면 `--baseUrl`을 사용하세요. 기본값은 배포 환경 기준 [`https://seed-design.io`](https://seed-design.io)입니다. - npm: npx @seed-design/cli@latest add --baseUrl https://v1-2.seed-design.io - pnpm: pnpm dlx @seed-design/cli@latest add --baseUrl https://v1-2.seed-design.io - yarn: yarn dlx @seed-design/cli@latest add --baseUrl https://v1-2.seed-design.io - bun: bun x @seed-design/cli@latest add --baseUrl https://v1-2.seed-design.io ### 파일 충돌 처리 \[#파일-충돌-처리] `--on-diff`를 지정하지 않으면, 기존 파일과 새 스니펫이 다를 때 아래 중 하나를 직접 고를 수 있어요. - 덮어쓰기 - 기존 파일을 `legacy-<파일명>-`로 백업 후 교체 - 건너뛰기 ## add-all \[#add-all] 레지스트리 단위로 항목을 한 번에 추가하는 명령어예요. - npm: npx @seed-design/cli@latest add-all \[...registry-ids] - pnpm: pnpm dlx @seed-design/cli@latest add-all \[...registry-ids] - yarn: yarn dlx @seed-design/cli@latest add-all \[...registry-ids] - bun: bun x @seed-design/cli@latest add-all \[...registry-ids] 모든 레지스트리의 모든 항목을 추가하려면 `--all` 옵션을 사용하세요. - npm: npx @seed-design/cli@latest add-all --all # 모든 레지스트리의 모든 항목 추가 - pnpm: pnpm dlx @seed-design/cli@latest add-all --all # 모든 레지스트리의 모든 항목 추가 - yarn: yarn dlx @seed-design/cli@latest add-all --all # 모든 레지스트리의 모든 항목 추가 - bun: bun x @seed-design/cli@latest add-all --all # 모든 레지스트리의 모든 항목 추가 특정 레지스트리의 모든 항목을 추가할 수도 있습니다. - npm: npx @seed-design/cli@latest add-all breeze # breeze 레지스트리의 모든 항목 추가 - pnpm: pnpm dlx @seed-design/cli@latest add-all breeze # breeze 레지스트리의 모든 항목 추가 - yarn: yarn dlx @seed-design/cli@latest add-all breeze # breeze 레지스트리의 모든 항목 추가 - bun: bun x @seed-design/cli@latest add-all breeze # breeze 레지스트리의 모든 항목 추가 deprecated 항목을 포함하려면 `--include-deprecated` 옵션을 사용하세요. - npm: npx @seed-design/cli@latest add-all ui --include-deprecated # ui 레지스트리의 모든 항목 추가 (deprecated 포함) - pnpm: pnpm dlx @seed-design/cli@latest add-all ui --include-deprecated # ui 레지스트리의 모든 항목 추가 (deprecated 포함) - yarn: yarn dlx @seed-design/cli@latest add-all ui --include-deprecated # ui 레지스트리의 모든 항목 추가 (deprecated 포함) - bun: bun x @seed-design/cli@latest add-all ui --include-deprecated # ui 레지스트리의 모든 항목 추가 (deprecated 포함) 레지스트리 ID와 `--all`을 모두 생략하면, 추가할 레지스트리를 인터랙티브하게 선택할 수 있어요. ### 옵션 \[#옵션-2] | 옵션 | 설명 | | -------------------------------- | ---------------------------------------------------------------------- | | `-a, --all` | 모든 레지스트리의 모든 항목 추가 | | `--include-deprecated` | deprecated 항목 포함 | | `-c, --cwd ` | 작업 디렉토리. 기본값은 현재 디렉토리 | | `-u, --baseUrl ` | 레지스트리 base URL | | `--seed-react-version ` | 지정한 SEED React 버전의 레지스트리 사용 (예: `1.2`). `--baseUrl`/`--framework`보다 우선 | | `-f, --framework ` | 대상 프레임워크 (`react` 또는 `lynx`). config의 `framework` 값을 덮어씀 | | `--on-diff ` | 파일 충돌 처리 방식 지정 (`overwrite` 또는 `backup`) | | `--verbose` | 실패 시 stack trace 등 상세 오류 정보 출력 | | `-h, --help` | 도움말 출력 | ## compat \[#compat] 현재 프로젝트의 seed-design 패키지 버전과 스니펫 요구 버전이 호환되는지 검사해요. 플랫폼에 따라 검사 대상 패키지가 달라요. (React: `@seed-design/react`, `@seed-design/css` / Lynx: `@seed-design/lynx-react`, `@seed-design/lynx-css`) - npm: npx @seed-design/cli@latest compat - pnpm: pnpm dlx @seed-design/cli@latest compat - yarn: yarn dlx @seed-design/cli@latest compat - bun: bun x @seed-design/cli@latest compat 기본값은 `seed-design.json`의 `path` 아래에 이미 추가된 스니펫만 검사해요. 호환성 이슈가 있으면 종료 코드 `1`, 문제가 없으면 `0`으로 종료돼서 CI/에이전트 환경에서도 바로 사용할 수 있어요. 특정 항목만 검사할 수도 있어요. - npm: npx @seed-design/cli@latest compat ui:action-button ui:alert-dialog - pnpm: pnpm dlx @seed-design/cli@latest compat ui:action-button ui:alert-dialog - yarn: yarn dlx @seed-design/cli@latest compat ui:action-button ui:alert-dialog - bun: bun x @seed-design/cli@latest compat ui:action-button ui:alert-dialog 컴포넌트 shorthand를 쓸 때는 `-c` 옵션을 사용해요. - npm: npx @seed-design/cli@latest compat -c action-button -c alert-dialog - pnpm: pnpm dlx @seed-design/cli@latest compat -c action-button -c alert-dialog - yarn: yarn dlx @seed-design/cli@latest compat -c action-button -c alert-dialog - bun: bun x @seed-design/cli@latest compat -c action-button -c alert-dialog 모든 registry 항목을 검사하려면 `--all`을 사용하세요. - npm: npx @seed-design/cli@latest compat --all - pnpm: pnpm dlx @seed-design/cli@latest compat --all - yarn: yarn dlx @seed-design/cli@latest compat --all - bun: bun x @seed-design/cli@latest compat --all ### 옵션 \[#옵션-3] | 옵션 | 설명 | | ----------------------------- | -------------------------------------------------------- | | `-c, --component ` | 검사할 컴포넌트 ID. 여러 번 또는 쉼표로 지정 가능 | | `-r, --registry ` | `--component`에 shorthand를 사용할 때 기본 registry 지정 | | `-a, --all` | 모든 registry 항목 검사 | | `--cwd ` | 작업 디렉토리. 기본값은 현재 디렉토리 | | `-u, --baseUrl ` | 레지스트리 base URL | | `-f, --framework ` | 대상 프레임워크 (`react` 또는 `lynx`). config의 `framework` 값을 덮어씀 | | `--verbose` | 실패 시 stack trace 등 상세 오류 정보 출력 | | `-h, --help` | 도움말 출력 | ## docs \[#docs] 문서 링크, llms.txt 링크, 스니펫 링크를 조회하는 명령어예요. - npm: npx @seed-design/cli@latest docs \[query] - pnpm: pnpm dlx @seed-design/cli@latest docs \[query] - yarn: yarn dlx @seed-design/cli@latest docs \[query] - bun: bun x @seed-design/cli@latest docs \[query] `query`를 생략하면, 카테고리 → 섹션 → 항목을 인터랙티브하게 선택할 수 있어요. - npm: npx @seed-design/cli@latest docs - pnpm: pnpm dlx @seed-design/cli@latest docs - yarn: yarn dlx @seed-design/cli@latest docs - bun: bun x @seed-design/cli@latest docs ```sh copy ◆ 카테고리를 선택해주세요 │ React │ ◆ 섹션을 선택해주세요 │ 컴포넌트 │ ◆ 항목을 선택해주세요 │ Action Button │ │ action-button │ - docs: https://seed-design.io/react/components/action-button │ - llms.txt: https://seed-design.io/llms/react/components/action-button.txt │ - snippet: https://raw.githubusercontent.com/daangn/seed-design/refs/heads/dev/docs/registry/react/ui/action-button.tsx │ ``` ### 직접 검색 \[#직접-검색] 항목 이름으로 직접 검색할 수 있어요. - npm: npx @seed-design/cli@latest docs action-button - pnpm: pnpm dlx @seed-design/cli@latest docs action-button - yarn: yarn dlx @seed-design/cli@latest docs action-button - bun: bun x @seed-design/cli@latest docs action-button ### 경로 기반 검색 \[#경로-기반-검색] `카테고리/섹션/항목` 형태로 직접 경로를 지정할 수 있어요. - npm: npx @seed-design/cli@latest docs react/components/action-button - pnpm: pnpm dlx @seed-design/cli@latest docs react/components/action-button - yarn: yarn dlx @seed-design/cli@latest docs react/components/action-button - bun: bun x @seed-design/cli@latest docs react/components/action-button 카테고리만 지정하면 해당 카테고리의 섹션/항목을 선택할 수 있어요. - npm: npx @seed-design/cli@latest docs react - pnpm: pnpm dlx @seed-design/cli@latest docs react - yarn: yarn dlx @seed-design/cli@latest docs react - bun: bun x @seed-design/cli@latest docs react 카테고리/섹션까지 지정하면 해당 섹션의 항목을 선택할 수 있어요. - npm: npx @seed-design/cli@latest docs react/components - pnpm: pnpm dlx @seed-design/cli@latest docs react/components - yarn: yarn dlx @seed-design/cli@latest docs react/components - bun: bun x @seed-design/cli@latest docs react/components ### 오타 제안 \[#오타-제안] 경로에 오타가 있으면 유사한 유효 경로를 제안해줘요. ```sh copy react/component/action-buton: 문서를 찾을 수 없어요. 💡 이것을 의미했나요? - react/components/action-button ``` ### Raw 모드 \[#raw-모드] `--raw` 옵션을 사용하면 UI 없이 llms.txt 내용을 순수 마크다운으로 출력해요. LLM 파이프나 스크립트에서 활용할 수 있어요. - npm: npx @seed-design/cli@latest docs react/components/action-button --raw - pnpm: pnpm dlx @seed-design/cli@latest docs react/components/action-button --raw - yarn: yarn dlx @seed-design/cli@latest docs react/components/action-button --raw - bun: bun x @seed-design/cli@latest docs react/components/action-button --raw 패키지 changelog도 가져올 수 있어요. - npm: npx @seed-design/cli@latest docs react/updates/changelog/react/1.2.5 --raw - pnpm: pnpm dlx @seed-design/cli@latest docs react/updates/changelog/react/1.2.5 --raw - yarn: yarn dlx @seed-design/cli@latest docs react/updates/changelog/react/1.2.5 --raw - bun: bun x @seed-design/cli@latest docs react/updates/changelog/react/1.2.5 --raw ### 옵션 \[#옵션-4] | 옵션 | 설명 | | ------------------------- | ------------------------------- | | `--raw` | UI 없이 llms.txt 내용을 순수 마크다운으로 출력 | | `-u, --baseUrl ` | 레지스트리 base URL | | `--verbose` | 실패 시 stack trace 등 상세 오류 정보 출력 | | `-h, --help` | 도움말 출력 | --- file: getting-started/cli/configuration.mdx # Configuration seed-design.json 설정을 안내해요. ## seed-design.json 만들기 \[#seed-designjson-만들기] `seed-design.json` 파일은 CLI로 생성하거나 직접 추가할 수 있어요. CLI로 seed-design을 추가하는 방법을 알아봐요. 아래 파일을 프로젝트 루트에 생성해주세요. ```json title="seed-design.json" { "rsc": false, "tsx": true, "framework": "react", "path": "./seed-design", "telemetry": true } ``` ## 기본 형태 \[#기본-형태] ```json title="seed-design.json" { "rsc": false, "tsx": true, "framework": "react", "path": "./seed-design", "telemetry": true } ``` `seed-design.json`은 엄격하게 파싱돼요. 문서에 없는 키를 임의로 추가하면 CLI 실행 시 오류가 날 수 있어요. ## 옵션 한눈에 보기 \[#옵션-한눈에-보기] | 옵션 | 타입 | 기본값 | 설명 | | ----------- | ------------------- | ---------------------------- | ----------------------------------------------------------- | | `path` | `string` | 없음 (init에서는 `./seed-design`) | 스니펫이 생성될 루트 경로 | | `rsc` | `boolean` | `false` | React Server Components 사용 여부에 맞춰 `"use client"` 유지/제거 | | `tsx` | `boolean` | `true` | 생성 파일을 TypeScript(`.ts/.tsx`) 또는 JavaScript(`.js/.jsx`)로 변환 | | `framework` | `"react" \| "lynx"` | `"react"` | 스니펫 대상 프레임워크. init 시 자동 감지 | | `telemetry` | `boolean` | `true` | 익명 사용 데이터 수집 여부 | | `$schema` | `string` | 선택값 | JSON 스키마 힌트용 필드 (CLI 동작에는 영향 없음) | ### path \[#path] `path`는 스니펫이 생성되는 루트 디렉토리예요. `add`/`add-all` 실행 시 파일은 `path` 아래에 레지스트리 ID와 스니펫 내부 경로 기준으로 생성돼요. 고정된 폴더 구조가 아니라, 선택한 항목에 따라 구조가 달라져요. ```json title="seed-design.json" { "path": "./seed-design" } ``` ### rsc \[#rsc] [React Server Components](https://react.dev/reference/rsc/server-components)를 사용할지 설정해요. 스니펫에 이미 있는 `"use client"` directive를 유지할지 결정해요. - `true`: `"use client"`를 유지해요. - `false`: 파일 최상단의 `"use client"`를 제거해요. ```json title="seed-design.json" { "rsc": true | false // default: false } ``` ### tsx \[#tsx] TypeScript를 사용할지 설정해요. `false`로 설정하면 `.ts/.tsx` 파일이 `.js/.jsx`로 변환돼요. ```json title="seed-design.json" { "tsx": true | false // default: true } ``` ### framework \[#framework] 스니펫 대상 프레임워크를 설정해요. `init` 실행 시 프로젝트의 `package.json`에 `@lynx-js/react` 또는 `@seed-design/lynx-react`가 있으면 `"lynx"`로 자동 감지돼요. - `"react"`: `@seed-design/react`, `@seed-design/css` 기반 스니펫을 사용해요. - `"lynx"`: `@seed-design/lynx-react`, `@seed-design/lynx-css` 기반 스니펫을 사용해요. ```json title="seed-design.json" { "framework": "react" | "lynx" // default: "react" } ``` CLI 명령어에서 `--framework` 옵션으로 이 값을 일시적으로 덮어쓸 수 있어요. ### telemetry \[#telemetry] 텔레메트리 데이터 수집 여부를 설정해요. SEED Design CLI는 개선을 위해 익명 사용 데이터를 수집해요. 프로젝트 경로, 파일 내용, 소스 코드는 수집하지 않아요. ```json title="seed-design.json" { "telemetry": true | false // default: true } ``` #### 우선순위 \[#우선순위] 아래 순서로 텔레메트리 활성화 여부를 판단해요. 1. `DISABLE_TELEMETRY=true` 환경 변수 2. `SEED_DISABLE_TELEMETRY=true` 환경 변수 3. `seed-design.json`의 `telemetry` 4. 기본값 `true` #### 수집되는 데이터 예시 \[#수집되는-데이터-예시] - 실행 이벤트: `init`, `add`, `add-all`, `compat` - 실행 통계: 실행 시간(`duration_ms`), 추가된 항목 수 - 설정 정보: `tsx`, `rsc`, `include_deprecated`, `yes_option` 등 #### 수집되지 않는 데이터 \[#수집되지-않는-데이터] - 프로젝트 경로, 생성된 파일 경로 - 스니펫 파일 내용 - 소스 코드 - 개인 식별 정보 #### 비활성화 방법 \[#비활성화-방법] **1. seed-design.json 설정** ```json { "telemetry": false } ``` **2. 환경 변수 설정** ```bash export DISABLE_TELEMETRY=true # 또는 export SEED_DISABLE_TELEMETRY=true ``` ### $schema \[#schema] `$schema`는 선택적으로 추가할 수 있는 문자열 필드예요. 현재 CLI 동작에는 영향을 주지 않고, 에디터의 JSON 자동완성/검증 용도로만 사용할 수 있어요. --- file: getting-started/installation/manual.mdx # Manual Seed Design을 번들러 통합 없이 설치하고 사용하는 방법을 알아봅니다. ### 의존성 설치 \[#의존성-설치] 필요한 의존성들을 설치해요. - npm: npm install @seed-design/react @seed-design/css - pnpm: pnpm add @seed-design/react @seed-design/css - yarn: yarn add @seed-design/react @seed-design/css - bun: bun add @seed-design/react @seed-design/css ### index.html 수정하기 \[#indexhtml-수정하기] Seed Design의 컬러 토큰은 `` 태그의 `data-seed-color-mode`, `data-seed-user-color-scheme` 속성을 통해 라이트/다크모드 전환을 해요. 테마 속성에 대한 자세한 설명과 활용 방법을 확인합니다. ```tsx title="index.html" ``` ```tsx title="index.html" ``` ```tsx title="index.html" ``` ### all.css import하기 \[#allcss-import하기] 스타일시트 크기를 줄이기 위해 `base.css` 및 각 컴포넌트의 스타일시트를 따로 import할 수 있어요. 번들러를 사용하는 경우 문서 상단의 번들러 가이드를 통해 컴포넌트 스타일시트를 자동으로 로드할 수 있어요. ```ts title="index.tsx" import "@seed-design/css/all.css"; ``` ### seed-design.json 생성하기 \[#seed-designjson-생성하기] ### init 명령어 입력하기 \[#init-명령어-입력하기] ```sh npx @seed-design/cli@latest init ``` ### seed-design 설정 \[#seed-design-설정] ```sh ◇ TypeScript를 사용중이신가요? │ Yes │ ◇ React Server Components를 사용중이신가요? │ No │ ◇ seed-design 폴더 경로를 입력해주세요. (기본값은 프로젝트 루트에 생성됩니다.) │ ./seed-design ``` 아래 파일을 프로젝트 루트에 생성해주세요. ```json title="seed-design.json" { "rsc": false, "tsx": true, "path": "./seed-design" } ``` ### 컴포넌트를 한번 사용해볼까요? \[#컴포넌트를-한번-사용해볼까요] ```sh npx @seed-design/cli@latest add ui:action-button ``` ### tsconfig.json 설정하기 \[#tsconfigjson-설정하기] ```json title="tsconfig.json" { "compilerOptions": { // ... your options "paths": { // [!code highlight] "seed-design/*": [ // [!code highlight] "./seed-design/*" ] } } } ``` *** SEED AppScreen을 사용하기 위해 SEED Stackflow 플러그인을 설정하는 방법을 알아봅니다. --- file: getting-started/installation/rsbuild.mdx # Rsbuild Rsbuild 프로젝트에서 Seed Design을 설치하고 사용하는 방법을 알아봅니다. ### 의존성 설치 \[#의존성-설치] 필요한 의존성들을 설치해요. - npm: npm install @seed-design/react @seed-design/css - pnpm: pnpm add @seed-design/react @seed-design/css - yarn: yarn add @seed-design/react @seed-design/css - bun: bun add @seed-design/react @seed-design/css ### base.css import하기 \[#basecss-import하기] ```ts title="index.tsx" import "@seed-design/css/base.css"; ``` ### seed-design.json 생성하기 \[#seed-designjson-생성하기] ### init 명령어 입력하기 \[#init-명령어-입력하기] ```sh npx @seed-design/cli@latest init ``` ### seed-design 설정 \[#seed-design-설정] ```sh ◇ TypeScript를 사용중이신가요? │ Yes │ ◇ React Server Components를 사용중이신가요? │ No │ ◇ seed-design 폴더 경로를 입력해주세요. (기본값은 프로젝트 루트에 생성됩니다.) │ ./seed-design ``` 아래 파일을 프로젝트 루트에 생성해주세요. ```json title="seed-design.json" { "rsc": false, "tsx": true, "path": "./seed-design" } ``` ### tsconfig.json 설정하기 \[#tsconfigjson-설정하기] 위에서 설정한 [seed-design.json path](/react/getting-started/cli/configuration#path)의 경로와 동일해야 해요. ```json title="tsconfig.json" { "compilerOptions": { // ... your options "paths": { // [!code highlight] "seed-design/*": [ // [!code highlight] "./seed-design/*" ] } } } ``` ### Rsbuild config 수정하기 \[#rsbuild-config-수정하기] - npm: npm install -D @seed-design/rsbuild-plugin - pnpm: pnpm add -D @seed-design/rsbuild-plugin - yarn: yarn add --dev @seed-design/rsbuild-plugin - bun: bun add --dev @seed-design/rsbuild-plugin `seedDesignPlugin`은 [theming](/react/getting-started/styling/theming) 및 CSS 파일 자동 로드 등을 담당해요. ```ts title="rsbuild.config.ts" import { defineConfig } from "@rsbuild/core"; import { pluginReact } from "@rsbuild/plugin-react"; import { seedDesignPlugin } from "@seed-design/rsbuild-plugin"; // [!code highlight] export default defineConfig({ plugins: [ pluginReact(), seedDesignPlugin(), // [!code highlight] ], }); ``` ### 컴포넌트를 한번 사용해볼까요? \[#컴포넌트를-한번-사용해볼까요] ```sh npx @seed-design/cli@latest add ui:action-button ``` *** SEED AppScreen을 사용하기 위해 SEED Stackflow 플러그인을 설정하는 방법을 알아봅니다. --- file: getting-started/installation/vite.mdx # Vite Vite 프로젝트에서 Seed Design을 설치하고 사용하는 방법을 알아봅니다. ### 의존성 설치 \[#의존성-설치] 필요한 의존성들을 설치해요. - npm: npm install @seed-design/react @seed-design/css - pnpm: pnpm add @seed-design/react @seed-design/css - yarn: yarn add @seed-design/react @seed-design/css - bun: bun add @seed-design/react @seed-design/css ### base.css import하기 \[#basecss-import하기] ```ts title="index.tsx" import "@seed-design/css/base.css"; ``` ### seed-design.json 생성하기 \[#seed-designjson-생성하기] ### init 명령어 입력하기 \[#init-명령어-입력하기] ```sh npx @seed-design/cli@latest init ``` ### seed-design 설정 \[#seed-design-설정] ```sh ◇ TypeScript를 사용중이신가요? │ Yes │ ◇ React Server Components를 사용중이신가요? │ No │ ◇ seed-design 폴더 경로를 입력해주세요. (기본값은 프로젝트 루트에 생성됩니다.) │ ./seed-design ``` 아래 파일을 프로젝트 루트에 생성해주세요. ```json title="seed-design.json" { "rsc": false, "tsx": true, "path": "./seed-design" } ``` ### tsconfig.json 설정하기 \[#tsconfigjson-설정하기] 위에서 설정한 [seed-design.json path](/react/getting-started/cli/configuration#path)의 경로와 동일해야 해요. ```json title="tsconfig.app.json" { "compilerOptions": { // ... your options "paths": { // [!code highlight] "seed-design/*": [ // [!code highlight] "./seed-design/*" ] } } } ``` ### Vite config 수정하기 \[#vite-config-수정하기] - npm: npm install -D @seed-design/vite-plugin - pnpm: pnpm add -D @seed-design/vite-plugin - yarn: yarn add --dev @seed-design/vite-plugin - bun: bun add --dev @seed-design/vite-plugin `seedDesignPlugin`은 [theming](/react/getting-started/styling/theming) 및 CSS 파일 자동 로드 등을 담당해요. ```ts title="vite.config.ts" import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; import { seedDesignPlugin } from "@seed-design/vite-plugin"; // [!code highlight] export default defineConfig({ plugins: [ react(), seedDesignPlugin(), // [!code highlight] ], resolve: { tsconfigPaths: true, // [!code highlight] }, }); ``` - npm: npm install -D @seed-design/vite-plugin vite-tsconfig-paths - pnpm: pnpm add -D @seed-design/vite-plugin vite-tsconfig-paths - yarn: yarn add --dev @seed-design/vite-plugin vite-tsconfig-paths - bun: bun add --dev @seed-design/vite-plugin vite-tsconfig-paths `seedDesignPlugin`은 [theming](/react/getting-started/styling/theming) 및 CSS 파일 자동 로드 등을 담당해요. ```ts title="vite.config.ts" import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; import { seedDesignPlugin } from "@seed-design/vite-plugin"; // [!code highlight] import tsconfigPaths from "vite-tsconfig-paths"; // [!code highlight] export default defineConfig({ plugins: [ react(), seedDesignPlugin(), // [!code highlight] tsconfigPaths(), // [!code highlight] ], }); ``` ### 컴포넌트를 한번 사용해볼까요? \[#컴포넌트를-한번-사용해볼까요] ```sh npx @seed-design/cli@latest add ui:action-button ``` *** SEED AppScreen을 사용하기 위해 SEED Stackflow 플러그인을 설정하는 방법을 알아봅니다. --- file: getting-started/installation/webpack.mdx # Webpack Webpack 프로젝트에서 Seed Design을 설치하고 사용하는 방법을 알아봅니다. ### 의존성 설치 \[#의존성-설치] 필요한 의존성들을 설치해요. - npm: npm install @seed-design/react @seed-design/css @seed-design/webpack-plugin - pnpm: pnpm add @seed-design/react @seed-design/css @seed-design/webpack-plugin - yarn: yarn add @seed-design/react @seed-design/css @seed-design/webpack-plugin - bun: bun add @seed-design/react @seed-design/css @seed-design/webpack-plugin ### base.css import하기 \[#basecss-import하기] ```ts title="index.tsx" import "@seed-design/css/base.css"; ``` ### Webpack config 설정하기 \[#webpack-config-설정하기] ```ts title="vite.config.ts" +import { SeedDesignPlugin } from '@seed-design/webpack-plugin'; export default defineConfig({ plugins: [ + new SeedDesignPlugin() ] }) ``` ### seed-design.json 생성하기 \[#seed-designjson-생성하기] ### init 명령어 입력하기 \[#init-명령어-입력하기] ```sh npx @seed-design/cli@latest init ``` ### seed-design 설정 \[#seed-design-설정] ```sh ◇ TypeScript를 사용중이신가요? │ Yes │ ◇ React Server Components를 사용중이신가요? │ No │ ◇ seed-design 폴더 경로를 입력해주세요. (기본값은 프로젝트 루트에 생성됩니다.) │ ./seed-design ``` 아래 파일을 프로젝트 루트에 생성해주세요. ```json title="seed-design.json" { "rsc": false, "tsx": true, "path": "./seed-design" } ``` ### 컴포넌트를 한번 사용해볼까요? \[#컴포넌트를-한번-사용해볼까요] ```sh npx @seed-design/cli@latest add ui:action-button ``` ### tsconfig.json 설정하기 \[#tsconfigjson-설정하기] ```json title="tsconfig.json" { "compilerOptions": { // ... your options "paths": { // [!code highlight] "seed-design/*": [ // [!code highlight] "./seed-design/*" ] } } } ``` *** SEED AppScreen을 사용하기 위해 SEED Stackflow 플러그인을 설정하는 방법을 알아봅니다. --- file: getting-started/styling/cascade-layers.mdx # Cascade Layers (Experimental) CSS Cascade Layers를 사용하여 SEED 스타일의 우선순위를 제어하는 방법을 알아봅니다. Tailwind CSS 4 등 CSS Cascade Layer를 사용하는 CSS 프레임워크와 SEED 컴포넌트를 함께 사용할 때, 프레임워크의 유틸리티 클래스로 SEED 컴포넌트 스타일을 오버라이드하려면 다음과 같은 설정이 필요합니다. - `@layer` directive로 래핑된 버전의 SEED CSS 사용 - 레이어 간 적절한 우선순위 설정 CSS Cascade Layers는 Chrome 99+, Safari 15.4+, Firefox 97+ 이상에서 지원됩니다. ([Can I Use](https://caniuse.com/css-cascade-layers)) ## 개요 \[#개요] 기본적으로 SEED CSS는 `@layer` 없이 제공됩니다. 이때 `@layer` directive 없이 정의된 unlayered 스타일은 layered 스타일보다 항상 높은 우선순위를 갖습니다. ([MDN](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Cascade_layers#cascade_layer_precedence_order)) ```css /* SEED 컴포넌트 (unlayered) */ /* layered 스타일보다 우선순위를 가짐 */ .seed-floating-action-button__root { background: var(--seed-color-bg-brand-solid); } /* Tailwind 유틸리티 (layered) */ /* class="seed-floating-action-button__root bg-red-100" 사용 시 SEED 스타일이 우선순위를 가짐 */ @layer utilities { .bg-red-100 { background-color: var(--color-red-100); } } ``` 이 문제를 해결하기 위해 SEED는 `@layer`로 래핑된 CSS 파일을 별도로 제공합니다. ## 설정 \[#설정] ### base.css 대신 base.layered.css 사용 \[#basecss-대신-baselayeredcss-사용] ```css title="style.css" @import "@seed-design/css/base.layered.css"; ``` ### 레이어 순서 커스터마이징 \[#레이어-순서-커스터마이징] `seed-base` 및 `seed-components` 레이어와 다른 레이어의 선언 순서를 적절히 조절합니다. #### Tailwind CSS \[#tailwind-css] Tailwind CSS 4 사용 시 유틸리티 클래스를 사용하여 SEED 컴포넌트 스타일을 오버라이드하려면, `@layer` 선언에서 Tailwind `utilities` 레이어가 `seed-components` 레이어보다 높은 우선순위를 가지도록 설정해야 합니다. ```css @layer theme, base, seed-base, components, seed-components, utilities; ``` ### 번들러 설정 \[#번들러-설정] 번들러의 module resolution 설정에 `seed-layered` condition을 추가하세요. 이 설정을 통해 SEED React 컴포넌트가 내부적으로 CSS를 import할 때, 자동으로 layered 버전을 사용합니다. ```ts title="vite.config.ts" import { defineConfig } from "vite"; export default defineConfig({ resolve: { conditions: ["seed-layered"], // [!code highlight] }, // ... }); ``` Vite `conditions` 옵션에 대해 알아봅니다. ```ts title="rsbuild.config.ts" import { defineConfig } from "@rsbuild/core"; export default defineConfig({ resolve: { conditionNames: ["seed-layered", "..."], // [!code highlight] }, // ... }); ``` Rsbuild `conditionNames` 옵션에 대해 알아봅니다. ```js title="webpack.config.js" module.exports = { resolve: { conditionNames: ["seed-layered", "..."], // [!code highlight] }, // ... }; ``` Webpack `conditionNames` 옵션에 대해 알아봅니다. #### Chunk Splitting 사용 시 레이어 순서 문제 \[#chunk-splitting-사용-시-레이어-순서-문제] 번들러가 CSS를 여러 청크로 분리하는 경우, `@layer seed-components { ... }` 블록이 포함된 청크가 다른 `@layer` 선언보다 먼저 로드되면서 레이어 우선순위가 의도한 것과 달라질 수 있습니다. **HTML에 `@layer` 선언을 인라인**하여 모든 `` 태그보다 먼저 로드되도록 보장할 수 있습니다. [`transformIndexHtml`](https://vite.dev/guide/api-plugin#transformindexhtml) 플러그인 훅으로 ``의 맨 앞에 ` ``` ## SEED의 레이어 구조 \[#seed의-레이어-구조] SEED는 두 개의 레이어를 사용합니다. | 이름 | 내용 | 파일 | 비고 | | ----------------- | -------------------------- | ----------------------- | ------------------------------ | | `seed-base` | 디자인 토큰, 글로벌 스타일, keyframes | `base.layered.css` | 별도 import 필요 | | `seed-components` | 컴포넌트 스타일 | `recipes/*.layered.css` | SEED React 컴포넌트 사용 시 자동 import | ## 관련 문서 \[#관련-문서] Tailwind CSS에서 SEED 토큰을 유틸리티 클래스로 사용하는 방법을 알아봅니다. --- file: getting-started/styling/tailwind-css.mdx # Tailwind CSS Tailwind CSS 프로젝트에서 SEED 디자인 시스템을 사용하는 방법을 알아봅니다. SEED에서 제공하는 토큰을 Tailwind CSS 3과 4 유틸리티 클래스를 통해 쉽게 사용하고, SEED React 컴포넌트의 스타일을 오버라이드할 수 있습니다. ## 설정 \[#설정] ### 의존성 설치 \[#의존성-설치] - npm: npm install @seed-design/css @seed-design/tailwind4-theme - pnpm: pnpm add @seed-design/css @seed-design/tailwind4-theme - yarn: yarn add @seed-design/css @seed-design/tailwind4-theme - bun: bun add @seed-design/css @seed-design/tailwind4-theme ### CSS 파일 설정 \[#css-파일-설정] ```css title="style.css" @layer theme, base, seed-base, components, seed-components, utilities; @import "@seed-design/css/base.layered.css"; @import "tailwindcss"; @import "@seed-design/tailwind4-theme"; ``` `@layer` directive를 통해 SEED CSS와 Tailwind 간의 스타일 우선순위를 결정합니다. 자세한 내용은 [Cascade Layers](/react/getting-started/styling/cascade-layers) 문서를 참고하세요. ### 번들러 설정 \[#번들러-설정] SEED React 컴포넌트가 layered CSS 파일을 사용하도록 번들러의 module resolution 설정에 `seed-layered` condition을 추가하세요. 번들러별 설정 방법은 [Cascade Layers](/react/getting-started/styling/cascade-layers#번들러-설정) 문서를 참고하세요. ### Theming 설정 \[#theming-설정] Tailwind CSS 스타일시트는 theming 스크립트와 별개로 작동합니다. 아래 가이드를 참고하여 테마 설정을 진행하세요. - [Vite](/react/getting-started/installation/vite) - [Rsbuild](/react/getting-started/installation/rsbuild) - [Webpack](/react/getting-started/installation/webpack) - [Manual](/react/getting-started/installation/manual) ### 의존성 설치 \[#의존성-설치-1] - npm: npm install @seed-design/css @seed-design/tailwind3-plugin - pnpm: pnpm add @seed-design/css @seed-design/tailwind3-plugin - yarn: yarn add @seed-design/css @seed-design/tailwind3-plugin - bun: bun add @seed-design/css @seed-design/tailwind3-plugin ### base.css import \[#basecss-import] ```ts title="index.tsx" import "@seed-design/css/base.css"; ``` ### Tailwind 설정 파일 수정 \[#tailwind-설정-파일-수정] ```js title="tailwind.config.js" import seedPlugin from "@seed-design/tailwind3-plugin"; // [!code highlight] /** @type {import('tailwindcss').Config} */ export default { content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], theme: { extend: {}, }, plugins: [seedPlugin], // [!code highlight] }; ``` ### Theming 설정 \[#theming-설정-1] Tailwind CSS 스타일시트는 theming 스크립트와 별개로 작동합니다. 아래 가이드를 참고하여 테마 설정을 진행하세요. - [Vite](/react/getting-started/installation/vite) - [Rsbuild](/react/getting-started/installation/rsbuild) - [Webpack](/react/getting-started/installation/webpack) - [Manual](/react/getting-started/installation/manual) ## 유틸리티 클래스 \[#유틸리티-클래스] SEED 디자인 시스템의 모든 디자인 토큰을 Tailwind 유틸리티 클래스로 사용할 수 있습니다. ### 색상 \[#색상] ```tsx import { VStack, Text } from "@seed-design/react"; export default function TailwindColors() { return ( {/* 텍스트 색상 */}

    text-fg-brand

    text-fg-neutral

    text-palette-blue-500

    {/* 배경 색상 */}
    bg-bg-informative-weak
    bg-palette-gray-200
    {/* 테두리 색상 */}
    border-stroke-brand-solid
    border-palette-red-500
    ); } ``` ### 타이포그래피 \[#타이포그래피] ```tsx import { VStack } from "@seed-design/react"; export default function TailwindTypography() { return (

    screen-title

    t3-regular

    t3-bold

    t4-medium

    article-body

    ); } ``` ### 크기 및 여백 \[#크기-및-여백] ```tsx import { VStack, HStack, Text } from "@seed-design/react"; export default function TailwindSpacing() { return ( {/* 크기 유틸리티 */}
    size-x6
    w-x8 h-x4 {/* 패딩 유틸리티 */}
    p-x2
    px-x4 py-x2 {/* 간격 유틸리티 */}
    p-x2 gap-x3 ); } ``` ### 테두리 반경 \[#테두리-반경] ```tsx import { HStack, VStack, Text } from "@seed-design/react"; export default function TailwindBorderRadius() { return (
    rounded-r2
    rounded-r4
    rounded-full ); } ``` ### 그라디언트 \[#그라디언트] ```tsx import { VStack, Text } from "@seed-design/react"; export default function TailwindGradient() { return (
    bg-gradient-glow-magic-[90deg]
    bg-gradient-highlight-magic-[90deg] ); } ``` ## 관련 문서 \[#관련-문서] Tailwind CSS를 사용하지 않아도 SEED 토큰을 쉽게 사용할 수 있습니다. 레이아웃 컴포넌트에서 SEED 토큰을 사용하는 방법을 알아봅니다. --- file: getting-started/styling/theming.mdx # Theming SEED Design의 테마 시스템을 이해하고 활용하는 방법을 알아봅니다. ## 개요 \[#개요] SEED Design은 라이트 모드와 다크 모드를 지원하는 테마 시스템을 제공합니다. 테마는 HTML 요소의 `data-seed-color-mode`와 `data-seed-user-color-scheme` 속성을 통해 제어됩니다. 두 속성의 역할과 차이점을 이해하고, 전역 테마 설정 방법과 특정 영역에만 테마를 오버라이딩하는 방법을 알아봅니다. ## 테마 속성 이해하기 \[#테마-속성-이해하기] SEED Design의 테마 시스템은 두 가지 `data-*` 속성을 사용합니다. ### `data-seed-color-mode` \[#data-seed-color-mode] 애플리케이션의 **테마 정책**을 설정하는 속성입니다. 세 가지 값을 가질 수 있습니다. - `system`: 사용자의 시스템 설정에 따라 자동으로 라이트/다크 모드를 전환합니다. - `light-only`: 항상 라이트 모드로 고정합니다. - `dark-only`: 항상 다크 모드로 고정합니다. ```html ``` ### `data-seed-user-color-scheme` \[#data-seed-user-color-scheme] 사용자 기기의 **실제 컬러 스킴**을 나타내는 속성입니다. 두 가지 값을 가집니다. - `light`: 사용자의 시스템이 라이트 모드로 설정되어 있음 - `dark`: 사용자의 시스템이 다크 모드로 설정되어 있음 이 값은 `data-seed-color-mode="system"`일 때만 의미를 가지며, `prefers-color-scheme` 미디어 쿼리를 통해 자동으로 업데이트됩니다. ```html ``` ### 작동 원리 \[#작동-원리] SEED CSS는 두 속성의 조합을 통해 적절한 색상 토큰을 적용합니다: ```css /* 라이트 모드가 적용되는 경우 */ :root, :root[data-seed-color-mode="system"][data-seed-user-color-scheme="light"], :root[data-seed-color-mode="light-only"], :root [data-seed-color-mode="light-only"] { /* 라이트 모드 색상 토큰 */ } /* 다크 모드가 적용되는 경우 */ :root[data-seed-color-mode="system"][data-seed-user-color-scheme="dark"], :root[data-seed-color-mode="dark-only"], :root [data-seed-color-mode="dark-only"] { /* 다크 모드 색상 토큰 */ } ``` ## 전역 테마 적용하기 \[#전역-테마-적용하기] 애플리케이션 전체에 테마를 적용하는 방법은 Installation 문서를 참고하세요. - [Vite](/react/getting-started/installation/vite) - [Rsbuild](/react/getting-started/installation/rsbuild) - [Webpack](/react/getting-started/installation/webpack) - [Manual](/react/getting-started/installation/manual) 번들러 플러그인을 사용하거나, HTML에 직접 속성과 스크립트를 추가하는 방법을 확인할 수 있습니다. ## 일부 요소에만 테마 오버라이드하기 \[#일부-요소에만-테마-오버라이드하기] 특정 영역이나 컴포넌트에만 다른 테마를 적용하고 싶을 때는 해당 요소에 `data-seed-color-mode`에 `light-only`, `dark-only` 또는 `system` 값을 설정합니다. ```tsx import { ActionButton } from "seed-design/ui/action-button"; import { actionButtonVariantMap } from "@seed-design/css/recipes/action-button"; import { Text, VStack } from "@seed-design/react"; function Demo() { return ( <> {actionButtonVariantMap.variant.map((variant) => ( {variant} ))} ); } export default function ThemingColorModeOverride() { return (
    라이트 모드 다크 모드
    ); } ``` ## 클라이언트에서 현재 테마 정보 감지하기 \[#클라이언트에서-현재-테마-정보-감지하기] 컴포넌트에서 현재 테마 설정에 따라 다른 동작을 수행해야 할 때가 있습니다. 이런 경우 `data-seed-color-mode`와 `data-seed-user-color-scheme` 속성을 감지하여 사용할 수 있습니다. 다음은 커스텀 훅을 만들어 현재 테마 정보를 감지하는 예제입니다: ```tsx "use client"; import { useEffect, useState } from "react"; export type ColorMode = "system" | "light-only" | "dark-only"; export type UserColorScheme = "light" | "dark"; export interface ThemeInfo { colorMode: ColorMode; userColorScheme: UserColorScheme; } function readThemeInfo(): ThemeInfo { if (typeof document === "undefined") { return { colorMode: "system", userColorScheme: "light", }; } const colorMode = document.documentElement.getAttribute( "data-seed-color-mode" ) as ColorMode; const userColorScheme = document.documentElement.getAttribute( "data-seed-user-color-scheme" ); return { colorMode: colorMode || "system", userColorScheme: userColorScheme === "dark" ? "dark" : "light", }; } export function useTheme(): ThemeInfo { const [themeInfo, setThemeInfo] = useState(readThemeInfo); useEffect(() => { if (typeof document === "undefined") { return; } const observer = new MutationObserver(() => { setThemeInfo(readThemeInfo()); }); observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-seed-color-mode", "data-seed-user-color-scheme"], }); return () => observer.disconnect(); }, []); return themeInfo; } ``` 이 훅은 다음과 같이 사용할 수 있습니다: ```tsx "use client"; function MyComponent() { const { colorMode, userColorScheme } = useTheme(); return (

    테마 정책: {colorMode}

    현재 컬러 스킴: {userColorScheme}

    {userColorScheme === "dark" ? ( ) : ( )}
    ); } ``` ### 작동 원리 \[#작동-원리-1] - `MutationObserver`를 사용하여 `data-seed-color-mode`와 `data-seed-user-color-scheme` 속성의 변경을 감지합니다. - `colorMode`는 애플리케이션의 테마 정책(`system`, `light-only`, `dark-only`)을 나타냅니다. - `userColorScheme`는 현재 적용된 실제 컬러 스킴(`light` 또는 `dark`)을 나타냅니다. - 시스템 설정이 변경되거나 테마가 전환될 때 자동으로 상태가 업데이트됩니다. --- file: migration/guide.mdx # Migrating from Sprout Sprout에서 SEED React로 마이그레이션하기 위해 필요한 작업들을 안내합니다. ## 마이그레이션 개요 \[#마이그레이션-개요] Sprout에서 SEED React로의 마이그레이션은 아래와 같은 순서로 진행됩니다. 1. 이전 버전 용례 분석 2. SEED 설치 3. 파운데이션 마이그레이션 (일괄 적용 권장) 4. 컴포넌트 마이그레이션 (화면별로 적용 권장 및 점진적 적용 가능) 기본적으로 파운데이션까지는 프로젝트 전체에 일괄 교체를 진행합니다. 컴포넌트는 그 이후에 화면별로 적용하거나, 컴포넌트별로 적용하는 것과 같이 점진적 적용이 가능합니다. ## SEED 설치 \[#seed-설치] SEED를 사용하기 위한 기반 작업들을 먼저 진행합니다. 아래 작업들이 끝나면 SEED 사용이 가능합니다. ### 의존성 설치 \[#의존성-설치] 필요한 의존성들을 설치해요. - npm: npm install @seed-design/react @seed-design/css - pnpm: pnpm add @seed-design/react @seed-design/css - yarn: yarn add @seed-design/react @seed-design/css - bun: bun add @seed-design/react @seed-design/css * `@seed-design/react`는 리액트 컴포넌트 라이브러리가 전부 포함되어 있습니다. - 기존 `sprout` 패키지들을 대체합니다. - 컴포넌트 마이그레이션 전까지는 기존 패키지들을 사용해주세요. * `@seed-design/css`는 스타일 관련된 모든 것이 포함되어 있습니다. (vars, theming, recipe, css, etc.) - 기존 `@seed-design/stylesheet` 라이브러리를 대체합니다. - 기존 `@seed-design/design-token` 라이브러리를 대체합니다. - 파운데이션 마이그레이션 전까지는 기존 패키지들을 사용해주세요. {/* ----------------------------------------------------------------------- */} {/* ----------------------------------------------------------------------- */} ### CSS import \[#css-import] Sprout `@seed-design/stylesheet` 패키지의 `global.css`와 상호운용이 가능합니다. 번들러 플러그인([webpack](https://github.com/daangn/seed-design/tree/dev/packages/webpack-plugin), [vite](https://github.com/daangn/seed-design/tree/dev/packages/vite-plugin), [rsbuild](https://github.com/daangn/seed-design/tree/dev/packages/rsbuild-plugin))을 사용하는 경우에는 `all.css` 대신 `base.css`를 사용해주세요. ```ts title="index.tsx" import "@seed-design/css/all.css"; // [!code ++] ``` SEED 스타일시트 사용법은 [Installation의 css import](/react/getting-started/installation/manual#allcss-import하기) 설명을 참고해주세요. ### Theme 적용 \[#theme-적용] #### 변경사항 \[#변경사항] 기존 Sprout에서 사용되던 data-attribute가 바뀌었습니다. ```html ``` - `data-seed`: Seed Design 전역 테마 적용 (SEED에서 그대로 유지) - `data-seed-color-mode`: Seed Design 테마 강제 적용 - `light-only`, `dark-only`, `system` 중 하나를 선택할 수 있습니다. - `data-seed-user-color-scheme`: 유저 기기의 설정 - `light`, `dark` 중 하나의 값을 가집니다. - Sprout에서 `data-seed-scale-color` 속성을 사용하던 코드가 있다면 `data-seed-user-color-scheme` 속성으로 변경해주세요. 테마 속성에 대한 자세한 설명과 활용 방법을 확인하세요. 해당 속성들을 직접 적용하는 방법도 있지만 `플러그인 적용` 방식을 권장합니다. 지원되지 않는 번들러를 사용하는 경우 직접 `스크립트 적용` 방식을 사용해서 적용해주세요. #### 플러그인 적용 \[#플러그인-적용] Vite, Rsbuild, Webpack 등의 번들러를 사용하는 프로젝트에서는 플러그인을 추가하면 바로 Theming을 적용할 수 있습니다. Vite 사용 시 - [@seed-design/vite-plugin](https://github.com/daangn/seed-design/tree/dev/packages/vite-plugin) * npm: npm install @seed-design/vite-plugin * pnpm: pnpm add @seed-design/vite-plugin * yarn: yarn add @seed-design/vite-plugin * bun: bun add @seed-design/vite-plugin Rsbuild 사용 시 - [@seed-design/rsbuild-plugin](https://github.com/daangn/seed-design/tree/dev/packages/rsbuild-plugin) * npm: npm install @seed-design/rsbuild-plugin * pnpm: pnpm add @seed-design/rsbuild-plugin * yarn: yarn add @seed-design/rsbuild-plugin * bun: bun add @seed-design/rsbuild-plugin Webpack 사용 시 - [@seed-design/webpack-plugin](https://github.com/daangn/seed-design/tree/dev/packages/webpack-plugin) * npm: npm install @seed-design/webpack-plugin * pnpm: pnpm add @seed-design/webpack-plugin * yarn: yarn add @seed-design/webpack-plugin * bun: bun add @seed-design/webpack-plugin #### 스크립트 적용 \[#스크립트-적용] `@seed-design/css/theming` 패키지에서 제공하는 `generateThemingScript` 함수를 사용해서 스크립트를 생성해요. ```ts title="index.tsx" import { generateThemingScript } from "@seed-design/css/theming"; ``` 생성된 스크립트를 직접 적용해주세요. ```html ``` 혹은 직접 스크립트를 작성하실 수도 있습니다. ```html ``` ## 파운데이션 마이그레이션 \[#파운데이션-마이그레이션] 파운데이션 마이그레이션에는 크게 `Color`와 `Typography` 두 가지에 대해서 작업이 필요합니다. ### 용례 분석하기 \[#용례-분석하기] 우선 현재 프로젝트에서 `@seed-design/design-token` 패키지를 사용하는 용례를 분석해야합니다. - `@seed-design/design-token` 패키지의 vars를 사용하는 용례 - `@seed-design/design-token` 패키지의 vars를 확장해서 사용하는 컴포넌트 - 위 컴포넌트를 사용중인 코드 용례 - `@seed-design/stylesheet` 패키지를 사용하는 용례 - `--seed-` prefix로 시작하는 CSS Variable을 사용하는 용례 - 사용중인 CSS 라이브러리와 `@seed-design/design-token` 패키지와 결합해서 사용하는 용례 아래의 프롬프트를 이용해서 현재 프로젝트에서 사용중인 용례를 분석할 수도 있습니다. ```text title="prompt" Seed Design System의 디자인 토큰을 새로운 토큰으로 마이그레이션 할건데 코드 어디 부분들을 변경해야 하는지 보고서를 써줘. 1. `@seed-design/design-token` 패키지의 vars 변수를 사용하는 용례 2. `@seed-design/design-token` 패키지의 vars 변수를 확장해서 사용하는 컴포넌트 3. 2번 컴포넌트를 사용중인 코드 용례 4. `@seed-design/stylesheet` 패키지를 사용하는 용례 5. `--seed-` prefix로 시작하는 CSS Variable을 사용하는 용례 6. 사용중인 CSS 라이브러리와 `@seed-design/design-token` 패키지와 결합해서 사용하는 용례 위와 같은 사례들말고도 네가 더 분석해서 추가적으로 마이그레이션 해야하는 부분들도 알려줘. ``` ### 마이그레이션 전략 \[#마이그레이션-전략] - 만약 여러 패키지를 사용하는 경우에는 공통 패키지 먼저 마이그레이션 합니다. - 전처리해서 사용하는 경우에는 전처리 코드 자체를 SEED로 마이그레이션 한 후 나머지 코드들을 Codemod로 마이그레이션 하는게 좋습니다. - Color와 Typography 각각 진행해도 되지만, Color와 Typography 마이그레이션을 동시에 진행 후 한번에 QA를 진행하는게 더 간결합니다. ### Typography Preset \[#typography-preset] 많은 프로젝트에서 기존 타이포그래피 토큰을 이용해서 유틸리티 함수를 만들어서 사용하는 경우가 있습니다. 이런 경우에는 아래의 코드를 참고해서 전처리를 진행 후 코드들을 그에 맞게 변경해주세요. - [replace-custom-seed-design-typography](https://seed-design.io/react/developer-tools/codemods/available-transforms#replace-custom-seed-design-typography) 커스텀 transform을 사용해서 마이그레이션 할 수 있습니다. ```ts title="typography.ts" import type { TextVariant } from "@seed-design/css/recipes/text"; import { vars as typographyVars } from "@seed-design/css/vars/component/typography"; /** * @see https://seed-design.io/docs/foundation/typography/overview * @example * typography.t1Bold * typography.t2Regular * typography.t3Bold * typography.t4Regular * typography.t5Bold * typography.t6Bold * typography.t7Bold * typography.screenTitle * ... */ export const typography = Object.entries(typographyVars).reduce( (acc, [key, value]) => { const withoutPrefix = key.replace("textStyle", ""); const firstChar = withoutPrefix.charAt(0).toLowerCase(); const restOfString = withoutPrefix.slice(1); const name = (firstChar + restOfString) as TextVariant["textStyle"]; acc[name] = value.enabled.root; return acc; }, {} as Record< TextVariant["textStyle"], { fontSize: string; fontWeight: string; lineHeight: string; } >, ); ``` ### Codemod \[#codemod] 기본적으로 마이그레이션은 [Codemod](/react/developer-tools/codemods/introduction)를 이용해 진행합니다. - npm: npx @seed-design/codemod@latest \ \ \<옵션> - pnpm: pnpm dlx @seed-design/codemod@latest \ \ \<옵션> - yarn: yarn dlx @seed-design/codemod@latest \ \ \<옵션> - bun: bun x @seed-design/codemod@latest \ \ \<옵션> * Codemod는 기본적으로 `color`와 `typography` transform을 분리해서 제공합니다. * 현재는 Tailwind CSS, Stitches 혹은 Emotion, Vanilla Extract CSS와 같은 CSS-in-JS 라이브러리에 대한 transform을 제공합니다. * 각팀에서 커스텀으로 제공되는 transform의 경우에는 `replace-custom-`의 prefix로 제공됩니다. * 커스텀 transform의 변경 예시를 살펴보고 프로젝트와 상황이 같다면 사용할 수 있습니다. * 추가로 특정 라이브러리의 transform이 필요하거나, 필요한 경우 커스텀 transform을 만들 수 있습니다. (디자인코어 팀에 문의) #### Tailwind CSS \[#tailwind-css] - Tailwind V3 플러그인을 제공합니다. - [@seed-design/tailwind3-plugin](https://github.com/daangn/seed-design/tree/dev/packages/tailwind3-plugin) - [@seed-design/tailwind4-theme](https://github.com/daangn/seed-design/tree/dev/packages/tailwind4-theme) - Tailwind transform은 [replace-tailwind-typography](/react/developer-tools/codemods/available-transforms#replace-tailwind-typography), [replace-tailwind-color](/react/developer-tools/codemods/available-transforms#replace-tailwind-color) 두 개가 제공됩니다. - Tailwind 같은 경우는 각 프로젝트에서 커스텀 플러그인을 작성해서 사용중인 경우가 많기 때문에 Codemod가 정확하게 맞지 않을 수 있습니다. - Codemod 페이지에서 변경 예시를 살펴보고 프로젝트와 상황이 같다면 사용할 수 있습니다. - 예시와 맞지 않다면 디자인코어팀에 문의해주세요. #### Stitches CSS \[#stitches-css] - Stitches는 [replace-stitches-styled-typography](/react/developer-tools/codemods/available-transforms#replace-stitches-styled-typography), [replace-stitches-styled-color](/react/developer-tools/codemods/available-transforms#replace-stitches-styled-color), [replace-stitches-theme-color](/react/developer-tools/codemods/available-transforms#replace-stitches-theme-color) 의 형태로 제공합니다. - 자세한 내용은 [Codemod](/react/developer-tools/codemods/introduction) 페이지를 참고해주세요. #### Emotion, Vanilla Extract CSS, (CSS-in-JS) \[#emotion-vanilla-extract-css-css-in-js] - Emotion, Vanilla Extract CSS와 같은 CSS-in-JS 라이브러리는 `@seed-design/design-token`을 사용하는 경우가 대부분입니다. - [replace-seed-design-token-vars](/react/developer-tools/codemods/available-transforms#replace-seed-design-token-vars) transform을 사용해서 일괄적으로 마이그레이션 할 수 있습니다. - 해당 transform은 `color`와 `typography` 두 개 동시에 변경합니다. #### Native CSS \[#native-css] - 기본적으로 `--seed-` prefix가 붙은 CSS Variable을 사용하는 경우가 있습니다. - [replace-css-seed-design-color-variable](/react/developer-tools/codemods/available-transforms#replace-css-seed-design-color-variable) CSS transform도 제공합니다. ### Codemod 사용 시 주의사항 \[#codemod-사용-시-주의사항] Codemod는 다수의 파일을 변경하기 때문에 주의가 필요합니다. - 변경사항 추적과 히스토리 관리를 위해서 별도의 브랜치를 사용할 것을 권장합니다. - transform에 따라 커밋을 나누는 것을 권장합니다. - Codemod 실행 후 `lint`와 `test`를 진행해주세요. ### Codemod Log 파일 \[#codemod-log-파일] Codemod 실행 시 디버깅에 도움을 주는 로그 파일을 생성할 수 있습니다. - `--log` 옵션을 추가해주세요. - 로그 파일은 Codemod 실행 경로에 `.report` 폴더에 생성됩니다. - 로그 파일은 변경사항을 추적하고 히스토리 관리에 도움을 줍니다. #### debug.log \[#debuglog] - 디버깅 정보를 기록합니다. - 변경 시도한 파일 경로와 시간을 기록합니다. ```log title="debug.log" 2025-03-28 19:17:36 [DEBUG]: Starting transformation of /Project/src/component1.tsx 2025-03-28 19:17:36 [DEBUG]: Starting transformation of /Project/src/component2.tsx 2025-03-28 19:17:36 [DEBUG]: Starting transformation of /Project/src/component3.tsx 2025-03-28 19:17:36 [DEBUG]: Starting transformation of /Project/src/component4.tsx ``` #### issues.log \[#issueslog] - 확인이 필요한 변경사항을 기록합니다. - 문제가 있는 파일 경로와 라인 번호를 기록합니다. - 문제가 있는 이유를 기록합니다. - 매핑이 없는 경우를 제외하고는 이미 변경된 상태입니다. ```log title="issues.log" 2025-03-28 18:46:55 [WARN]: /Project/src/component1.tsx (line: 171) ↳ reason: $gray900-static static에서 palette로 변경되는 색상으로 용도 파악 필요 2025-03-28 18:46:55 [WARN]: /Project/src/component2.tsx (line: 194) ↳ reason: $gray900-static static에서 palette로 변경되는 색상으로 용도 파악 필요 2025-03-28 18:46:55 [WARN]: /Project/src/component3.tsx (line: 147) ↳ reason: $gray900-static static에서 palette로 변경되는 색상으로 용도 파악 필요 2025-03-28 18:46:56 [WARN]: /Project/src/component4.tsx (line: 242) ↳ reason: $gray900-static static에서 palette로 변경되는 색상으로 용도 파악 필요 ``` 자주 발생하는 문제들은 제일 아래 [issues.log 파일에 생긴 문제들은 어떻게 해결해야 하나요?](#issueslog-파일에-생긴-문제들은-어떻게-해결해야-하나요)에 정리해두었습니다. #### success.log \[#successlog] - 변경사항 추적에 도움을 줍니다. - 변경된 파일 경로와 라인 번호를 기록합니다 - 변경된 토큰을 기록합니다. - CSS 파일을 변경하는 transform의 경우는 토큰을 일일이 기록하지 않습니다. - tailwind 같은 경우는 변경된 className을 기록합니다. ```log title="success.log" 2025-03-28 18:46:54 /Project/src/hooks/hook1.tsx (line: 149): $gray900 → $palette-gray-1000 (line 149) 2025-03-28 18:46:54 /Project/src/hooks/hook2.tsx (line: 155): $gray700 → $palette-gray-800 (line 155) 2025-03-28 18:46:54 /Project/src/components/component1.tsx (line: 54): $divider1-semantic → $stroke-neutral-muted (line 54) 2025-03-28 18:46:54 /Project/src/components/component2.tsx (line: 60): $paperDefault-semantic → $bg-layer-default (line 60) ``` ### Sprout-SEED Compatibility \[#sprout-seed-compatibility] Color, Typography 마이그레이션이 끝나고 나면 기존 Sprout 컴포넌트와의 호환성 문제가 있을 수 있습니다. Sprout 내부에 Sprout 토큰을 SEED 토큰으로 전부 교체한 Compatibility 버전을 제공합니다. 여기까지 진행해야 파운데이션 마이그레이션이 끝난 것으로 간주할 수 있습니다. [Sprout Compatibility 버전](https://github.com/daangn/sprout/pkgs/npm/sprout-components-button) 페이지에서 `-v3-compat` 태그가 붙은 버전중에 최신 버전으로 모든 컴포넌트를 전부 교체해주시면 됩니다. (모든 컴포넌트 동일한 버전을 제공합니다.) 주의할 점: 너무 예전의 버전을 사용하는 경우 컴포넌트 내부에서 breaking changes가 있을 수 있습니다. Sprout-SEED Compatibility 버전까지 설치가 되었다면 파운데이션 마이그레이션이 끝난 것으로 간주할 수 있습니다. 이론상 이때부터는 `@seed-design/design-token` 패키지와 `@seed-design/stylesheet` 패키지를 제거할 수 있습니다. 또한 SEED 컴포넌트를 점진적 사용이 가능합니다. ### Compat Script 제거 \[#compat-script-제거] ```html ``` 기존에 25.10.0 앱버전 이상에서 Sprout 백그라운드 색상을 SEED로 변경하기 위해 추가했던 스크립트는 마이그레이션이 끝나고 제거해도 됩니다. ## 컴포넌트 마이그레이션 \[#컴포넌트-마이그레이션] TBD ## 자주 묻는 질문 \[#자주-묻는-질문] ### issues.log 파일에 생긴 문제들은 어떻게 해결해야 하나요? \[#issueslog-파일에-생긴-문제들은-어떻게-해결해야-하나요] #### 토큰 매핑이 안되는 경우 (컬러, 타이포그래피) \[#토큰-매핑이-안되는-경우-컬러-타이포그래피] - SEED에서 지원하지 않는 토큰을 사용하는 경우입니다. (주로 alpha 색상 토큰이거나 너무 작거나 큰 타이포 토큰입니다.) - 토큰 매핑을 확인할 수 있는 방법은 [설명](#파운데이션-토큰-매핑은-어디서-확인할-수-있나요)을 참고해주세요. - 해당 경우는 디자이너분과 상의 후 다른 적절한 토큰으로 직접 매핑해주세요. - 결정에 어려움이 있는 경우 디자인코어팀에 문의해주세요. #### alpha 토큰이 사라진 경우 (컬러) \[#alpha-토큰이-사라진-경우-컬러] - alpha가 들어간 토큰은 대부분의 경우 더 이상 사용되지 않습니다. - 직접 CSS에 `alpha` 채널을 적용해주세요. (opacity 속성을 사용하거나, rgba 형식으로 직접 적용) #### static 토큰이 palette로 변경된 경우 (컬러) \[#static-토큰이-palette로-변경된-경우-컬러] - SEED에서 static 컬러 토큰은 웬만해서는 `white`, `black` 두 가지만 지원합니다. - 나머지 색상 (yellow, green, blue, red, gray) 등은 palette 토큰으로 변경되었습니다. - 다크모드와 라이트모드에 따라 색상이 달라질 수 있기 때문에 확인이 필요합니다. - 배경은 변하는데 텍스트 색상이 변하지 않거나, 그 반대의 상황이 나올 수 있습니다. - 대부분의 경우는 static 토큰이 필요한 경우가 아니기 때문에 palette 토큰으로 변경되었습니다. - 이 경우는 디자이너분과 상의 후 기존 static 토큰을 다른 적절한 palette 토큰으로 직접 매핑해주세요. #### 선택지가 두개인 토큰으로 매핑된 경우 (컬러) \[#선택지가-두개인-토큰으로-매핑된-경우-컬러] - 컬러 토큰의 경우 대체 토큰이 두 가지인 경우가 있습니다. (ex. `primary` -> `bg.brand-solid`, `fg.brand`) - `fg`는 텍스트, 아이콘 등 UI의 전경 요소에 사용되는 색상입니다. (color, fill, etc.) - `bg`는 전체 화면 또는 UI의 배경에 사용하는 색상입니다. (backgroundColor, etc.) - 변경되어야 하는 토큰을 Codemod가 판단하기 어려울 수 있습니다. - 이 경우는 직접 적절한 토큰으로 매핑해주세요. #### alternate 토큰으로 변경된 경우 (타이포그래피) \[#alternate-토큰으로-변경된-경우-타이포그래피] - 기본적으로 [Mapping](/docs/migration/migration-reference)은 1:1 매핑을 지원합니다. - 하지만 SEED에서 Deprecated 된 토큰인 경우, 대체 토큰으로 매핑 될 수 있습니다. - 그런 경우 font size나 line height가 변경되었을 수 있습니다. - 이 경우는 직접 UI를 확인해야 합니다. ### 파운데이션 토큰 매핑은 어디서 확인할 수 있나요? \[#파운데이션-토큰-매핑은-어디서-확인할-수-있나요] [Migration Reference](/docs/migration/migration-reference)에서 토큰 매핑을 확인할 수 있습니다. ## Known Issues \[#known-issues] ### Vanilla Extract CSS + Vite + SEED 사용시 \[#vanilla-extract-css--vite--seed-사용시] - `@vanilla-extract/vite-plugin`와 `@seed-design/vite-plugin`을 같이 사용하는 경우 빌드가 실패할 수 있습니다. - `@vanilla-extract/vite-plugin`의 버전을 `5` 이상으로 업그레이드 해주세요. --- file: migration/migrating-icons.mdx # Migrating Icons 구형 아이콘을 신규 아이콘으로 쉽게 마이그레이션해요. ## 업그레이드 방법 \[#업그레이드-방법] 현재 아이콘을 사용하고 있는 방법에 따라 적용할 수 있는 2가지 방법이 있어요. ### Codemod 스크립트 \[#codemod-스크립트] 아래 3개 아이콘 패키지를 사용하고 있다면, 제공되는 codemod 스크립트를 사용해서 리뉴얼된 아이콘 패키지로 손쉽게 업그레이드할 수 있어요. - [`@seed-deesign/react-icon`](https://www.npmjs.com/package/@seed-design/react-icon) - [`@seed-design/icon`](https://www.npmjs.com/package/@seed-design/icon) (deprecated) - [`@karrotmarket/karrot-ui-icon`](https://www.npmjs.com/package/@karrotmarket/karrot-ui-icon) (deprecated) (React 컴포넌트를 사용하는 경우) 3개 패키지에서 사용하고 있는 아이콘은 [`@karrotmarket/react-monochrome-icon`](https://github.com/daangn/seed-icon-v3/pkgs/npm/react-monochrome-icon)의 아이콘으로 대체돼요. ### 수동 업그레이드 \[#수동-업그레이드] 다음과 같은 경우에는 수동으로 업그레이드해야 해요. - PNG, SVG 등의 파일로 기존 아이콘을 사용하는 경우 - [`@karrotmarket/karrot-ui-icon`](https://www.npmjs.com/package/@karrotmarket/karrot-ui-icon) (deprecated)에서 제공되는 SVG 파일을 사용하는 경우 - 다음 패키지를 사용하는 경우 - [`@seed-design/vue2-icon`](https://www.npmjs.com/package/@seed-design/vue2-icon) → [`@daangn/vue2-monochrome-icon`](https://github.com/daangn/seed-icon-v3/pkgs/npm/vue2-monochrome-icon)으로 대체돼요. - [`@seed-design/vue3-icon`](https://www.npmjs.com/package/@seed-design/vue3-icon) → [`@daangn/vue3-monochrome-icon`](https://github.com/daangn/seed-icon-v3/pkgs/npm/vue3-monochrome-icon)으로 대체돼요. ## 작업 순서 \[#작업-순서] 많은 변경사항이 발생하게 되므로, 커밋하지 않은 변경사항이 없는지 확인하고 시작하는 것이 좋아요. ### Codemod 스크립트 \[#codemod-스크립트-1] #### 리뉴얼된 아이콘 패키지 설치 \[#리뉴얼된-아이콘-패키지-설치] - npm: npm install @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon - pnpm: pnpm add @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon - yarn: yarn add @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon - bun: bun add @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon 2개 패키지 중 필요한 패키지만 설치해요. - [`@karrotmarket/react-monochrome-icon`](https://github.com/daangn/seed-icon-v3/pkgs/npm/react-monochrome-icon) - 단색 아이콘 패키지에요. **일반** 아이콘, **카테고리** 아이콘, **서비스** 아이콘이 포함되어 있어요. - 이 패키지에서 제공되는 서비스 아이콘은 검은색으로만 사용할 수 있어요. - codemod 실행 시, 기존 패키지들의 아이콘은 모두 이 패키지의 아이콘으로 대체돼요. - [`@karrotmarket/react-multicolor-icon`](https://github.com/daangn/seed-icon-v3/pkgs/npm/react-multicolor-icon) - 멀티컬러 아이콘 패키지에요. **카테고리** 아이콘과 **서비스** 아이콘이 포함되어 있어요. - 이 패키지에서 제공되는 카테고리 아이콘과 서비스 아이콘은 패키지에서 제공되는 색상 그대로만 사용할 수 있어요. - 기존에 SVG로 사용하던 멀티컬러 아이콘들을 이 패키지로 직접 대체할 수 있어요. 따라서, 필요한 경우에만 설치해도 좋아요. #### Codemod 스크립트 실행 \[#codemod-스크립트-실행] `replace-react-icon` transform을 실행해요. ```shell npx @seed-design/codemod@latest replace-react-icon ``` 해당 transform에 대해 자세히 알아봅니다. #### 결과 확인 \[#결과-확인] 문제 없이 모든 변환이 완료된 경우 다음과 같은 결과가 표시돼요. ``` All done. Results: 0 errors // [!code highlight] 439 unmodified 1 skipped 27 ok Time elapsed: 43.376seconds ``` - `errors`: 파싱 오류 등으로 인해, 아이콘을 참조하는 코드가 있는지 확인하지 못한 파일의 수에요. - 에러가 발생한 파일에 이전 아이콘을 참조하는 코드가 없다면, 해당 파일에서 발생하는 에러는 무시해도 좋아요. - 이전 아이콘을 참조하는 코드가 있다면, 문제가 발생한 부분을 수정하고 다시 스크립트를 실행하거나, 해당 파일의 아이콘 마이그레이션을 직접 진행해주세요. - `unmodified`: 아이콘을 참조하는 코드가 없어, 아무 변환도 이루어지지 않은 파일의 수에요. - `skipped`: 빈 파일 등 변환 대상이 아니어서, 아무 변환도 이루어지지 않은 파일의 수에요. - `ok`: 변환이 이루어진 파일의 수에요. ``` ERR /Users/seed/foo/bar.js Transformation error (Unexpected reserved word 'package'. (3:3)) ``` codemod 스크립트 실행 중 파싱 오류가 발생할 수 있어요. 파싱 오류는 다음과 같은 이유로 발생해요. - `assert`를 사용한 [import assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-3.html#import-attributes)과 같은, deprecated된 문법이 있는 경우 - `package`와 같은 예약어를 변수 이름으로 사용한 경우 등 #### 코드 포맷 \[#코드-포맷] 프로젝트에서 사용하는 포매터를 사용해서 코드를 포맷해요. #### 이전 패키지 제거 \[#이전-패키지-제거] 이전 아이콘이 모두 대체된 것을 확인하고, 이전 패키지를 제거해요. - npm: npm uninstall @seed-design/icon @seed-design/react-icon @karrotmarket/karrot-ui-icon - pnpm: pnpm remove @seed-design/icon @seed-design/react-icon @karrotmarket/karrot-ui-icon - yarn: yarn remove @seed-design/icon @seed-design/react-icon @karrotmarket/karrot-ui-icon - bun: bun remove @seed-design/icon @seed-design/react-icon @karrotmarket/karrot-ui-icon #### 사이드 이펙트 확인 \[#사이드-이펙트-확인] - [사이드 이펙트](#발생-가능한-사이드-이펙트)가 발생했는지 확인하여 의도한 대로 변경이 이루어졌는지 검토해요. - 디자인 QA 과정에서, 자동으로 변경된 아이콘이 아닌 새로운 아이콘을 사용하도록 결정되었을 수 있어요. 이러한 경우, Figma 디자인 파일을 참고하여 코드에 반영해요. - 멀티컬러 아이콘 사용이 필요한 곳에는 [멀티컬러 아이콘 패키지](https://github.com/daangn/seed-icon-v3/pkgs/npm/react-multicolor-icon)를 사용해주세요. ### 수동 업그레이드 \[#수동-업그레이드-1] #### 이전 패키지 제거 \[#이전-패키지-제거-1] 이전 아이콘 패키지들을 제거해요. - npm: npm uninstall @seed-design/icon @seed-design/react-icon @karrotmarket/karrot-ui-icon @seed-design/vue2-icon @seed-design/vue3-icon - pnpm: pnpm remove @seed-design/icon @seed-design/react-icon @karrotmarket/karrot-ui-icon @seed-design/vue2-icon @seed-design/vue3-icon - yarn: yarn remove @seed-design/icon @seed-design/react-icon @karrotmarket/karrot-ui-icon @seed-design/vue2-icon @seed-design/vue3-icon - bun: bun remove @seed-design/icon @seed-design/react-icon @karrotmarket/karrot-ui-icon @seed-design/vue2-icon @seed-design/vue3-icon #### 리뉴얼된 패키지 설치 \[#리뉴얼된-패키지-설치] ##### React \[#react] - npm: npm install @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon - pnpm: pnpm add @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon - yarn: yarn add @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon - bun: bun add @karrotmarket/react-monochrome-icon @karrotmarket/react-multicolor-icon ##### Vue 2 \[#vue-2] - npm: npm install @daangn/vue2-monochrome-icon @daangn/vue2-multicolor-icon - pnpm: pnpm add @daangn/vue2-monochrome-icon @daangn/vue2-multicolor-icon - yarn: yarn add @daangn/vue2-monochrome-icon @daangn/vue2-multicolor-icon - bun: bun add @daangn/vue2-monochrome-icon @daangn/vue2-multicolor-icon ##### Vue 3 \[#vue-3] - npm: npm install @daangn/vue3-monochrome-icon @daangn/vue3-multicolor-icon - pnpm: pnpm add @daangn/vue3-monochrome-icon @daangn/vue3-multicolor-icon - yarn: yarn add @daangn/vue3-monochrome-icon @daangn/vue3-multicolor-icon - bun: bun add @daangn/vue3-monochrome-icon @daangn/vue3-multicolor-icon 2개 패키지 중 필요한 패키지만 설치해요. - `@daangn/*-monochrome-icon` - 단색 아이콘 패키지에요. **일반** 아이콘, **카테고리** 아이콘, **서비스** 아이콘이 포함되어 있어요. - 이 패키지에서 제공되는 서비스 아이콘은 검은색으로만 사용할 수 있어요. - codemod 실행 시, 기존 패키지들의 아이콘은 모두 이 패키지의 아이콘으로 대체돼요. - `@daangn/*-multicolor-icon` - 멀티컬러 아이콘 패키지에요. **카테고리** 아이콘과 **서비스** 아이콘이 포함되어 있어요. - 이 패키지에서 제공되는 카테고리 아이콘과 서비스 아이콘은 패키지에서 제공되는 색상 그대로만 사용할 수 있어요. - 기존에 SVG로 사용하던 멀티컬러 아이콘들을 이 패키지로 직접 대체할 수 있어요. 따라서, 필요한 경우에만 설치해도 좋아요. #### 아이콘 이름 변경 \[#아이콘-이름-변경] [아이콘 마이그레이션 레퍼런스](/docs/migration/migration-reference#iconography) 문서를 참고하여 리뉴얼된 패키지를 사용하도록 코드를 수정해요. - 기존 아이콘의 `IconHome*`은 신규 아이콘에서 `IconHouse*`로 이름이 바뀌었어요. - 기존 아이콘의 `IconHouse*`는 신규 아이콘에서 `IconWindow4House*`로 이름이 바뀌었어요. 따라서, 수동 마이그레이션 시 `IconHome*`이 `IconWindow4House*`로 잘못 변경되지 않게 주의해야 해요. **`IconHouse*`를 먼저 마이그레이션**하고, `IconHome*`을 마이그레이션하는 것을 추천해요. #### 사이드 이펙트 확인 \[#사이드-이펙트-확인-1] - [사이드 이펙트](#발생-가능한-사이드-이펙트)가 발생했는지 확인하여 의도한 대로 변경이 이루어졌는지 검토해요. - 디자인 QA 과정에서, [아이콘 마이그레이션 레퍼런스](/docs/migration/migration-reference#iconography)에 따른 아이콘이 아닌 새로운 아이콘을 사용하도록 결정되었을 수 있어요. 이러한 경우, Figma 디자인 파일을 참고하여 코드에 반영해요. - 멀티컬러 아이콘 사용이 필요한 곳에는 [멀티컬러 아이콘 패키지](https://github.com/daangn/seed-icon-v3/pkgs/npm/react-multicolor-icon)를 사용해주세요. ## 발생 가능한 사이드 이펙트 \[#발생-가능한-사이드-이펙트] ### 정확히 대응되는 리뉴얼된 아이콘이 없는 경우 \[#정확히-대응되는-리뉴얼된-아이콘이-없는-경우] - 이전 아이콘 중 아래 5개 아이콘(총 15개 variant)은 리뉴얼된 아이콘 패키지에 시각적으로 정확히 대응되는 항목이 없어요. - codemod 스크립트 실행 시 **추천되는 아이콘으로 변환되지만**, 시각적인 차이가 크기 때문에 변경 후 확인이 필요해요. | 이전 아이콘 이름 | 리뉴얼된 아이콘 이름 | | --------------------------------------- | ---------------------------------------------------------------------------------------- | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - codemod 스크립트 사용 시, 5개 아이콘(총 15개 variant)이 사용된 경우 터미널에 메시지가 출력돼요. ``` REP ...을 ...로 변경했지만, 변경된 아이콘이 적절한지 확인이 필요해요 ``` - codemod 스크립트를 `--log` flag와 함께 사용 시, 사용된 내역이 `migrate-icons-warnings.log`에 기록돼요. - 다음을 활용하여 코드를 직접 검색할 수 있어요. ```regex (IconBold|IconCobuying|IconDelivery|IconSuggest|IconWriteStory)(Thin|Regular|Fill)|(IconTUppercaseSerif|IconShoppingbag2Stacked|IconTruck|IconLightbulbDot5|IconHorizline2VerticalChatbubbleRight)(Line|Fill) ``` ```shell git grep -E '(IconBold|IconCobuying|IconDelivery|IconSuggest|IconWriteStory)(Thin|Regular|Fill)|(IconTUppercaseSerif|IconShoppingbag2Stacked|IconTruck|IconLightbulbDot5|IconHorizline2VerticalChatbubbleRight)(Line|Fill)' ``` ### 여러 이전 아이콘이 하나의 리뉴얼된 아이콘으로 대체되는 경우 \[#여러-이전-아이콘이-하나의-리뉴얼된-아이콘으로-대체되는-경우] **이전 아이콘과 리뉴얼된 아이콘은 `n:1`로 대응돼요.** 따라서, 같은 페이지에 표시되었던 서로 다른 여러 개의 아이콘이, 동일한 리뉴얼된 아이콘으로 대체되는 경우가 있어요. 의도한 목적대로 아이콘이 표시되는지 확인해요. - 한 이전 아이콘의 3개 variant 중 `Thin` variant와 `Regular` variant는 리뉴얼된 아이콘에서 모두 `Line` variant로 대체돼요. - 예를 들면, `IconForwardThin`, `IconForwardRegular`는 모두 [`IconArrowRightLine`](/docs/foundation/iconography/library?icon=icon_arrow_right_line)으로 대체돼요. - 이전 아이콘의 `Fill` variant는 리뉴얼된 아이콘에서도 `Fill` variant로 대체돼요. - 여러 개의 이전 아이콘이 하나의 리뉴얼된 아이콘으로 대체되는 경우도 있어요. - 예를 들면, `IconHeadphoneRegular`, `IconHelpcenterRegular`, `IconHelperRegular`는 모두 [`IconHeadsetLine`](/docs/foundation/iconography/library?icon=icon_headset_line)으로 대체돼요. --- file: stackflow/alert-dialog.mdx # Alert Dialog Alert Dialog를 Stackflow와 함께 사용하는 방법을 안내합니다. Alert Dialog 컴포넌트에 대해 자세히 알아봅니다. ## Making an Alert Dialog Activity \[#making-an-alert-dialog-activity] **일반적인 경우 Alert Dialog를 [Activity](https://stackflow.so/docs/get-started/activity)로 만들어 사용하는 것을 권장합니다.** - Activity로 관리되므로, 하위 Activity보다 높고 상위 Activity보다는 낮은 z-index를 갖도록 관리하기 쉽습니다. - 딥링킹이 가능합니다. (URL 접속으로 Alert Dialog를 열 수 있습니다.) - [@stackflow/plugin-basic-ui](https://www.npmjs.com/package/@stackflow/plugin-basic-ui) `Modal`에서의 마이그레이션이 쉽습니다. ```tsx title='ActivityAlertDialogActivity.tsx' import { VStack } from "@seed-design/react"; import { useFlow, type StaticActivityComponentType } from "@stackflow/react/future"; import { AppBar, AppBarBackButton, AppBarLeft, AppBarMain, AppBarIconButton, AppBarRight, } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { ActionButton } from "seed-design/ui/action-button"; import { IconHouseLine } from "@karrotmarket/react-monochrome-icon"; declare module "@stackflow/config" { interface Register { ActivityAlertDialogActivity: {}; } } const ActivityAlertDialogActivity: StaticActivityComponentType< "ActivityAlertDialogActivity" > = () => { const { push } = useFlow(); return ( push("ActivityHome", {})}> push("ActivityAlertDialog", {})} > ActivityAlertDialog를 Push push("ActivityAlertDialogActivity", {})} > 지금 열린 이 Activity를 Push ); }; export default ActivityAlertDialogActivity; ``` ```tsx title='ActivityAlertDialog.tsx' import { useActivity, useFlow, type StaticActivityComponentType } from "@stackflow/react/future"; import { useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, } from "seed-design/ui/alert-dialog"; import { ResponsivePair, VStack } from "@seed-design/react"; import { send } from "@stackflow/compat-await-push"; import { useActivityZIndexBase } from "@seed-design/stackflow"; import { Switch } from "seed-design/ui/switch"; declare module "@stackflow/config" { interface Register { ActivityAlertDialog: {}; } } const ActivityAlertDialog: StaticActivityComponentType<"ActivityAlertDialog"> = () => { const activity = useActivity(); const { pop, push } = useFlow(); const [keepMounted, setKeepMounted] = useState(false); const handleClose = (open: boolean) => { if (!open) { pop(); send({ activityId: activity.id, data: { message: "hello", }, }); } }; const open = keepMounted ? activity.transitionState === "enter-active" || activity.transitionState === "enter-done" : activity.isActive; const onOpenChange = keepMounted ? (open: boolean) => !open && activity.isActive && handleClose(open) : handleClose; return ( 제목 다람쥐 헌 쳇바퀴에 타고파 확인 push("ActivityDetail", { title: "AlertDialog에서 Push됨", body: keepMounted ? "AlertDialog가 언마운트되지 않았으므로, 현재 Activity를 pop하는 경우 AlertDialog가 열린 상태로 표시됩니다." : "AlertDialog가 언마운트되었으므로, 현재 Activity를 pop하는 경우 AlertDialog가 다시 enter 트랜지션을 재생하며 마운트됩니다.", }) } > Push ); }; export default ActivityAlertDialog; ``` ### Usage \[#usage] ```tsx import { useActivityZIndexBase } from "@seed-design/stackflow"; import { useActivity, useFlow, type ActivityComponentType } from "@stackflow/react/future"; // ... more imports const ActivityAlertDialog: ActivityComponentType<"ActivityAlertDialog"> = () => { const { pop, push } = useFlow(); return ( {/* [!code highlight] */} !open && pop()}> {/* [!code highlight] */} 제목 설명 텍스트 확인 push("AnotherActivity", {})} > 다른 화면으로 이동 ); }; ``` 1. `open` prop에 `useActivity().isActive`를 전달하여 Activity가 활성화될 때 Alert Dialog가 열리도록 합니다. 2. `onOpenChange`를 통해 Alert Dialog가 닫힐 때 `pop()`을 실행하여 Activity를 종료합니다. 3. `layerIndex={useActivityZIndexBase()}`로 Alert Dialog Activity의 z-index 기준점을 전달합니다. - [`useActivityZIndexBase`에 대해 자세히 알아보기](#about-useactivityzindexbase) #### Keeping Alert Dialog Mounted \[#keeping-alert-dialog-mounted] Alert Dialog Activity 위에 다른 Activity를 push할 때 Alert Dialog가 unmount되는 것을 방지하려면, - `open` 상태를 `isActive` 대신 `transitionState`로 관리하고 - `modal` prop을 `isActive`로 설정하고 - `onOpenChange` 핸들러에서 `!isOpen && isActive`인 경우 `pop()`을 실행하도록 합니다. 이 패턴은 Alert Dialog 액티비티 위에 다른 오버레이 컴포넌트 액티비티를 중첩하여 표시하고 싶은 경우 유용합니다. ```tsx const { isActive, transitionState } = useActivity(); return ( !open && isActive && pop()} > {/* ... */} ); ``` 1. `open`을 `transitionState`로 관리하여 다른 Activity가 위에 push되어도 Alert Dialog가 unmount되지 않도록 합니다. 2. `modal={isActive}`로 Alert Dialog Activity가 비활성 상태일 때 `modal`을 `false`로 설정합니다. 이렇게 하지 않으면, 위에 push된 Activity에서 포커스 및 스크린 리더 접근이 동작하지 않습니다. 3. `onOpenChange` 핸들러에서 `isActive`인 경우에만 `pop()`을 실행하여, 비활성 상태에서의 의도치 않은 Activity 종료를 방지합니다. ## Syncing Alert Dialog State with a Step \[#syncing-alert-dialog-state-with-a-step] Alert Dialog를 Activity로 만들 수 없는 경우, Alert Dialog가 표시된 상태를 [Step](https://stackflow.so/docs/get-started/navigating-step)으로 만들 수 있습니다. - 현재 Activity를 유지하면서도, 뒤로 가기 버튼 등으로 Alert Dialog를 닫을 수 있습니다. - `AlertDialogTrigger`를 사용하여 Alert Dialog를 열고 닫을 수 있습니다. **Activity로 만들지 않은 Alert Dialog에서 다른 Activity를 push하기 전, z-index 문제를 방지하기 위해 Alert Dialog를 닫으세요.** Alert Dialog를 닫을 수 없거나, Alert Dialog를 연 Activity로 돌아왔을 때 Alert Dialog가 열린 상태를 유지해야 하는 경우 [Alert Dialog를 Activity로](#making-an-alert-dialog-activity) 만들어 사용하는 것을 권장합니다. Activity 간 유려한 트랜지션을 제공하기 위해 하위 AppScreen 요소 중 일부가 상위 AppScreen 요소보다 위에 위치합니다. 이 제약으로 인해, 열린 상태의 Alert Dialog는 독립적인 Activity로 만들지 않는 경우 하위 Activity와 상위 Activity 사이에 위치시키는 것이 불가능합니다. ```tsx title='ActivityAlertDialogStep.tsx' import { HStack, Portal, VStack } from "@seed-design/react"; import { useActivityZIndexBase } from "@seed-design/stackflow"; import { useActivityParams, useFlow, useStepFlow, type StaticActivityComponentType, } from "@stackflow/react/future"; import { useEffect, useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { AppBar, AppBarMain, AppBarIconButton, AppBarRight } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { IconHouseLine } from "@karrotmarket/react-monochrome-icon"; import { AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, } from "seed-design/ui/alert-dialog"; declare module "@stackflow/config" { interface Register { ActivityAlertDialogStep: { "alert-dialog"?: "open"; }; } } const ActivityAlertDialogStep: StaticActivityComponentType<"ActivityAlertDialogStep"> = () => { const [open, setOpen] = useState(false); const { push } = useFlow(); const { pushStep, popStep } = useStepFlow("ActivityAlertDialogStep"); const params = useActivityParams<"ActivityAlertDialogStep">(); const isOverlayOpen = params["alert-dialog"] === "open"; useEffect(() => setOpen(isOverlayOpen), [isOverlayOpen]); const onOpenChange = (newOpen: boolean) => { setOpen(newOpen); if (newOpen && !isOverlayOpen) { pushStep((params) => ({ ...params, "alert-dialog": "open" })); return; } if (!newOpen && isOverlayOpen) { popStep(); return; } }; return ( push("ActivityHome", {})}> Alert Dialog 열기 Step Alert Dialog가 Step으로 만들어져 있기 때문에 뒤로 가기로 닫을 수 있습니다. setOpen(false)} variant="neutralWeak"> 닫기 { // 이 Alert Dialog는 Activity로 만들어지지 않았기 때문에, z-index 정리를 위해 // Alert Dialog를 먼저 닫고 다음 Activity를 push해야 합니다. setOpen(false); push("ActivityDetail", { title: "Alert Dialog에서 이동한 화면", body: "Alert Dialog를 닫고 이동했습니다.", }); }} > Push ); }; export default ActivityAlertDialogStep; ``` ### Usage \[#usage-1] ```tsx import { useActivityZIndexBase } from "@seed-design/stackflow"; import { Portal } from "@seed-design/react"; import { useActivity, useActivityParams, useFlow, useStepFlow, type ActivityComponentType, } from "@stackflow/react/future"; import { useEffect, useState } from "react"; // ... more imports declare module "@stackflow/config" { interface Register { ActivityHome: { "alert-dialog"?: "open"; }; } } const ActivityHome: ActivityComponentType<"ActivityHome"> = () => { const [open, setOpen] = useState(false); const { push } = useFlow(); const { pushStep, popStep } = useStepFlow("ActivityHome"); const params = useActivityParams<"ActivityHome">(); // [!code highlight] const isOverlayOpen = params["alert-dialog"] === "open"; // [!code highlight] useEffect(() => { if (!isOverlayOpen) { setOpen(false); } }, [isOverlayOpen]); // [!code highlight] const onOpenChange = (newOpen: boolean) => { setOpen(newOpen); if (newOpen && !isOverlayOpen) { pushStep((params) => ({ ...params, "alert-dialog": "open" })); return; } if (!newOpen && isOverlayOpen) { popStep(); return; } }; return ( Open {/* [!code highlight] */} {/* [!code highlight] */} 제목 설명 텍스트 setOpen(false)}>취소 { // [!code highlight] setOpen(false); // 다른 Activity로 이동하기 전에는 Alert Dialog를 닫으세요. push("ActivityNext"); }} > 다음 {/* [!code highlight] */} ); }; ``` 1. `Portal`을 사용하여 Alert Dialog가 DOM 상 현재 Activity 밖에 렌더링되도록 합니다. 2. `open` prop를 관리하고, `onOpenChange` 핸들러를 통해 Step 상태와 동기화합니다. 3. 뒤로 가기 버튼 등을 통해 Activity 파라미터가 변경될 때 Alert Dialog의 `open` 상태를 동기화합니다. 4. `layerIndex={useActivityZIndexBase({ activityOffset: 1 })}`로 현재 Activity보다 한 단계 높은 z-index 기준점을 전달합니다. - [`useActivityZIndexBase`에 대해 자세히 알아보기](#about-useactivityzindexbase) ### `useStepOverlay` \[#usestepoverlay] \#2와 #3을 일반화하여 `useStepOverlay`를 사용하면 편리합니다. `useStepOverlay` 구현 예시는 [코드](https://github.com/daangn/seed-design/blob/dev/examples/stackflow-spa/src/seed-design/stackflow/use-step-overlay.ts)를 참고하세요. ```tsx import { useActivityZIndexBase } from "@seed-design/stackflow"; import { Portal } from "@seed-design/react"; import { useStepOverlay } from "./use-step-overlay"; // ... more imports const MyActivity: ActivityComponentType = () => { // [!code highlight] const { overlayProps, setOpen } = useStepOverlay(); const { popStep } = useStepFlow("MyActivity"); const { push } = useFlow(); return ( {/* [!code highlight] */} Open {/* [!code highlight] */} {/* [!code highlight] */} 제목 설명 텍스트 setOpen(false)}>취소 { // [!code highlight] setOpen(false); // 다른 Activity로 이동하기 전에는 Alert Dialog를 닫으세요. push("ActivityNext"); }} > 다음 {/* [!code highlight] */} ); }; ``` ## About `useActivityZIndexBase` \[#about-useactivityzindexbase] `useActivityZIndexBase`는 각 Activity의 z-index 기준점을 반환하는 훅입니다. - `useActivity().zIndex * 5`를 반환합니다. `useActivity().zIndex`는 현재 Activity가 enter된 Activity 중 몇 번째 Activity인지를 나타내는 값입니다. - AppScreen을 구성하는 요소들(layer, appbar, dim, edge)은 `useActivityZIndexBase()` 값을 바탕으로 계산된 z-index를 갖습니다. ``` ┌─ 2번 AlertDialog Activity │ └─ AlertDialog: 12 (2 × 5 + 2) ├─ 1번 AppScreen Activity │ ├─ appbar: 12 (1 × 5 + 7) │ ├─ edge: 9 (1 × 5 + 4) │ ├─ layer: 7 (1 × 5 + 2) │ └─ dim: 5 (1 × 5 + 0) └─ 0번 AppScreen Activity ├─ appbar: 7 (0 × 5 + 7) ├─ edge: 4 (0 × 5 + 4) ├─ layer: 2 (0 × 5 + 2) └─ dim: 0 (0 × 5 + 0) ``` - `activityOffset` 옵션은 현재 Activity가 아닌 다음 Activity의 z-index 기준점을 사용하고 싶을 때 사용합니다. - 예를 들어, `useActivityZIndexBase({ activityOffset: 1 })`는 다음 Activity의 z-index 기준점을 반환합니다. - 이는 Step 패턴에서 현재 Activity 위에 AlertDialog를 표시할 때 유용합니다. ``` ┌─ 1번 AppScreen Activity에서 activityOffset: 1로 띄운 AlertDialog (예: Step 패턴) │ └─ AlertDialog: 12 ((1 + 1) × 5 + 2) ├─ 1번 AppScreen Activity │ ├─ appbar: 12 (1 × 5 + 7) │ ├─ edge: 9 (1 × 5 + 4) │ ├─ layer: 7 (1 × 5 + 2) │ └─ dim: 5 (1 × 5 + 0) └─ 0번 AppScreen Activity ├─ appbar: 7 (0 × 5 + 7) ├─ edge: 4 (0 × 5 + 4) ├─ layer: 2 (0 × 5 + 2) └─ dim: 0 (0 × 5 + 0) ``` - 두 훅 모두 현재 Activity가 enter된 Activity 중 \**몇 번째 Activity인지를 바탕으로 z-index 스타일링에 필요한 값을 반환하는 훅입니다.*\* - 두 훅이 반환하는 값은 같으나, [@stackflow/plugin-basic-ui](https://www.npmjs.com/package/@stackflow/plugin-basic-ui)가 아닌, SEED 컴포넌트를 사용하는 경우 SEED가 관리하는 `@seed-design/stackflow` 패키지의 `useActivityZIndexBase` 훅을 사용하는 것을 권장합니다. - `@stackflow/react-ui-core` 패키지의 `useZIndexBase`는 `activityOffset` 옵션을 제공하지 않습니다. - `@seed-design/stackflow@1.1.1`부터 `useActivityZIndexBase` 훅이 제공됩니다. `layerIndex`를 `useActivity().zIndex * 5 + n` 형태로 지정하고 있었다면 `useActivityZIndexBase() + n`으로 변경할 수 있습니다. 변경 과정에서 `+ n`이 필요한지 함께 검토해보세요. --- file: stackflow/app-screen.mdx # App Screen Stackflow 네비게이션에서 개별 화면을 구성하는 컴포넌트입니다. 모바일 앱과 같은 화면 전환 경험을 제공할 때 사용됩니다. ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { Flex } from "@seed-design/react"; import type { StaticActivityComponentType } from "@stackflow/react/future"; import { AppBar, AppBarCloseButton, AppBarIconButton, AppBarLeft, AppBarMain, AppBarRight, } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; declare module "@stackflow/config" { interface Register { ActivityAppScreenPreview: {}; } } const ActivityAppScreenPreview: StaticActivityComponentType<"ActivityAppScreenPreview"> = () => { return ( Preview Preview ); }; export default ActivityAppScreenPreview; ``` ## Installation \[#installation] - npm: npx @seed-design/cli@latest add ui:app-screen - pnpm: pnpm dlx @seed-design/cli@latest add ui:app-screen - yarn: yarn dlx @seed-design/cli@latest add ui:app-screen - bun: bun x @seed-design/cli@latest add ui:app-screen ## Usage \[#usage] ```tsx import { AppBar, AppBarBackButton, AppBarCloseButton, AppBarIconButton, AppBarLeft, AppBarMain, AppBarRight, } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; ``` ```tsx Title {/* actions */} {/* content */} ``` ## Props \[#props] ### App Screen \[#app-screen] #### `AppScreen` \[#appscreen] - `preventSwipeBack` - type: `boolean | undefined` - `tone` - type: `"transparent" | "layer" | undefined` - default: `"layer"` - `theme` - type: `"android" | "cupertino" | undefined` - default: `"cupertino"` - `transitionStyle` - type: `"slideFromRightIOS" | "fadeFromBottomAndroid" | "fadeIn" | undefined` - default: `"slideFromRightIOS"` - `layerOffsetTop` - type: `"none" | "safeArea" | "appBar" | undefined` - default: `"appBar"` - `layerOffsetBottom` - type: `"none" | "safeArea" | undefined` - default: `"none"` - `gradient` - type: `boolean | undefined` - default: `true` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `swipeBackDisplacementRatioThreshold` - type: `number | undefined` - default: `0.4` - description: The threshold to determine whether the swipe back is intentional, by displacement ratio. - `swipeBackVelocityThreshold` - type: `number | undefined` - default: `1` - description: The threshold to determine whether the swipe back is intentional, by velocity. - `onSwipeBackStart` - type: `(() => void) | undefined` - `onSwipeBackMove` - type: `((props: { displacement: number; displacementRatio: number; }) => void) | undefined` - `onSwipeBackEnd` - type: `((props: { swiped: boolean; }) => void) | undefined` #### `AppScreenContent` \[#appscreencontent] - `ptr` - type: `boolean | undefined` - `onPtrReady` - type: `(() => void) | undefined` - `onPtrRefresh` - type: `(() => Promise) | undefined` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ### App Bar \[#app-bar] #### `AppBar` \[#appbar] - `theme` - type: `"android" | "cupertino" | undefined` - default: `"cupertino"` - `transitionStyle` - type: `"slideFromRightIOS" | "fadeFromBottomAndroid" | "fadeIn" | undefined` - default: `"slideFromRightIOS"` - `tone` - type: `"transparent" | "layer" | undefined` - default: `"layer"` - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. - `bg` - type: `(string & {}) | ScopedColorBg | ScopedColorPalette | ScopedColorBanner | undefined` - description: Shorthand for \`background\`. - `background` - type: `(string & {}) | ScopedColorBg | ScopedColorPalette | ScopedColorBanner | undefined` #### `AppBarLeft` \[#appbarleft] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. #### `AppBarMain` \[#appbarmain] - `title` - type: `string | undefined` - description: The title of the app bar. If children is provided as ReactElement, this prop will be ignored. - `subtitle` - type: `string | undefined` - description: The subtitle of the app bar. If children is provided as ReactElement, this prop will be ignored. - `tone` - type: `"transparent" | "layer" | undefined` - default: `"layer"` - `layout` - type: `"titleOnly" | "withSubtitle" | undefined` - default: `"titleOnly"` - `theme` - type: `"cupertino" | "android" | undefined` - default: `"cupertino"` - `transitionStyle` - type: `"slideFromRightIOS" | "fadeFromBottomAndroid" | "fadeIn" | undefined` - default: `"slideFromRightIOS"` #### `AppBarRight` \[#appbarright] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. #### `AppBarIconButton`, `AppBarBackButton`, `AppBarCloseButton` \[#appbariconbutton-appbarbackbutton-appbarclosebutton] - `asChild` - type: `boolean | undefined` - default: `false` - description: Whether the element should be rendered as a child of a slot. ## Examples \[#examples] ### Tones \[#tones] AppScreen의 `tone` 속성을 `transparent`로 설정하여 투명한 배경을 사용할 수 있습니다. - `AppBar`의 배경이 투명해집니다. - 모바일 OS 상태바를 포함한 `AppScreen` 상단에 그라디언트가 표시됩니다. - `gradient` 속성을 `false`로 설정하여 숨길 수 있습니다. ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { Flex } from "@seed-design/react"; import type { StaticActivityComponentType } from "@stackflow/react/future"; import { AppBar, AppBarCloseButton, AppBarIconButton, AppBarLeft, AppBarMain, AppBarRight, } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; declare module "@stackflow/config" { interface Register { ActivityAppScreenTransparent: {}; } } const ActivityAppScreenTransparent: StaticActivityComponentType< "ActivityAppScreenTransparent" > = () => { return ( Preview Preview ); }; export default ActivityAppScreenTransparent; ``` #### With Intersection Observer \[#with-intersection-observer] Intersection Observer를 사용해 `AppBar`의 `tone` 속성을 동적으로 변경할 수 있습니다. ```tsx import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import { Flex } from "@seed-design/react"; import type { StaticActivityComponentType } from "@stackflow/react/future"; import { useEffect, useRef, useState } from "react"; import { AppBar, AppBarCloseButton, AppBarIconButton, AppBarLeft, AppBarMain, type AppBarProps, AppBarRight, } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; declare module "@stackflow/config" { interface Register { ActivityAppScreenIntersectionObserver: {}; } } const ActivityAppScreenIntersectionObserver: StaticActivityComponentType< "ActivityAppScreenIntersectionObserver" > = () => { const [tone, setTone] = useState("transparent"); const whiteImageRef = useRef(null); useEffect(() => { const observer = new IntersectionObserver( (entries) => { const entry = entries[0]; if (!entry.isIntersecting) { // 이미지 영역을 벗어나면 tone을 layer로 변경 setTone("layer"); } else { // 이미지 영역을 포함하면 tone을 transparent로 변경 setTone("transparent"); } }, { threshold: [0, 0.1, 0.5, 1], rootMargin: "0px", }, ); if (whiteImageRef.current) { observer.observe(whiteImageRef.current); } return () => { observer.disconnect(); }; }, []); return ( Preview 하얀 이미지 컨텐츠 영역 ); }; export default ActivityAppScreenIntersectionObserver; ``` ### Layer Offset Top \[#layer-offset-top] `layerOffsetTop` 속성을 사용해 `AppScreenContent`의 상단 오프셋을 조정할 수 있습니다. `tone="transparent"`와 `gradient`를 사용하는 경우, 일반적으로 `layerOffsetTop="none"`을 함께 설정하여 모바일 OS 상태바 영역까지 콘텐츠 영역을 확장합니다. 디스플레이 컷아웃 (notch) 등 safe area를 올바르게 처리하기 위해 `viewport-fit=cover`가 포함된 `viewport` 메타 태그를 사용하세요. ```html ```
    layerOffsetTop을 none으로 설정한 스크린샷 layerOffsetTop을 safeArea으로 설정한 스크린샷 layerOffsetTop을 appBar로 설정한 스크린샷
    ### Customizing App Bar \[#customizing-app-bar] `tone="layer"`인 경우 `AppBar`의 색상을 변경할 수 있습니다. `AppBarIconButton` 내에 `Icon` 컴포넌트를 사용하여 아이콘을 커스터마이징할 수 있습니다. 아이콘 컴포넌트에 대해 자세히 알아봅니다. ```tsx import { Flex, Icon } from "@seed-design/react"; import { IconBellFill } from "@karrotmarket/react-monochrome-icon"; import type { StaticActivityComponentType } from "@stackflow/react/future"; import { AppBar, AppBarIconButton, AppBarMain, AppBarRight } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; declare module "@stackflow/config" { interface Register { ActivityAppScreenAppBarCustomization: {}; } } const ActivityAppScreenAppBarCustomization: StaticActivityComponentType< "ActivityAppScreenAppBarCustomization" > = () => { return ( } color="palette.blue500" size="x5" /> Preview ); }; export default ActivityAppScreenAppBarCustomization; ``` #### Custom Elements in App Bar \[#custom-elements-in-app-bar] `AppBar` 내에 `AppBarLeft`, `AppBarMain`, `AppBarRight`에 대응되지 않는 커스텀 요소를 배치하는 경우, 다른 요소와 동일한 화면 전환 트랜지션을 적용하기 위해 `AppBarSlot` 컴포넌트를 사용합니다. `AppBarSlot`은 하위 요소에 `class`를 주입하여 트랜지션을 적용하며, 추가 마크업은 생성하지 않습니다. ```tsx title='ActivityAppBarSlot.tsx' import { useActivity, useFlow, type StaticActivityComponentType } from "@stackflow/react/future"; import { AppBar, AppBarBackButton, AppBarLeft, AppBarRight, AppBarIconButton, AppBarSlot, } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { IconBellLine } from "@karrotmarket/react-monochrome-icon"; import { Flex, Text, VStack } from "@seed-design/react"; import { ActionButton } from "seed-design/ui/action-button"; declare module "@stackflow/config" { interface Register { ActivityAppBarSlot: Record; } } function FakeSearchBar(props: React.HTMLAttributes) { return ( 검색어를 입력하세요 ); } const ActivityAppBarSlot: StaticActivityComponentType<"ActivityAppBarSlot"> = () => { const { isRoot } = useActivity(); const { push } = useFlow(); return ( {!isRoot && ( )} AppBar.Slot은 커스텀 요소에 stackflow 트랜지션 애니메이션을 적용합니다. 이 페이지에서 뒤로 swipe하면 검색바가 IconButton과 동일하게 fade 트랜지션됩니다. push("ActivityAppBarSlot", {})} variant="neutralSolid"> 이 액티비티 다시 열기 ); }; export default ActivityAppBarSlot; ``` ### Preventing Swipe Back \[#preventing-swipe-back] `preventSwipeBack` 속성을 사용해 `theme="cupertino"`인 AppScreen에서 edge 영역을 렌더링하지 않음으로써 스와이프 백 제스처를 방지할 수 있습니다. ### Transition Styles \[#transition-styles] `transitionStyle` 속성을 사용해 AppScreen이 최상위로 push되거나 최상위에서 pop될 때 재생할 트랜지션을 지정할 수 있습니다. 최상위: `useActivity().isTop === true`인 액티비티로 만들어진 AppScreen을 의미합니다. 별도로 지정하지 않는 경우, `transitionStyle`은 `theme`에 따른 기본값을 갖습니다. - `theme="cupertino"`: `slideFromRightIOS` - `theme="android"`: `fadeFromBottomAndroid` 최상위 AppScreen이 push/pop될 때, 최상위가 아닌 AppScreen도 함께 트랜지션을 재생합니다. **`@seed-design/stackflow@1.1.15`까지** 최상위 AppScreen이 push/pop될 때, 최상위가 아닌 AppScreen은 **각 AppScreen의 고유한 `transitionStyle`을 재생합니다**. - 예를 들면, `transitionStyle="fadeFromBottomAndroid"`인 0번 AppScreen 위에 `transitionStyle="slideFromLeftIOS"`인 1번 AppScreen이 push되는 경우, 0번 AppScreen은 `fadeFromBottomAndroid` 트랜지션을 재생합니다. - 0번 AppScreen이 위치 변화 없이 그대로 유지된 상태에서(`fadeFromBottomAndroid`) 1번 AppScreen이 우측에서 슬라이드 인(`slideFromLeftIOS`) **이후 버전** 최상위 AppScreen이 push/pop될 때, 최상위가 아닌 AppScreen은 **최상위 AppScreen의 `transitionStyle`을 재생합니다**. - 같은 스택 내에 여러 `transitionStyle`이 공존할 때 자연스러운 트랜지션을 제공합니다. - 예를 들면, `transitionStyle="fadeFromBottomAndroid"`인 0번 AppScreen 위에 `transitionStyle="slideFromLeftIOS"`인 1번 AppScreen이 push되는 경우, 0번 AppScreen은 `slideFromLeftIOS` 트랜지션을 재생합니다. - 0번 AppScreen이 자연스럽게 좌측으로 조금 밀려나며 어두워지고(`slideFromLeftIOS`) 1번 AppScreen이 우측에서 슬라이드 인(`slideFromLeftIOS`) ```tsx title='ActivityTransitionStyle.tsx' import { VStack, Text } from "@seed-design/react"; import { useFlow, type StaticActivityComponentType } from "@stackflow/react/future"; import { AppBar, AppBarBackButton, AppBarIconButton, AppBarLeft, AppBarMain, AppBarRight, } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent, type AppScreenProps } from "seed-design/ui/app-screen"; import { IconHouseLine } from "@karrotmarket/react-monochrome-icon"; import { ActionButton } from "seed-design/ui/action-button"; import { appScreenVariantMap } from "@seed-design/css/recipes/app-screen"; import { Snackbar, useSnackbarAdapter } from "seed-design/ui/snackbar"; import { SegmentedControl, SegmentedControlItem } from "seed-design/ui/segmented-control"; import { useState } from "react"; declare module "@stackflow/config" { interface Register { ActivityTransitionStyle: { transitionStyle: NonNullable; }; } } const ActivityTransitionStyle: StaticActivityComponentType<"ActivityTransitionStyle"> = ({ params: { transitionStyle }, }) => { const { push } = useFlow(); const { create } = useSnackbarAdapter(); const [preventSwipeBack, setPreventSwipeBack] = useState(false); return ( { create({ render: () => , timeout: 500 }); }} onSwipeBackEnd={({ swiped }) => { create({ render: () => , timeout: 500 }); }} > {/* can be undefined if search parameter isn't provided */} push("ActivityHome", {})}> {appScreenVariantMap.transitionStyle.map((style) => ( push("ActivityTransitionStyle", { transitionStyle: style })} > {style} ))} Prevent Swipe Back setPreventSwipeBack(value === "true")} aria-label="Prevent Swipe Back" > false true ); }; export default ActivityTransitionStyle; ``` --- file: stackflow/bottom-sheet.mdx # Bottom Sheet Bottom Sheet를 Stackflow와 함께 사용하는 방법을 안내합니다. Bottom Sheet 컴포넌트에 대해 자세히 알아봅니다. ## Making a Bottom Sheet Activity \[#making-a-bottom-sheet-activity] **일반적인 경우 Bottom Sheet를 [Activity](https://stackflow.so/docs/get-started/activity)로 만들어 사용하는 것을 권장합니다.** - Activity로 관리되므로, 하위 Activity보다 높고 상위 Activity보다는 낮은 z-index를 갖도록 관리하기 쉽습니다. - 딥링킹이 가능합니다. (URL 접속으로 Bottom Sheet를 열 수 있습니다.) - [@stackflow/plugin-basic-ui](https://www.npmjs.com/package/@stackflow/plugin-basic-ui) `BottomSheet`에서의 마이그레이션이 쉽습니다. ```tsx title='ActivityBottomSheetActivity.tsx' import { VStack } from "@seed-design/react"; import { useFlow, type StaticActivityComponentType } from "@stackflow/react/future"; import { AppBar, AppBarBackButton, AppBarLeft, AppBarMain, AppBarIconButton, AppBarRight, } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { ActionButton } from "seed-design/ui/action-button"; import { IconHouseLine } from "@karrotmarket/react-monochrome-icon"; declare module "@stackflow/config" { interface Register { ActivityBottomSheetActivity: {}; } } const ActivityBottomSheetActivity: StaticActivityComponentType< "ActivityBottomSheetActivity" > = () => { const { push } = useFlow(); return ( push("ActivityHome", {})}> push("ActivityBottomSheet", {})} > ActivityBottomSheet을 Push push("ActivityBottomSheetActivity", {})} > 지금 열린 이 Activity를 Push ); }; export default ActivityBottomSheetActivity; ``` ```tsx title='ActivityBottomSheet.tsx' import { Box, Divider, HStack, VStack } from "@seed-design/react"; import { useActivity, useFlow, type StaticActivityComponentType } from "@stackflow/react/future"; import { useRef, useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, } from "seed-design/ui/bottom-sheet"; import { Checkbox } from "seed-design/ui/checkbox"; import { Snackbar, useSnackbarAdapter } from "seed-design/ui/snackbar"; import { TextField, TextFieldInput } from "seed-design/ui/text-field"; import { useActivityZIndexBase } from "@seed-design/stackflow"; import { Switch } from "seed-design/ui/switch"; import { SegmentedControl, SegmentedControlItem } from "seed-design/ui/segmented-control"; import { appScreenVariantMap, type AppScreenVariant } from "@seed-design/css/recipes/app-screen"; declare module "@stackflow/config" { interface Register { ActivityBottomSheet: {}; } } const ActivityBottomSheet: StaticActivityComponentType<"ActivityBottomSheet"> = () => { const { push, pop } = useFlow(); const activity = useActivity(); const form = useRef(null); const snackbar = useSnackbarAdapter(); const [nameError, setNameError] = useState(null); const [keepMounted, setKeepMounted] = useState(false); const [transitionStyle, setTransitionStyle] = useState("slideFromRightIOS"); const handleSubmit = () => { if (!form.current) return; const formData = new FormData(form.current); if (!formData.get("name")) { setNameError("이름을 입력해주세요."); return; } setNameError(null); pop(); snackbar.create({ render: () => ( ), }); }; const open = keepMounted ? activity.transitionState === "enter-active" || activity.transitionState === "enter-done" : activity.isActive; const onOpenChange = keepMounted ? (open: boolean) => !open && activity.isActive && pop() : (open: boolean) => !open && pop(); return (
    { e.preventDefault(); handleSubmit(); }} > 닫기 제출 setTransitionStyle(style as AppScreenVariant["transitionStyle"]) } > {appScreenVariantMap.transitionStyle.map((style) => ( {style} ))} push("ActivityDetail", { title: "ActivityDetail", body: keepMounted ? "BottomSheet가 언마운트되지 않았으므로, 현재 Activity를 pop하는 경우 uncontrolled 상태의 TextField와 Checkbox 값이 유지되며 BottomSheet가 열린 상태로 표시됩니다." : "BottomSheet가 언마운트되었으므로, 현재 Activity를 pop하는 경우 uncontrolled 상태의 TextField와 Checkbox 값이 초기화되며 BottomSheet가 다시 enter 트랜지션을 재생하며 마운트됩니다.", transitionStyle, }) } > ActivityDetail push("ActivityHome", { transitionStyle })} > ActivityHome
    ); }; export default ActivityBottomSheet; ```
    ### Usage \[#usage] ```tsx import { useActivityZIndexBase } from "@seed-design/stackflow"; import { useActivity, useFlow, type ActivityComponentType } from "@stackflow/react/future"; // ... more imports const ActivityBottomSheetSimple: ActivityComponentType<"ActivityBottomSheetSimple"> = () => { const { pop } = useFlow(); return ( {/* [!code highlight] */} !open && pop()}> 확인 ); }; ``` 1. `open` prop에 `useActivity().isActive`를 전달하여 Activity가 활성화될 때 Bottom Sheet가 열리도록 합니다. 2. `onOpenChange`를 통해 Bottom Sheet가 닫힐 때 `pop()`을 실행하여 Activity를 종료합니다. 3. `layerIndex={useActivityZIndexBase()}`로 Bottom Sheet Activity의 z-index 기준점을 전달합니다. - [`useActivityZIndexBase`에 대해 자세히 알아보기](#about-useactivityzindexbase) #### Keeping Bottom Sheet Mounted \[#keeping-bottom-sheet-mounted] Bottom Sheet Activity 위에 다른 Activity를 push할 때 Bottom Sheet가 unmount되는 것을 방지하려면, - `open` 상태를 `isActive` 대신 `transitionState`로 관리하고 - `modal` prop을 `isActive`로 설정하고 - `onOpenChange` 핸들러에서 `!isOpen && isActive`인 경우 `pop()`을 실행하도록 합니다. 이 패턴은 다음 상황에서 유용합니다. - Bottom Sheet 액티비티 위에 다른 오버레이 컴포넌트 액티비티를 중첩하여 표시하고 싶은 경우 - 하위 Bottom Sheet 액티비티 내부에 존재하는 Uncontrolled 폼 요소의 상태를 유지하고 싶은 경우 ```tsx const { isActive, transitionState } = useActivity(); return ( !open && isActive && pop()} > {/* ... */} ); ``` 1. `open`을 `transitionState`로 관리하여 다른 Activity가 위에 push되어도 Bottom Sheet가 unmount되지 않도록 합니다. 2. `modal={isActive}`로 Bottom Sheet Activity가 비활성 상태일 때 `modal`을 `false`로 설정합니다. 이렇게 하지 않으면, 위에 push된 Activity에서 포커스, 스크린 리더 접근 및 스크롤 등의 상호작용이 동작하지 않습니다. 3. `onOpenChange` 핸들러에서 `isActive`인 경우에만 `pop()`을 실행하여, 비활성 상태에서의 의도치 않은 Activity 종료를 방지합니다. #### Stacking Another Modal Overlay on Top \[#stacking-another-modal-overlay-on-top] Bottom Sheet Activity 위에 자체 backdrop을 갖는 또 다른 modal Activity(예: Alert Dialog Activity)를 push해 얹는 경우에는, `modal={isActive}` 대신 `modal`을 항상 `true`로 두는 것을 권장합니다. `modal={isActive}` 패턴은 위에 push되는 Activity가 일반 AppScreen일 때 스크롤 등의 상호작용을 보장하기 위한 것이지만, 위로 push되는 Activity가 자체 backdrop을 갖는 modal overlay라면 비활성 시점에 Bottom Sheet의 backdrop이 사라지면서 dim layering이 끊겨 보일 수 있습니다. ```tsx title='ActivityNestedBottomSheet.tsx' import { VStack } from "@seed-design/react"; import { useActivityZIndexBase } from "@seed-design/stackflow"; import { useActivity, useFlow, type StaticActivityComponentType } from "@stackflow/react/future"; import { ActionButton } from "seed-design/ui/action-button"; import { BottomSheetBody, BottomSheetContent, BottomSheetFooter, BottomSheetRoot, } from "seed-design/ui/bottom-sheet"; import { Callout } from "seed-design/ui/callout"; declare module "@stackflow/config" { interface Register { ActivityNestedBottomSheet: {}; } } const ActivityNestedBottomSheet: StaticActivityComponentType<"ActivityNestedBottomSheet"> = () => { const { pop, push } = useFlow(); const { isActive, transitionState } = useActivity(); const open = transitionState === "enter-active" || transitionState === "enter-done"; const onOpenChange = (next: boolean) => !next && isActive && pop(); return ( // 위에 AlertDialog Activity가 push되어 BottomSheet가 비활성(isActive=false)이 되더라도 // BottomSheet 자체의 modal 동작과 backdrop을 유지해 dim layering이 끊기지 않도록 modal을 항상 true로 둔다. // outside close는 onOpenChange에서 isActive 가드로 막혀 비활성 상태에서는 pop이 발생하지 않는다. push("ActivityAlertDialog", {})} > Open AlertDialog 닫기 ); }; export default ActivityNestedBottomSheet; ``` ```tsx const { isActive, transitionState } = useActivity(); return ( !open && isActive && pop()} > {/* ... */} ); ``` 1. `modal`을 항상 `true`로 두어 위에 또 다른 modal Activity가 push되어도 Bottom Sheet의 backdrop layering이 끊기지 않도록 합니다. 2. `onOpenChange`의 `isActive` 가드 덕분에 비활성 상태에서의 outside 상호작용으로 의도치 않게 `pop()`이 발생하지 않습니다. 위에 push되는 Activity가 Bottom Sheet 영역까지 덮는 backdrop을 갖지 않는 일반 AppScreen이라면, 위 Activity의 스크롤 등 상호작용을 위해 `modal={isActive}` 가이드가 적합합니다. modal overlay 두 개가 stack되는 경우에 한해 `modal`을 항상 `true`로 두는 변형을 사용하세요. ## Syncing BottomSheet State with a Step \[#syncing-bottomsheet-state-with-a-step] Bottom Sheet를 Activity로 만들 수 없는 경우, Bottom Sheet가 표시된 상태를 [Step](https://stackflow.so/docs/get-started/navigating-step)으로 만들 수 있습니다. - 현재 Activity를 유지하면서도, 뒤로 가기 버튼 등으로 Bottom Sheet를 닫을 수 있습니다. - `BottomSheetTrigger`를 사용하여 Bottom Sheet를 열고 닫을 수 있습니다. **Activity로 만들지 않은 Bottom Sheet에서 다른 Activity를 push하기 전, z-index 문제를 방지하기 위해 Bottom Sheet를 닫으세요.** Bottom Sheet를 닫을 수 없거나, Bottom Sheet를 연 Activity로 돌아왔을 때 Bottom Sheet가 열린 상태를 유지해야 하는 경우 [Bottom Sheet를 Activity로](#making-a-bottom-sheet-activity) 만들어 사용하는 것을 권장합니다. Activity 간 유려한 트랜지션을 제공하기 위해 하위 AppScreen 요소 중 일부가 상위 AppScreen 요소보다 위에 위치합니다. 이 제약으로 인해, 열린 상태의 Bottom Sheet는 독립적인 Activity로 만들지 않는 경우 하위 Activity와 상위 Activity 사이에 위치시키는 것이 불가능합니다. ```tsx title='ActivityBottomSheetStep.tsx' import { HStack, Portal, VStack } from "@seed-design/react"; import { useActivityZIndexBase } from "@seed-design/stackflow"; import { useActivityParams, useFlow, useStepFlow, type StaticActivityComponentType, } from "@stackflow/react/future"; import { useEffect, useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { AppBar, AppBarMain, AppBarIconButton, AppBarRight } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { IconHouseLine } from "@karrotmarket/react-monochrome-icon"; import { BottomSheetContent, BottomSheetFooter, BottomSheetRoot, BottomSheetTrigger, } from "seed-design/ui/bottom-sheet"; declare module "@stackflow/config" { interface Register { ActivityBottomSheetStep: { "bottom-sheet"?: "open"; }; } } const ActivityBottomSheetStep: StaticActivityComponentType<"ActivityBottomSheetStep"> = () => { const [open, setOpen] = useState(false); const { push } = useFlow(); const { pushStep, popStep } = useStepFlow("ActivityBottomSheetStep"); const params = useActivityParams<"ActivityBottomSheetStep">(); const isOverlayOpen = params["bottom-sheet"] === "open"; useEffect(() => setOpen(isOverlayOpen), [isOverlayOpen]); const onOpenChange = (newOpen: boolean) => { setOpen(newOpen); if (newOpen && !isOverlayOpen) { pushStep((params) => ({ ...params, "bottom-sheet": "open" })); return; } if (!newOpen && isOverlayOpen) { popStep(); return; } }; return ( push("ActivityHome", {})}> Bottom Sheet 열기 setOpen(false)} variant="neutralWeak"> 닫기 { // 이 Bottom Sheet는 Activity로 만들어지지 않았기 때문에, z-index 정리를 위해 // BottomSheet를 먼저 닫고 다음 Activity를 push해야 합니다. setOpen(false); push("ActivityDetail", { title: "Bottom Sheet에서 이동한 화면", body: "Bottom Sheet를 닫고 이동했습니다.", }); }} > Push ); }; export default ActivityBottomSheetStep; ``` ### Usage \[#usage-1] ```tsx import { useActivityZIndexBase } from "@seed-design/stackflow"; import { Portal } from "@seed-design/react"; import { useActivity, useActivityParams, useFlow, useStepFlow, type ActivityComponentType, } from "@stackflow/react/future"; import { useEffect, useState } from "react"; // ... more imports declare module "@stackflow/config" { interface Register { ActivityHome: { "bottom-sheet"?: "open"; }; } } const ActivityHome: ActivityComponentType<"ActivityHome"> = () => { const [open, setOpen] = useState(false); const { push } = useFlow(); const { pushStep, popStep } = useStepFlow("ActivityHome"); const params = useActivityParams<"ActivityHome">(); // [!code highlight] const isOverlayOpen = params["bottom-sheet"] === "open"; // [!code highlight] useEffect(() => { if (!isOverlayOpen) { setOpen(false); } }, [isOverlayOpen]); // [!code highlight] const onOpenChange = (newOpen: boolean) => { setOpen(newOpen); if (newOpen && !isOverlayOpen) { pushStep((params) => ({ ...params, "bottom-sheet": "open" })); return; } if (!newOpen && isOverlayOpen) { popStep(); return; } }; return ( Open {/* [!code highlight] */} setOpen(false)}>취소 { // [!code highlight] setOpen(false); // 다른 Activity로 이동하기 전에는 Bottom Sheet를 닫으세요. push("ActivityNext"); }} > 다음 {/* [!code highlight] */} ); }; ``` 1. `Portal`을 사용하여 Bottom Sheet가 DOM 상 현재 Activity 밖에 렌더링되도록 합니다. 2. `open` prop를 관리하고, `onOpenChange` 핸들러를 통해 Step 상태와 동기화합니다. 3. 뒤로 가기 버튼 등을 통해 Activity 파라미터가 변경될 때 Bottom Sheet의 `open` 상태를 동기화합니다. 4. `layerIndex={useActivityZIndexBase({ activityOffset: 1 })}`로 현재 Activity보다 한 단계 높은 z-index 기준점을 전달합니다. - [`useActivityZIndexBase`에 대해 자세히 알아보기](#about-useactivityzindexbase) ### `useStepOverlay` \[#usestepoverlay] \#2와 #3을 일반화하여 `useStepOverlay`를 사용하면 편리합니다. `useStepOverlay` 구현 예시는 [코드](https://github.com/daangn/seed-design/blob/dev/examples/stackflow-spa/src/seed-design/stackflow/use-step-overlay.ts)를 참고하세요. ```tsx import { useActivityZIndexBase } from "@seed-design/stackflow"; import { Portal } from "@seed-design/react"; import { useStepOverlay } from "./use-step-overlay"; // ... more imports const MyActivity: ActivityComponentType = () => { // [!code highlight] const { overlayProps, setOpen } = useStepOverlay(); const { popStep } = useStepFlow("MyActivity"); const { push } = useFlow(); return ( {/* [!code highlight] */} Open {/* [!code highlight] */} setOpen(false)}>취소 { // [!code highlight] setOpen(false); // 다른 Activity로 이동하기 전에는 Bottom Sheet를 닫으세요. push("ActivityNext"); }} > 다음 {/* [!code highlight] */} ); }; ``` ## About `useActivityZIndexBase` \[#about-useactivityzindexbase] `useActivityZIndexBase`는 각 Activity의 z-index 기준점을 반환하는 훅입니다. - `useActivity().zIndex * 5`를 반환합니다. `useActivity().zIndex`는 현재 Activity가 enter된 Activity 중 몇 번째 Activity인지를 나타내는 값입니다. - AppScreen을 구성하는 요소들(layer, appbar, dim, edge)은 `useActivityZIndexBase()` 값을 바탕으로 계산된 z-index를 갖습니다. ``` ┌─ 2번 BottomSheet Activity │ └─ BottomSheet: 12 (2 × 5 + 2) ├─ 1번 AppScreen Activity │ ├─ appbar: 12 (1 × 5 + 7) │ ├─ edge: 9 (1 × 5 + 4) │ ├─ layer: 7 (1 × 5 + 2) │ └─ dim: 5 (1 × 5 + 0) └─ 0번 AppScreen Activity ├─ appbar: 7 (0 × 5 + 7) ├─ edge: 4 (0 × 5 + 4) ├─ layer: 2 (0 × 5 + 2) └─ dim: 0 (0 × 5 + 0) ``` - `activityOffset` 옵션은 현재 Activity가 아닌 다음 Activity의 z-index 기준점을 사용하고 싶을 때 사용합니다. - 예를 들어, `useActivityZIndexBase({ activityOffset: 1 })`는 다음 Activity의 z-index 기준점을 반환합니다. - 이는 Step 패턴에서 현재 Activity 위에 BottomSheet를 표시할 때 유용합니다. ``` ┌─ 1번 AppScreen Activity에서 activityOffset: 1로 띄운 BottomSheet (예: Step 패턴) │ └─ BottomSheet: 12 ((1 + 1) × 5 + 2) ├─ 1번 AppScreen Activity │ ├─ appbar: 12 (1 × 5 + 7) │ ├─ edge: 9 (1 × 5 + 4) │ ├─ layer: 7 (1 × 5 + 2) │ └─ dim: 5 (1 × 5 + 0) └─ 0번 AppScreen Activity ├─ appbar: 7 (0 × 5 + 7) ├─ edge: 4 (0 × 5 + 4) ├─ layer: 2 (0 × 5 + 2) └─ dim: 0 (0 × 5 + 0) ``` - 두 훅 모두 현재 Activity가 enter된 Activity 중 \**몇 번째 Activity인지를 바탕으로 z-index 스타일링에 필요한 값을 반환하는 훅입니다.*\* - 두 훅이 반환하는 값은 같으나, [@stackflow/plugin-basic-ui](https://www.npmjs.com/package/@stackflow/plugin-basic-ui)가 아닌, SEED 컴포넌트를 사용하는 경우 SEED가 관리하는 `@seed-design/stackflow` 패키지의 `useActivityZIndexBase` 훅을 사용하는 것을 권장합니다. - `@stackflow/react-ui-core` 패키지의 `useActivityZIndexBase`는 `activityOffset` 옵션을 제공하지 않습니다. - `@seed-design/stackflow@1.1.1`부터 `useActivityZIndexBase` 훅이 제공됩니다. `layerIndex`를 `useActivity().zIndex * 5 + n` 형태로 지정하고 있었다면 `useActivityZIndexBase() + n`으로 변경할 수 있습니다. 변경 과정에서 `+ n`이 필요한지 함께 검토해보세요. --- file: stackflow/getting-started.mdx # Getting Started SEED AppScreen을 사용하기 위한 Stackflow 플러그인 설정 방법을 안내합니다. ## Overview \[#overview] SEED React의 [`AppScreen`](/react/stackflow/app-screen) 컴포넌트를 사용하려면 [Stackflow](https://stackflow.so/) 플러그인에 `seedPlugin`을 추가합니다. AppScreen 컴포넌트에 대해 자세히 알아봅니다. `seedPlugin`은 다음 기능을 제공합니다. - **테마 컨텍스트 제공**: 모든 `AppScreen` 컴포넌트에 `theme`을 자동으로 전달합니다. - **전역(Global) 인터랙션 관리**: Swipe Back (쓸어서 뒤로 가기) 제스처 및 전역 트랜지션 상태를 관리합니다. ## Installation \[#installation] `seedPlugin`은 `@seed-design/stackflow` 패키지에 포함되어 있습니다. - npm: npm install @seed-design/stackflow - pnpm: pnpm add @seed-design/stackflow - yarn: yarn add @seed-design/stackflow - bun: bun add @seed-design/stackflow ## Usage \[#usage] Stackflow 설정 시 `plugins` 배열에 `seedPlugin`을 추가합니다. ```tsx import { stackflow } from "@stackflow/react/future"; import { seedPlugin } from "@seed-design/stackflow"; export const { Stack, actions } = stackflow({ plugins: [ seedPlugin({ theme: "cupertino" }), // ... ], // ... }); ``` ### Options \[#options] - `theme` - type: `"android" | "cupertino"` - required: `true` ### Dynamic Options \[#dynamic-options] SSR 환경 등에서 theme을 동적으로 결정해야 하는 경우, Stack에 제공한 `initialContext`를 통해 `seedPlugin` 옵션을 설정할 수 있습니다. ```tsx title="stackflow.ts" import { stackflow } from "@stackflow/react/future"; import { seedPlugin } from "@seed-design/stackflow"; export const { Stack, actions } = stackflow({ plugins: [ seedPlugin(({ initialContext }) => ({ theme: initialContext?.theme ?? "cupertino", })), // ... ], // ... }); ``` `` 렌더링 시 `initialContext`로 theme을 전달합니다. ```tsx title="App.tsx" import type { SeedPluginOptions } from "@seed-design/stackflow"; ; ``` ## What It Provides \[#what-it-provides] ### Theme Context \[#theme-context] `seedPlugin`은 `theme` 옵션을 통해 설정된 테마를 모든 `AppScreen` 컴포넌트에 전달합니다. 모든 `AppScreen`은 기본적으로 이 테마를 사용합니다. `theme`은 [@stackflow/plugin-basic-ui](https://stackflow.so/api-references/plugins/plugin-basic-ui)와 동일하게 `"android" | "cupertino"` 중 선택할 수 있습니다. ```tsx // seedPlugin에 옵션으로 제공한 theme이 `AppScreen`에 기본적으로 적용됩니다. // `AppScreen`의 `theme` prop을 통해 오버라이드할 수 있습니다. {/* ... */} {/* ... */} ``` ### Swipe Back Gesture \[#swipe-back-gesture] `theme="cupertino"`를 사용하는 경우, iOS 스타일의 Swipe Back (쓸어서 뒤로 가기) 제스처가 활성화됩니다. - 화면 왼쪽 가장자리에서 오른쪽으로 스와이프하면 이전 Activity로 이동합니다. - 스와이프 정도에 따라 `AppScreen`이 위치를 업데이트합니다. --- file: stackflow/menu-sheet.mdx # Menu Sheet Menu Sheet를 Stackflow와 함께 사용하는 방법을 안내합니다. Menu Sheet 컴포넌트에 대해 자세히 알아봅니다. ## Making a Menu Sheet Activity \[#making-a-menu-sheet-activity] **일반적인 경우 Menu Sheet를 [Activity](https://stackflow.so/docs/get-started/activity)로 만들어 사용하는 것을 권장합니다.** - Activity로 관리되므로, 하위 Activity보다 높고 상위 Activity보다는 낮은 z-index를 갖도록 관리하기 쉽습니다. - 딥링킹이 가능합니다. (URL 접속으로 Menu Sheet를 열 수 있습니다.) - [@stackflow/plugin-basic-ui](https://www.npmjs.com/package/@stackflow/plugin-basic-ui) `BottomSheet`에서의 마이그레이션이 쉽습니다. ```tsx title='ActivityMenuSheetActivity.tsx' import { VStack } from "@seed-design/react"; import { useFlow, type StaticActivityComponentType } from "@stackflow/react/future"; import { IconHouseLine } from "@karrotmarket/react-monochrome-icon"; import { AppBar, AppBarBackButton, AppBarIconButton, AppBarLeft, AppBarMain, AppBarRight, } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { ActionButton } from "seed-design/ui/action-button"; declare module "@stackflow/config" { interface Register { ActivityMenuSheetActivity: {}; } } const ActivityMenuSheetActivity: StaticActivityComponentType<"ActivityMenuSheetActivity"> = () => { const { push } = useFlow(); return ( push("ActivityHome", {})}> push("ActivityMenuSheetSimple", {})} > ActivityMenuSheetSimple을 Push push("ActivityMenuSheetActivity", {})} > 지금 열린 이 Activity를 Push ); }; export default ActivityMenuSheetActivity; ``` ```tsx title='ActivityMenuSheetSimple.tsx' import { Flex, VStack } from "@seed-design/react"; import { useActivityZIndexBase } from "@seed-design/stackflow"; import { useActivity, useFlow, type StaticActivityComponentType } from "@stackflow/react/future"; import { useState } from "react"; import { IconPencilLine, IconPlusLine, IconTrashcanLine, } from "@karrotmarket/react-monochrome-icon"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, } from "seed-design/ui/menu-sheet"; import { Snackbar, useSnackbarAdapter } from "seed-design/ui/snackbar"; import { Switch } from "seed-design/ui/switch"; declare module "@stackflow/config" { interface Register { ActivityMenuSheetSimple: {}; } } const ActivityMenuSheetSimple: StaticActivityComponentType<"ActivityMenuSheetSimple"> = () => { const { pop, push } = useFlow(); const activity = useActivity(); const snackbar = useSnackbarAdapter(); const [keepMounted, setKeepMounted] = useState(false); const handleAction = (action: string) => { snackbar.create({ render: () => , }); pop(); }; const open = keepMounted ? activity.transitionState === "enter-active" || activity.transitionState === "enter-done" : activity.isActive; const onOpenChange = keepMounted ? (open: boolean) => !open && activity.isActive && pop() : (open: boolean) => !open && pop(); return ( handleAction("add")} label="추가" prefixIcon={} /> handleAction("edit")} label="수정" prefixIcon={} /> handleAction("delete")} tone="critical" label="삭제" prefixIcon={} /> push("ActivityDetail", { title: "Activity", body: keepMounted ? "MenuSheet가 언마운트되지 않았으므로, 현재 Activity를 pop하는 경우 MenuSheet가 열린 상태로 표시됩니다." : "MenuSheet가 언마운트되었으므로, 현재 Activity를 pop하는 경우 MenuSheet가 다시 enter 트랜지션을 재생하며 마운트됩니다.", }) } label="Push" /> ); }; export default ActivityMenuSheetSimple; ``` ### Usage \[#usage] ```tsx import { useActivityZIndexBase } from "@seed-design/stackflow"; import { useActivity, useFlow, type ActivityComponentType } from "@stackflow/react/future"; // ... more imports const ActivityMenuSheet: ActivityComponentType<"ActivityMenuSheet"> = () => { const { pop } = useFlow(); const handleAction = (action: string) => { console.log("선택한 액션:", action); pop(); }; return ( {/* [!code highlight] */} !open && pop()}> handleAction("add")} label="추가" prefixIcon={} /> handleAction("edit")} label="수정" prefixIcon={} /> handleAction("delete")} tone="critical" label="삭제" prefixIcon={} /> ); }; ``` 1. `open` prop에 `useActivity().isActive`를 전달하여 Activity가 활성화될 때 Menu Sheet가 열리도록 합니다. 2. `onOpenChange`를 통해 Menu Sheet가 닫힐 때 `pop()`을 실행하여 Activity를 종료합니다. 3. `layerIndex={useActivityZIndexBase()}`로 Menu Sheet Activity의 z-index 기준점을 전달합니다. - [`useActivityZIndexBase`에 대해 자세히 알아보기](#about-useactivityzindexbase) #### Keeping Menu Sheet Mounted \[#keeping-menu-sheet-mounted] Menu Sheet Activity 위에 다른 Activity를 push할 때 Menu Sheet가 unmount되는 것을 방지하려면, - `open` 상태를 `isActive` 대신 `transitionState`로 관리하고 - `modal` prop을 `isActive`로 설정하고 - `onOpenChange` 핸들러에서 `!isOpen && isActive`인 경우 `pop()`을 실행하도록 합니다. 이 패턴은 Menu Sheet 액티비티 위에 다른 오버레이 컴포넌트 액티비티를 중첩하여 표시하고 싶은 경우 유용합니다. ```tsx const { isActive, transitionState } = useActivity(); return ( !open && isActive && pop()} > {/* ... */} ); ``` 1. `open`을 `transitionState`로 관리하여 다른 Activity가 위에 push되어도 Menu Sheet가 unmount되지 않도록 합니다. 2. `modal={isActive}`로 Menu Sheet Activity가 비활성 상태일 때 `modal`을 `false`로 설정합니다. 이렇게 하지 않으면, 위에 push된 Activity에서 포커스 및 스크린 리더 접근이 동작하지 않습니다. 3. `onOpenChange` 핸들러에서 `isActive`인 경우에만 `pop()`을 실행하여, 비활성 상태에서의 의도치 않은 Activity 종료를 방지합니다. ## Syncing Menu Sheet State with a Step \[#syncing-menu-sheet-state-with-a-step] Menu Sheet를 Activity로 만들 수 없는 경우, Menu Sheet가 표시된 상태를 [Step](https://stackflow.so/docs/get-started/navigating-step)으로 만들 수 있습니다. - 현재 Activity를 유지하면서도, 뒤로 가기 버튼 등으로 Menu Sheet를 닫을 수 있습니다. - `MenuSheetTrigger`를 사용하여 Menu Sheet를 열고 닫을 수 있습니다. **Activity로 만들지 않은 Menu Sheet에서 다른 Activity를 push하는 경우, push하기 전 Menu Sheet를 닫으세요.** Menu Sheet를 닫을 수 없거나, Menu Sheet를 연 Activity로 돌아왔을 때 Menu Sheet가 열린 상태를 유지해야 하는 경우 [Menu Sheet를 Activity로](#making-a-menu-sheet-activity) 만들어 사용하는 것을 권장합니다. Activity 간 유려한 트랜지션을 제공하기 위해 하위 AppScreen 요소 중 일부가 상위 AppScreen 요소보다 위에 위치합니다. 이 제약으로 인해, 열린 상태의 Menu Sheet는 독립적인 Activity로 만들지 않는 경우 하위 Activity와 상위 Activity 사이에 위치시키는 것이 불가능합니다. ```tsx title='ActivityMenuSheetStep.tsx' import { Portal, VStack } from "@seed-design/react"; import { useActivityZIndexBase } from "@seed-design/stackflow"; import { useActivityParams, useFlow, useStepFlow, type StaticActivityComponentType, } from "@stackflow/react/future"; import { useEffect, useState } from "react"; import { ActionButton } from "seed-design/ui/action-button"; import { AppBar, AppBarIconButton, AppBarMain, AppBarRight } from "seed-design/ui/app-bar"; import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen"; import { MenuSheetContent, MenuSheetGroup, MenuSheetItem, MenuSheetRoot, MenuSheetTrigger, } from "seed-design/ui/menu-sheet"; import { IconHouseLine, IconPencilLine, IconPlusLine, IconTrashcanLine, } from "@karrotmarket/react-monochrome-icon"; import { Snackbar, useSnackbarAdapter } from "seed-design/ui/snackbar"; declare module "@stackflow/config" { interface Register { ActivityMenuSheetStep: { "menu-sheet"?: "open"; }; } } const ActivityMenuSheetStep: StaticActivityComponentType<"ActivityMenuSheetStep"> = () => { const [open, setOpen] = useState(false); const { push } = useFlow(); const { pushStep, popStep } = useStepFlow("ActivityMenuSheetStep"); const params = useActivityParams<"ActivityMenuSheetStep">(); const isOverlayOpen = params["menu-sheet"] === "open"; const snackbar = useSnackbarAdapter(); useEffect(() => setOpen(isOverlayOpen), [isOverlayOpen]); const onOpenChange = (newOpen: boolean) => { setOpen(newOpen); if (newOpen && !isOverlayOpen) { pushStep((params) => ({ ...params, "menu-sheet": "open" })); return; } if (!newOpen && isOverlayOpen) { popStep(); return; } }; const handleAction = (action: string) => { snackbar.create({ render: () => , }); setOpen(false); }; return ( push("ActivityHome", {})}> Menu Sheet 열기 handleAction("add")} label="추가" prefixIcon={} /> handleAction("edit")} label="수정" prefixIcon={} /> handleAction("delete")} tone="critical" label="삭제" prefixIcon={} /> { // 이 Menu Sheet는 Activity로 만들어지지 않았기 때문에, z-index 정리를 위해 // Menu Sheet를 먼저 닫고 다음 Activity를 push해야 합니다. setOpen(false); push("ActivityDetail", { title: "Menu Sheet에서 이동한 화면", body: "Menu Sheet를 닫고 이동했습니다.", }); }} label="Push" /> ); }; export default ActivityMenuSheetStep; ``` ### Usage \[#usage-1] ```tsx import { useActivityZIndexBase } from "@seed-design/stackflow"; import { Portal } from "@seed-design/react"; import { useActivity, useActivityParams, useFlow, useStepFlow, type ActivityComponentType, } from "@stackflow/react/future"; import { useEffect, useState } from "react"; // ... more imports declare module "@stackflow/config" { interface Register { ActivityHome: { "menu-sheet"?: "open"; }; } } const ActivityHome: ActivityComponentType<"ActivityHome"> = () => { const [open, setOpen] = useState(false); const { push } = useFlow(); const { pushStep, popStep } = useStepFlow("ActivityHome"); const params = useActivityParams<"ActivityHome">(); // [!code highlight] const isOverlayOpen = params["menu-sheet"] === "open"; // [!code highlight] useEffect(() => { if (!isOverlayOpen) { setOpen(false); } }, [isOverlayOpen]); // [!code highlight] const onOpenChange = (newOpen: boolean) => { setOpen(newOpen); if (newOpen && !isOverlayOpen) { pushStep((params) => ({ ...params, "menu-sheet": "open" })); return; } if (!newOpen && isOverlayOpen) { popStep(); return; } }; const handleAction = (action: string) => { console.log("선택한 액션:", action); setOpen(false); }; return ( Open {/* [!code highlight] */} handleAction("add")} label="추가" /> handleAction("edit")} label="수정" /> { // [!code highlight] setOpen(false); // 다른 Activity로 이동하기 전에는 Menu Sheet를 닫으세요. push("ActivityNext"); }} label="Push" /> handleAction("delete")} tone="critical" label="삭제" /> {/* [!code highlight] */} ); }; ``` 1. `Portal`을 사용하여 Menu Sheet가 DOM 상 현재 Activity 밖에 렌더링되도록 합니다. 2. `open` prop를 관리하고, `onOpenChange` 핸들러를 통해 Step 상태와 동기화합니다. 3. 뒤로 가기 버튼 등을 통해 Activity 파라미터가 변경될 때 Menu Sheet의 `open` 상태를 동기화합니다. 4. `layerIndex={useActivityZIndexBase({ activityOffset: 1 })}`로 현재 Activity보다 한 단계 높은 z-index 기준점을 전달합니다. - [`useActivityZIndexBase`에 대해 자세히 알아보기](#about-useactivityzindexbase) ### `useStepOverlay` \[#usestepoverlay] \#2와 #3을 일반화하여 `useStepOverlay`를 사용하면 편리합니다. `useStepOverlay` 구현 예시는 [코드](https://github.com/daangn/seed-design/blob/dev/examples/stackflow-spa/src/seed-design/stackflow/use-step-overlay.ts)를 참고하세요. ```tsx import { useActivityZIndexBase } from "@seed-design/stackflow"; import { Portal } from "@seed-design/react"; import { useStepOverlay } from "./use-step-overlay"; // ... more imports const MyActivity: ActivityComponentType = () => { // [!code highlight] const { overlayProps, setOpen } = useStepOverlay(); const { popStep } = useStepFlow("MyActivity"); const { push } = useFlow(); const handleAction = (action: string) => { console.log("선택한 액션:", action); popStep(); }; return ( {/* [!code highlight] */} Open {/* [!code highlight] */} handleAction("add")} label="추가" /> handleAction("edit")} label="수정" /> { // [!code highlight] setOpen(false); // 다른 Activity로 이동하기 전에는 Menu Sheet를 닫으세요. push("ActivityNext"); }} label="Push" /> handleAction("delete")} tone="critical" label="삭제" /> {/* [!code highlight] */} ); }; ``` ## About `useActivityZIndexBase` \[#about-useactivityzindexbase] `useActivityZIndexBase`는 각 Activity의 z-index 기준점을 반환하는 훅입니다. - `useActivity().zIndex * 5`를 반환합니다. `useActivity().zIndex`는 현재 Activity가 enter된 Activity 중 몇 번째 Activity인지를 나타내는 값입니다. - AppScreen을 구성하는 요소들(layer, appbar, dim, edge)은 `useActivityZIndexBase()` 값을 바탕으로 계산된 z-index를 갖습니다. ``` ┌─ 2번 BottomSheet Activity │ └─ BottomSheet: 12 (2 × 5 + 2) ├─ 1번 AppScreen Activity │ ├─ appbar: 12 (1 × 5 + 7) │ ├─ edge: 9 (1 × 5 + 4) │ ├─ layer: 7 (1 × 5 + 2) │ └─ dim: 5 (1 × 5 + 0) └─ 0번 AppScreen Activity ├─ appbar: 7 (0 × 5 + 7) ├─ edge: 4 (0 × 5 + 4) ├─ layer: 2 (0 × 5 + 2) └─ dim: 0 (0 × 5 + 0) ``` - `activityOffset` 옵션은 현재 Activity가 아닌 다음 Activity의 z-index 기준점을 사용하고 싶을 때 사용합니다. - 예를 들어, `useActivityZIndexBase({ activityOffset: 1 })`는 다음 Activity의 z-index 기준점을 반환합니다. - 이는 Step 패턴에서 현재 Activity 위에 BottomSheet를 표시할 때 유용합니다. ``` ┌─ 1번 AppScreen Activity에서 activityOffset: 1로 띄운 BottomSheet (예: Step 패턴) │ └─ BottomSheet: 12 ((1 + 1) × 5 + 2) ├─ 1번 AppScreen Activity │ ├─ appbar: 12 (1 × 5 + 7) │ ├─ edge: 9 (1 × 5 + 4) │ ├─ layer: 7 (1 × 5 + 2) │ └─ dim: 5 (1 × 5 + 0) └─ 0번 AppScreen Activity ├─ appbar: 7 (0 × 5 + 7) ├─ edge: 4 (0 × 5 + 4) ├─ layer: 2 (0 × 5 + 2) └─ dim: 0 (0 × 5 + 0) ``` - 두 훅 모두 현재 Activity가 enter된 Activity 중 \**몇 번째 Activity인지를 바탕으로 z-index 스타일링에 필요한 값을 반환하는 훅입니다.*\* - 두 훅이 반환하는 값은 같으나, [@stackflow/plugin-basic-ui](https://www.npmjs.com/package/@stackflow/plugin-basic-ui)가 아닌, SEED 컴포넌트를 사용하는 경우 SEED가 관리하는 `@seed-design/stackflow` 패키지의 `useActivityZIndexBase` 훅을 사용하는 것을 권장합니다. - `@stackflow/react-ui-core` 패키지의 `useActivityZIndexBase`는 `activityOffset` 옵션을 제공하지 않습니다. - `@seed-design/stackflow@1.1.1`부터 `useActivityZIndexBase` 훅이 제공됩니다. `layerIndex`를 `useActivity().zIndex * 5 + n` 형태로 지정하고 있었다면 `useActivityZIndexBase() + n`으로 변경할 수 있습니다. 변경 과정에서 `+ n`이 필요한지 함께 검토해보세요. --- file: updates/changelog.mdx # Changelog 최신 업데이트와 변경사항을 기록합니다. ## @seed-design/cli ## 1.5.1 ### Patch Changes - 304b0c9: `--seed-react-version` 옵션에 `1.0`과 같은 버전 값을 넘길 때 파싱 에러가 발생하던 문제를 수정합니다. ## 1.5.0 ### Minor Changes - b9a2956: `add`, `add-all` 명령어에 `--seed-react-version` 옵션을 추가합니다. - 설치한 `@seed-design/react` 버전에 맞는 스니펫 레지스트리를 CLI가 자동으로 찾아줍니다. 정확한 `--baseUrl`을 직접 지정할 필요가 없습니다. - `--baseUrl`, `--framework`보다 우선 적용됩니다. ```sh npx @seed-design/cli@latest add --seed-react-version 1.2 ui:action-button ``` ## 1.4.0 ### Minor Changes - 699af2c: SEED Design Lynx의 첫 stable 버전을 릴리즈합니다. - `@seed-design/lynx-css`: Lynx 환경에서 사용할 수 있는 SEED 디자인 토큰, base CSS, component recipe CSS를 제공합니다. - `@seed-design/lynx-react`: Lynx용 SEED React 컴포넌트와 hook을 제공합니다. - 컴포넌트: `ActionButton`, `BottomSheet`, `Box`, `Checkbox`, `HStack`, `ProgressCircle`, `RadioGroup`, `Switch`, `TagGroup`, `Text`, `VStack` - Hook: `useControllableState`, `useIconColor`, `usePressTap`, `useSafeArea` - Safe Area: `useSafeArea` API로 `env(safe-area-inset-*)`와 host 앱의 `lynx.__globalProps` fallback을 함께 지원합니다. - `@seed-design/cli`: Lynx framework 감지, registry snippet 추가, compatibility check, docs lookup 흐름을 지원합니다. ## 1.3.5 ### Patch Changes - 91f2e12: CLI 기반 도구의 `cac` 의존성을 v7로 업데이트합니다. - `@seed-design/cli`와 `@seed-design/codemod`의 Node.js 요구사항을 `>=20.19.0`으로 맞춥니다. - `@seed-design/mcp`와 `@seed-design/figma-extractor`에서도 최신 `cac` 런타임을 사용합니다. ## 1.3.4 ### Patch Changes - c5012d1: @seed-design/cli 옵션 스키마 정합성을 개선합니다. ## 1.3.3 ### Patch Changes - abada1c: CLI 텔레메트리 수집을 개선합니다. - `add`, `add-all`, `compat`, `docs`, `init` 명령어의 실행 결과를 `completed`, `cancelled`, `failed`로 구분해 수집합니다. - `compat` 명령어는 `compatible`, `incompatible`, `empty` 결과를 추가로 구분해 수집합니다. - 텔레메트리 실패 테스트를 보강하고, 실패 시에는 상세 메시지 대신 안전한 에러 타입만 전송하도록 정리합니다. ## 1.3.2 ### Patch Changes - 1e91bf6: `docs` 명령에 `--raw` 옵션을 추가합니다. llms.txt 내용을 순수 마크다운으로 출력하여 LLM 파이프나 스크립트에서 활용할 수 있습니다. `upgrade` 명령을 제거하고 `docs --raw` + 스킬 조합으로 대체합니다. ## 1.3.1 ### Patch Changes - 8f37e56: `docs` 명령어에서 경로를 잘못 입력했을 때 유사한 항목을 제안합니다. - 오타가 포함된 경로를 입력하면 가장 가까운 유효 경로를 알려줍니다. (예: `react/component/action-buton` → `react/components/action-button`) - 카테고리, 섹션, 아이템 각 단계에서 유사 후보를 자동 검색합니다. - 0471a92: `upgrade` 명령어를 추가합니다. - 프로젝트에 설치된 `@seed-design/*` 패키지의 현재 버전과 최신 버전을 비교하고, 그 사이의 변경사항(changelog)을 확인할 수 있습니다. - `--raw` 플래그를 사용하면 UI 없이 순수 마크다운으로 출력되어 LLM 에이전트에 전달하기 적합합니다. - 패키지명은 shorthand(`react`) 또는 full name(`@seed-design/react`) 모두 지원합니다. ## 1.3.0 ### Minor Changes - d25a0d6: LLM 친화적 문서 링크를 위한 `docs` 명령어 추가 - 컴포넌트 및 파운데이션에 대한 문서 URL, llms.txt URL, GitHub raw 스니펫 URL을 출력하는 `seed-design docs [query]` 명령어를 추가합니다. - 섹션/항목의 인터랙티브 선택과 쿼리를 통한 직접 검색을 모두 지원합니다. ## 1.2.2 ### Patch Changes - 50cd41e: - `seed-design compat` 명령어를 추가해 현재 프로젝트의 스니펫과 `@seed-design/react`, `@seed-design/css` 버전 호환성을 비대화형으로 점검할 수 있도록 개선합니다. - `add`, `add-all` 실행 시 스니펫의 요구 버전과 프로젝트 버전을 semver로 비교해, 호환되지 않는 항목을 경고하도록 개선합니다. - 1af88d1: - CLI 실패 원인 표시를 개선하고 `--verbose` 상세 진단 출력을 추가합니다. - `seed-design.json`이 없을 때 외부 명령 실행 대신 내부 초기화 로직으로 설정 파일을 생성합니다. - 또한 `@clack/prompts`를 v1으로 업데이트하고 `init --default`를 `--yes` 호환 alias로 유지합니다. ## 1.2.1 ### Patch Changes - f7a6217: `--on-diff` flag를 추가하고 `--overwrite` flag를 `--on-diff=overwrite`로 대체합니다. `--on-diff=backup`을 사용하여 변경사항이 있는 모든 snippet에 대해 이전 파일을 유지할 수 있습니다. ## 1.2.0 ### Minor Changes - 21c6ca8: CLI add/add-all 명령 실행 시, 파일 변경사항을 보여주고 덮어쓰기/백업/그대로 두기 선택지를 제공합니다. `--overwrite` flag를 통해 이 과정을 건너뛰고 덮어쓰기를 선택할 수 있습니다. ## 1.1.0 ### Minor Changes - a6ae76f: telemetry 옵션을 추가합니다 ## 1.0.0 ### Major Changes - 950c9e1: **`add` 명령어 사용 방식을 변경합니다.** - 항목 추가 ```sh seed-design add ui:action-button breeze:animate-number # ui 이외 레지스트리의 항목도 추가할 수 있게 되었습니다. ``` - 특정 레지스트리에 있는 모든 항목 추가 ```sh seed-design add-all ui lib breeze ``` - 모든 레지스트리의 모든 항목 추가 ```sh seed-design add-all --all ``` 패키지 의존성 및 스니펫 의존성 설치 방식을 최적화합니다. ## 0.0.3 ### Patch Changes - 9b271d6: snippet의 deprecated 여부를 기록합니다. cli add 명령 실행 시 snippet 목록에서 deprecate 여부를 표시하고, `--all`로 모든 스니펫 추가 시 기본적으로 deprecated snippet을 제외합니다. ```sh seed-design add --all # deprecated snippet 제외 ``` ```sh seed-design add --all --include-deprecated # deprecated snippet 포함 ``` ## 0.0.2 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. ## 0.0.0-alpha-20241204134404 ### Patch Changes - 에러메세지 보완 및 사용성 강화 ## 0.0.0-alpha-20241113031935 ### Patch Changes - registry 기준으로 hook, util, ui 파일을 구분하여 설치해요. ## 0.0.0-alpha-20241101153412 ### Patch Changes - default resolve path를 `seed-design/ui`로 변경해요. ## 0.0.0-alpha-20241101030717 ### Patch Changes - url을 변경해요 ## 0.0.0-alpha-20241031064135 ### Patch Changes - implement cli command `check-deprecated-icon-files` ## 0.0.0-alpha-20241031063855 ### Patch Changes - change BASE_URL ## 0.0.0-alpha-20241016030836 ### Patch Changes - add icon-shift command ## 0.0.0-alpha-20241014145845 ### Patch Changes - sync style chipTabs, Tabs to figma ## 0.0.0-alpha-20241014090620 ### Patch Changes - change baseURL, add prepack script ## 0.0.0-alpha-20241014090450 ### Patch Changes - change registry schema ## 0.0.0-alpha-20241014082802 ### Patch Changes - change registry path ## 0.0.0-alpha-20241014082441 ### Patch Changes - change baseURL ## 0.0.0-alpha-20241004093556 ### Patch Changes - prerelease ## 0.0.0-alpha-20241004093313 ### Patch Changes - bump --- ## @seed-design/codemod ## 2.0.0 ### Patch Changes - Updated dependencies [60d1a82] - @seed-design/migration-index@2.0.0 ## 1.0.1 ### Patch Changes - 91f2e12: CLI 기반 도구의 `cac` 의존성을 v7로 업데이트합니다. - `@seed-design/cli`와 `@seed-design/codemod`의 Node.js 요구사항을 `>=20.19.0`으로 맞춥니다. - `@seed-design/mcp`와 `@seed-design/figma-extractor`에서도 최신 `cac` 런타임을 사용합니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/migration-index@1.0.0 ## 0.0.31 ### Patch Changes - 91952e4: `replace-semantic-stroke-color` transform을 추가합니다. - `@seed-design/css` v0.2.0에 맞춰 `replace-semantic-stroke-color` transform을 추가합니다. - `@seed-design/css/vars`에서 사용한 stroke 토큰을 업데이트하는 마이그레이션 코드모드를 추가합니다. ## 0.0.30 ### Patch Changes - Updated dependencies [8448880] - @seed-design/migration-index@0.0.30 ## 0.0.29 ### Patch Changes - 6d6d465: replace-custom-seed-design-typography 사용된 객체 그대로 사용하게끔 수정합니다 ## 0.0.28 ### Patch Changes - ab52481: `replace-custom-seed-design-typography`, `replace-custom-color-to-seed-design-vars-color` 로직 변경 - f801300: 새로운 black, white alpha 값 추가에 대해서 대응하고, V3로 이미 마이그레이션 된 프로젝트의 alpha 값들 변환해주는 transform을 추가합니다 (replace-alpha-color) - Updated dependencies [f801300] - @seed-design/migration-index@0.0.28 ## 0.0.27 ### Patch Changes - 7a146c2: - replace-custom-color-to-seed-design-vars-color 추가 - replace-custom-seed-design-text-component 케이스 추가 - replace-custom-seed-design-typography 케이스 추가 ## 0.0.26 ### Patch Changes - 2854d72: replace-tailwind-typography가 `text-` 접두사가 붙은 className도 처리해요 ## 0.0.25 ### Patch Changes - 3d7b0dc: replace-tailwind-color cva 처리 추가 및 `-seed` prefix 토큰 확인 로직 추가 ## 0.0.24 ### Patch Changes - 03aaeb1: replace-stitches-styled-color 로직 강화 - !important 처리 강화 - color가 들어갈 수 있는 다양한 복합 포맷 대응 - 삼항연산자 변환 대응 ## 0.0.23 ### Patch Changes - 1284609: feat: replace-custom-seed-design-color 직접 seed-design/css 패키지 import 하도록 변경 - 6895bd0: feat(codemod): add more cases in stitches transform ## 0.0.22 ### Patch Changes - b89ef88: - replace-custom-seed-design-vars를 추가해요 - replace-custom-imported-typography-variable를 추가해요 - replace-custom-seed-design-typography를 추가해요 - replace-custom-seed-design-color를 추가해요 - replace-seed-design-token-typography-classname VE에서 잘 변경되지 않던 이슈를 해결해요 - 03cc680: transform logger의 이름을 transform 이름과 동일하게 수정해요 - e368c69: 패키지 의존성을 최신화합니다. ## 0.0.21 ### Patch Changes - 775db48: feat: moduleResolution: node를 추가해요 ## 0.0.20 ### Patch Changes - f17f842: - stitches 관련 transform 추가 - replace-stitches-styled-color - replace-stitches-styled-typography - replace-stitches-theme-color - trasnform 이름 변경 - replace-color-design-token → replace-seed-design-token-vars - replace-color-prop → replace-custom-text-component-color-prop - replace-css-color-variable → replace-css-seed-design-color-variable - replace-css-typography-variable replace-css-seed-design-typography-variable - replace-tailwind-color 유지 - replace-tailwind-typography 유지 - replace-text-component → replace-custom-seed-design-text-component - replace-typography-design-token → replace-seed-design-token-typography-classname - replace-v2-icon → replace-react-icon - Updated dependencies [f17f842] - @seed-design/migration-index@0.0.20 ## 0.0.19 ### Patch Changes - 1cb5fcc: logging 파일 형식을 개선합니다 ## 0.0.18 ### Patch Changes - 5ef19b3: feat: remove custom `--reporter` option, add `log` in all transfomers - Updated dependencies [a7e2571] - @seed-design/migration-index@0.0.18 ## 0.0.17 ### Patch Changes - 93eaaeb: feat: change spawn to execa ## 0.0.16 ### Patch Changes - 95b140f: fix: support dynamic import in ESM ## 0.0.15 ### Patch Changes - 4f465ba: - add replace-text-component transform - add reporter warning message - add more complex test case in replace-tailwind-typography - replace-vars-color -> replace-color-design-token - add replace-typography-design-token - add replace-css-typography-variable - Updated dependencies [4f465ba] - @seed-design/migration-index@0.0.3 ## 0.0.14 ### Patch Changes - 37c12e9: feat: replace-css-color-variable ## 0.0.13 ### Patch Changes - Updated dependencies [9f55b8f] - @seed-design/migration-index@0.0.2 ## 0.0.12 ### Patch Changes - @seed-design/migration-index@0.0.1 ## 0.0.12-rc.4 ### Patch Changes - e2423fa: add prefix in tailwind color codemod, and remove external field - 296ce10: - add replace-color-prop transform - replace-color-design-token change import css to react ## 0.0.12-rc.3 ### Patch Changes - 14c9983: change package.json exports map ## 0.0.12-rc.2 ### Patch Changes - 9f40190: remove run ## 0.0.12-rc.1 ### Patch Changes - 2612bf7: remove require ## 0.0.12-rc.0 ### Patch Changes - f83bbf8: migration index, codemod (vars, tailwind) - Updated dependencies [f83bbf8] - @seed-design/migration-index@0.0.1-rc.0 ## 0.0.11 ### Patch Changes - icon_horizline2_vertical_chatbubble_rectangular_right 반영 ## 0.0.10 ### Patch Changes - icon_speaker_slash 이름 변경 반영 ## 0.0.9 ### Patch Changes - 멀티컬러로 이동시키던 아이콘 모노크롬으로 이동 ## 0.0.8 ### Patch Changes - Yen 아이콘 신규 맵핑 반영 ## 0.0.7 ### Patch Changes - Transform 정상 작동하도록 수정 ## 0.0.6 ### Patch Changes - Release 0.0.6 ## 0.0.6-alpha-20241114065324 ### Patch Changes - Node.js 버전 검증, git 관련 정보 수집 ## 0.0.6-alpha-20241113101316 ### Patch Changes - Require Node.js 20.16 ## 0.0.5 ### Patch Changes - Revert identifier-map to map old chevron_right to old chevron_right, allow turning analytics off ## 0.0.4 ### Patch Changes - map chevron_right to chevron_right_small ## 0.0.3 ### Patch Changes - Release 0.0.3 ## 0.0.3-alpha-20241108080931 ### Patch Changes - track inline svgs ## 0.0.3-alpha-20241108075855 ### Patch Changes - Use write key from env ## 0.0.3-alpha-20241108073245 ### Patch Changes - Submit logs to June.so ## 0.0.3-alpha-20241108030926 ### Patch Changes - make @daangn/\* deps optional ## 0.0.3-alpha-20241108030003 ### Patch Changes - move actual icon packages to devdeps ## 0.0.2 ### Patch Changes - 멀티컬러 패키지로의 마이그레이션에 대응, @karrotmarket/karrot-ui-icon/lib/react 마이그레이션 ## 0.0.2-alpha-20241031032411 ### Patch Changes - Return if importdefaultspecifiers ## 0.0.2-alpha-20241031025011 ### Patch Changes - Fix `icon_home` being replaced with `icon_window4_house` ## 0.0.2-alpha-20241031022819 ### Patch Changes - Notify inline SVGs being used ## 0.0.2-alpha-20241030021453 ### Patch Changes - Replace `@karrotmarket/karrot-ui-icon/lib/react` ## 0.0.2-alpha-20241028102047 ### Patch Changes - Migreate specific icons to the new multicolor package ## 0.0.1 ### Patch Changes - ee15292: Add codemod `migrate-icons` to help users easily migrate v2 icons to v3 ones ## 0.0.0-alpha-20241025045021 ### Patch Changes - Replace string literals ## 0.0.0-alpha-20241024093332 ### Patch Changes - refactor ## 0.0.0-alpha-20241023070021 ### Patch Changes - Keep icons that aren't available yet (categories & services) ## 0.0.0-alpha-20241022081111 ### Patch Changes - remove repeatedly printed filepath ## 0.0.0-alpha-20241022065031 ### Patch Changes - use report instead of winston to print stdout ## 0.0.0-alpha-20241022063510 ### Patch Changes - rename map to match, add tests ## 0.0.0-alpha-20241022061413 ### Patch Changes - update identifier map, warn users on action-required icons ## 0.0.0-alpha-20241021100240 ### Patch Changes - List available transforms & log selectively ## 0.0.0-alpha-20241021032759 ### Patch Changes - Update identifier map ## 0.0.0-alpha-20241020150322 ### Patch Changes - Stream logs to console ## 0.0.0-alpha-20241020144859 ### Patch Changes - Preserve comments ## 0.0.0-alpha-20241020125729 ### Patch Changes - Add prepack script ## 0.0.0-alpha-20241020124836 ### Patch Changes - Allow `--no-babel` ## 0.0.0-alpha-20241020123315 ### Patch Changes - Allow custom parser ## 0.0.0-alpha-20241020094046 ### Patch Changes - Build ## 0.0.0-alpha-20241020093300 ### Patch Changes - Run babel & exclude d.ts files ## 0.0.0-alpha-20241020090828 ### Patch Changes - Build correctly ## 0.0.0-alpha-20241020090615 ### Patch Changes - Add flag --no-babel ## 0.0.0-alpha-20241020064329 ### Patch Changes - Allow null for identifiermap target value & improve logging ## 0.0.0-alpha-20241020054343 ### Patch Changes - Remove unnecessary log files & update target package name ## 0.0.0-alpha-20241020052702 ### Patch Changes - Find transform files in the correct directory ## 0.0.0-alpha-20241020051045 ### Patch Changes - Find transform files in the correct directory ## 0.0.0-alpha-20241020044547 ### Patch Changes - Move files around ## 0.0.0-alpha-20241018101237 ### Patch Changes - Add shebang ## 0.0.0-alpha-20241018093322 ### Patch Changes - Add transform `migrate-icons` --- ## @seed-design/css ## 2.1.3 ### Patch Changes - d71f402: 구형 iOS Safari(16.4 미만)에서 중첩된 `Box` 및 일부 컴포넌트가 상위 요소의 레이아웃 값을 잘못 상속하던 문제를 수정합니다. - 값을 지정하지 않은 자식 `Box`가 상위의 `width`, `height`, `gap`, `margin` 등을 물려받아 의도와 다르게 렌더링되던 문제를 해결합니다. 모던 브라우저의 동작에는 변화가 없습니다. - `SidePanel`, `BottomSheet`, `Skeleton`, `HelpBubble`에서도 동일한 상속 문제를 바로잡습니다. - `bleed`를 지정한 요소가 구형 Safari에서도 정상 동작하도록 개선합니다. ## 2.1.2 ### Patch Changes - bfaa48a: Floating Action Button의 확장(extended) 형태 아이콘 크기를 `$dimension.x5`에서 `$dimension.x4`로 수정합니다. - 7224b94: ImageFrameReactionButton root에 반투명 배경색과 그림자를 추가하여 밝은 이미지 위에서의 가시성을 개선합니다. - c4ce431: Side Panel Close Button 및 Side Panel Header의 스타일을 디자인 스펙에 맞게 수정하고, Side Panel body에 `box-sizing: border-box`를 적용하여, body에 지정한 `height`/`minHeight`/`maxHeight`가 padding을 포함해 계산되도록 수정합니다. ## 2.1.1 ### Patch Changes - 11b7ffe: `AttachmentInput`/`AttachmentDisplay`의 image 타입 아이템 테두리가 이미지에 가려 렌더되지 않던 문제를 수정합니다. ## 2.1.0 ### Minor Changes - f2b198d: `$color.bg.layer-fill` 토큰을 다시 추가합니다. `2.0.0`에서 제거했으나 마땅한 대체 토큰이 없어 deprecated 상태로 되살립니다. 값은 제거 이전과 동일합니다. 추후 동일한 값의 새 이름 토큰으로 대체된 뒤 `3.0.0`에서 제거될 예정입니다. `@seed-design/tailwind3-plugin`과 `@seed-design/tailwind4-theme`의 `@seed-design/css` peer dependency 버전 범위를 `^2.1.0`으로 올립니다. ## 2.0.0 ### Major Changes - 4ad24cc: Box, Flex, Grid, VStack, HStack 등 레이아웃 컴포넌트에 margin 관련 프로퍼티를 추가합니다. - `margin`/`m`, `marginX`/`mx`, `marginY`/`my`, `marginTop`/`mt`, `marginRight`/`mr`, `marginBottom`/`mb`, `marginLeft`/`ml` - `"auto"` 리터럴 및 breakpoint 기반 반응형 스타일링을 지원합니다. - `margin*`과 `bleed*`를 동시에 사용할 수 없도록 타입 레벨 제약(discriminated union)을 추가합니다. - @seed-design/react를 통해 제공되는 다음 인터페이스가 `interface`에서 `type`으로 전환됩니다. - `BoxProps`, `FlexProps`, `GridProps`, `StackProps`, `VStackProps`, `HStackProps`, `ArticleProps`, `AspectRatioProps`, `ColumnsProps`, `ColumnProps`, `GridItemProps`, `ImageFrameProps`, `InlineProps`, `ResponsivePairProps`. - 위 인터페이스를 `extend`하는 타입은 `type X = BoxProps & {...}`처럼 전환할 수 있습니다. Box, Flex, Grid, VStack, HStack 등 레이아웃 컴포넌트에 4개 방향 모두에 negative margin을 적용하는 `bleed` 프로퍼티를 추가합니다. - 555525a: Attachment Field 관련 컴포넌트를 추가합니다. - Attachment Field: 파일을 선택하거나 드래그 앤 드롭으로 직접 업로드할 때 사용합니다. - Attachment Display Field: 외부 소스에서 URL로 제공된 미디어를 표시·관리할 때 사용합니다. - 7a6847e: `AppScreen` 관련 키보드 동작을 개선합니다. - `AppScreen` 액티비티 push 시, 해당 `AppScreen`으로 키보드 포커스가 이동하도록 수정합니다. - `AlertDialog` 등 모달 컴포넌트 위에 `AppScreen`을 push하는 경우에도 `AlertDialog`에 포커스가 남아 있는 문제를 수정합니다. - 가장 마지막에 등장한 `AppScreen` 안에서만 포커스가 유지되도록 수정합니다. - 029d052: Help Bubble Tooltip 컴포넌트를 추가합니다. - 54a2ff8: Layout 컴포넌트를 추가합니다. - d1a8d7c: 신규 타이포그래피 토큰을 추가합니다. - **폰트 크기 토큰 (`$font-size`)** - `t11`, `t11-static` - `t12`, `t12-static` - `t13`, `t13-static` - `t14`, `t14-static` - **줄 간격 토큰 (`$line-height`)** - `t11`, `t11-static` - `t12`, `t12-static` - `t13`, `t13-static` - `t14`, `t14-static` - **텍스트 스타일 (`textStyle`)** - `t8Regular`, `t8Medium`, `t8StaticRegular`, `t8StaticMedium` - `t9Regular`, `t9Medium`, `t9StaticRegular`, `t9StaticMedium` - `t10Regular`, `t10Medium`, `t10StaticRegular`, `t10StaticMedium` - `t11Regular`, `t11Medium`, `t11Bold`, `t11StaticRegular`, `t11StaticMedium`, `t11StaticBold` - `t12Regular`, `t12Medium`, `t12Bold`, `t12StaticRegular`, `t12StaticMedium`, `t12StaticBold` - `t13Regular`, `t13Medium`, `t13Bold`, `t13StaticRegular`, `t13StaticMedium`, `t13StaticBold` - `t14Regular`, `t14Medium`, `t14Bold`, `t14StaticRegular`, `t14StaticMedium`, `t14StaticBold` - 69e3b97: Menu 컴포넌트를 추가합니다. Drawer를 연 뒤 Drawer 뒤 요소에 포커스가 남아 있는 문제를 수정합니다. Drawer가 열리는 경우 `Drawer.Content`에 자동으로 포커스가 이동합니다. - `Drawer.RootProps`의 `autoFocus` 기본값을 `false`에서 `true`로 변경합니다. - 스크린 리더가 `modal=true` (기본값)인 Dialog 및 Drawer 뒤 요소를 읽을 수 있는 문제를 수정합니다. Dialog(AlertDialog, MenuSheet)와 Drawer(BottomSheet)를 `@radix-ui/react-dismissable-layer`에서 자체 `useDismissibleLayer` 훅 기반으로 리팩터링하고 불필요하게 외부로 노출되던 내부 옵션들을 제거합니다. - `DialogRoot` 및 `DrawerRoot`의 `onOpenChange` 두 번째 인자 `details.reason`이 `interactOutside`인 경우 `details.event`의 타입을 `PointerEvent | FocusEvent`에서 `PointerEvent | TouchEvent | FocusEvent`로 변경합니다. - `DialogRoot` 및 `DrawerRoot` 두 번째 인자 `details`의 `reason`으로 `cascadeDismiss`를 추가합니다. 두 개 이상의 오버레이 컴포넌트를 표시한 상황에서 하위 컴포넌트가 dismiss되는 경우 상위 컴포넌트는 `cascadeDismiss`와 함께 `onOpenChange`가 호출됩니다. - `@seed-design/react` 패키지에서 `@radix-ui/react-dialog` 의존성을 제거합니다. - 57e4f1a: Chip Tabs Trigger가 disabled 상태일 때 opacity를 사용하여 스타일을 조절하도록 수정합니다. - 2abd3ed: Side Navigation에서 내부적으로 사용되는 `NavigationMenu` 컴포넌트를 추가합니다. - 270b277: 컴포넌트 및 파운데이션 전반에 JSDoc 주석을 강화합니다. - 9369ff9: Breakpoint 기반 반응형 스타일링을 지원합니다. - Box, Flex, Grid, VStack 등 유틸리티 컴포넌트의 레이아웃 관련 프로퍼티에 breakpoint 기반 반응형 객체를 사용할 수 있습니다. ```tsx ``` - `@seed-design/react`에서 `useBreakpoint` 훅과 `useBreakpointValue` 훅을 제공합니다. - `useBreakpoint()` — 현재 활성 breakpoint 이름을 반환합니다. (`"base"` | `"sm"` | `"md"` | `"lg"` | `"xl"`) - `useBreakpointValue(values)` — 반응형 객체에서 현재 breakpoint에 해당하는 값을 반환합니다. ```tsx const actionButtonProps = useBreakpointValue({ base: { variant: "neutralWeak" }, lg: { variant: "brandSolid" }, }); ``` ``으로 Grid를 숨길 수 없던 문제를 수정합니다. - 4839a64: `selectBoxCheckmark` recipe의 생성 클래스명과 import 경로를 다른 recipe와 동일하게 kebab-case로 정정합니다. - 클래스명: `seed-selectBoxCheckmark__*` → `seed-select-box-checkmark__*` - import 경로: `@seed-design/css/recipes/selectBoxCheckmark` → `@seed-design/css/recipes/select-box-checkmark` - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. - ec33023: Side Navigation 컴포넌트를 추가합니다. - 98b52a7: Side Panel 컴포넌트를 추가합니다. - 화면 좌/우 가장자리에서 슬라이드해 보조 콘텐츠, 상세 정보, 설정 흐름을 표시할 수 있습니다. - f982d61: `SwipeableMenuSheet` 컴포넌트를 추가하고, 기존 `MenuSheet` 관련 API를 deprecate합니다. **`SwipeableMenuSheet` 추가** - Drawer 기반의 `SwipeableMenuSheet` 관련 컴포넌트를 추가합니다. - 전체 영역을 스와이프하여 시트를 닫을 수 있으며, 해당 동작의 힌트로 상단에 드래그 핸들이 표시됩니다. - `MenuSheet`에서는 항상 표시되었던 닫기 버튼을 기본적으로 UI에 표시하지 않습니다. `showCloseButton` prop을 통해 닫기 버튼의 노출 여부를 제어할 수 있습니다. **`MenuSheet` 관련 컴포넌트 및 API Deprecate** - Dialog 기반의 `MenuSheet` 관련 컴포넌트 및 API를 deprecate합니다. 신규로 Menu Sheet 사용이 필요한 경우 `SwipeableMenuSheet`를 사용합니다. - `open`/`defaultOpen`/`onOpenChange`등 기본적인 `MenuSheet`의 API는 `SwipeableMenuSheet`에서 동일하게 유지됩니다. - 7f1dbe3: `Footer.LinkText` 컴포넌트 및 `Footer` Block 예시를 추가합니다. - c0fd999: Text Input(Text Field)과 Input Button(Field Button)에 `size="medium"` variant를 추가합니다. - 31bf164: Accordion 컴포넌트를 추가합니다. - 여러 개의 관련된 콘텐츠 섹션을 수직으로 나열하고, 각 섹션을 펼치거나 접어 정보를 탐색할 수 있는 컴포넌트입니다. ## 1.2.16 ### Patch Changes - 234e8c7: deprecated 항목의 제거 예정 버전 안내를 1.3.0에서 2.0.0으로 변경합니다. - `ImageFrame`의 `rounded` 옵션, `Switch`/`Checkbox`/`ChipTabs`의 deprecated 옵션, 유틸리티 스타일 prop(`display`, `flexDirection`, `justifyContent`, `alignItems`)의 deprecated 값의 제거 예정 버전을 2.0.0으로 안내합니다. - `$color.bg.layer-fill`, `$gradient.fade-layer-floating`, `$gradient.fade-layer-default` 토큰의 제거 예정 버전을 2.0.0으로 안내합니다. - 2.0.0부터 deprecated 항목 제거를 포함한 breaking change는 메이저 릴리스에서만 수행됩니다. ## 1.2.15 ### Patch Changes - 26b1de5: `AlertDialog`, `MenuSheet` 등 Dialog 기반 컴포넌트가 열릴 때 첫 항목 대신 컨테이너에 포커스가 가도록 수정합니다. - URL로 직접 진입해 열었을 때 첫 항목에 포커스 링이 노출되던 문제를 수정합니다. - 스크린리더가 다이얼로그의 제목과 설명을 먼저 읽도록 초기 포커스를 컨테이너로 옮깁니다. ## 1.2.14 ### Patch Changes - 8dc8f4f: Menu Sheet의 디자인 스펙에서 핸들 여백 확보를 위해 `content` 상단 패딩을 `x4` → `x6`으로 늘리고 `header.paddingTop`을 제거합니다 (핸들 여백 확보). React/CSS `MenuSheet`의 경우 핸들이 존재하지 않으므로 변경사항이 없습니다. - e48f021: HelpBubble이 좁은 화면에서 화면 밖으로 잘리던 문제를 수정합니다. (Popover의 floating 요소 너비를 viewport에 맞게 동적으로 제한하고, 긴 텍스트가 Help Bubble 밖으로 넘치지 않도록 긴 단어 중간 줄바꿈을 허용합니다.) ## 1.2.13 ### Patch Changes - 9cacf29: AppBar 하단에 구분선을 표시하는 `divider` 옵션을 deprecated 처리합니다. SEED React 1.3에서 제거될 예정이며, 제거 이후에는 AppBar 하단 구분선이 표시되지 않습니다. ## 1.2.12 ### Patch Changes - d8860f4: Chip `layout=withText` variant에 size 별 최소 너비를 추가합니다. - 7588e59: 신규 pressed 상태 스타일링을 위한 `$duration.pressed-scale` 및 `$timing-function.pressed-scale` 토큰을 추가합니다. (컴포넌트 변경사항 없음) ## 1.2.11 ### Patch Changes - be7022a: AppScreen 전환 애니메이션의 성능을 개선하고 스와이프 백 관련 버그를 수정합니다. - 스와이프 중 CSS variable cascade로 인한 스타일 재계산 병목을 제거하고, 개별 요소에 직접 inline style을 적용하는 방식으로 변경합니다. - 스와이프 종료 시 발생하던 플리커(CSS 핸드오프 gap)를 WAAPI(Web Animations API)로 대체하여 해결합니다. - 스와이프 complete 후 CSS `[pop]` animation이 재트리거되는 이중 애니메이션 문제를 해결합니다. - 제스처 중 `useState` 기반 상태 관리를 `useRef`로 변경하여 불필요한 React 리렌더를 제거합니다. - `slideFromRightIOS`, `fadeFromBottomAndroid`, `fadeIn` 세 가지 transition style 모두 지원합니다. - 프로덕션에서 무거운 activity를 push/pop 할 때 애니메이션이 경로 중간부터 시작되던 jank를 수정합니다. (`requestAnimationFrame` 대기 구간 제거) - `Animation.finished`를 지원하지 않는 구형 WebView(Chrome < 84)에서 push → pop 시 화면이 비어버리던 문제를 `onfinish`/`oncancel` 기반 폴백과 setTimeout race 안전망으로 해결합니다. - AppBar 배경을 pseudo-element에서 일반 DOM slot으로 옮겨, `pseudoElement` WAAPI 옵션을 지원하지 않는 브라우저(Chrome < 82)에서도 swipe back 중 AppBar 전환이 정상 동작하도록 합니다. ## 1.2.10 ### Patch Changes - 46bdf17: AppBar 내부 title/subtitle 영역이 폰트 스케일링이 대응된 iOS 환경에서 최대 120%까지 폰트 스케일링을 하도록 변경합니다. (기존 동작: 폰트 스케일링이 대응된 환경에서도 폰트 스케일링 설정의 영향을 받지 않음) - 79c7aa3: NotificationBadge의 size=large variant가 최소 너비를 갖도록 하고, size=large에서 표시되는 레이블이 폰트 스케일링의 영향을 받지 않도록 수정합니다. ## 1.2.9 ### Patch Changes - 5bf2e8a: `ContentPlaceholder` asset의 최대 너비를 64px에서 160px로 변경합니다. ## 1.2.8 ### Patch Changes - c02bfba: ImageFrame에서 이미지 로드 실패 시 깨진 img 요소가 fallback 위에 노출되는 문제를 수정합니다. - f391668: 삼성 Galaxy 기기에서 `:hover` 스타일이 요소에서 손을 뗀 이후에도 남아있는 문제를 수정합니다. ## 1.2.7 ### Patch Changes - 846036b: `AppBarSlot` 컴포넌트를 추가합니다. `AppBar` 내에 `AppBarLeft`, `AppBarMain`, `AppBarRight`에 대응되지 않는 커스텀 요소를 배치하는 경우, 다른 요소와 동일한 화면 전환 트랜지션을 적용하기 위해 사용할 수 있습니다. - e78d460: Avatar 및 Avatar Stack의 `size=56` variant를 추가합니다. ## 1.2.6 ### Patch Changes - 4f56e76: (사용자 변경사항 없음) Rootage `text-input`에 `type=singleLine` variant 정의를 추가합니다. - 32938de: HelpBubble에서 닫기 버튼이 있을 때 title이 한 줄인 경우 아래쪽 여백이 과도하게 보이던 문제를 수정했습니다. - 67a7780: Content Placeholder 컴포넌트를 추가합니다. - 3273312: 사용되지 않는 `tag-group-tag.css` 파일을 코드베이스에서 제거합니다. (`TagGroupItem`에서는 `tag-group-item.css` 사용) - 9d9b891: ImageFrame 컴포넌트 개선 - `fallback` prop이 이미지 로딩 실패 시 대체 콘텐츠를 올바르게 표시하도록 개선합니다. - Reaction Button이 iOS에서 렌더링되지 않는 문제를 수정합니다. - Reaction Button uncontrolled 상태에서 클릭 시 상태가 변경되지 않는 문제를 수정합니다. ## 1.2.5 ### Patch Changes - c9fb0c5: Box의 배경 색상을 class 기반으로 지정하는 경우 `:active` 스타일 선언이 배경 색상을 덮어쓰는 문제를 수정합니다. ## 1.2.4 ### Patch Changes - cd9a46c: Android 환경에서 테마 고정이 적용되지 않을 수 있는 문제를 수정합니다. (`[data-seed-color-mode="*-only"]`로 테마를 고정하는 경우, `color-scheme: only *;` 사용 가능한 환경에서는 `only` 키워드를 사용하도록 수정합니다.) - 23e369d: rootage에 pressed 상태로 정의된 스타일을 디바이스 환경에 따라 다르게 적용합니다. 마우스 환경에서는 hover 시, 터치 환경에서는 active 시 표시됩니다. - 103830e: List Item의 title fontWeight를 `$font-weight.medium`에서 `$font-weight.regular`로 변경합니다. - 77cdc0e: IdentityPlaceholder의 스타일과 글리프를 업데이트하고, `identity="business"` variant를 추가합니다. - 7d6c910: Cascade Layer를 선택적으로 사용할 수 있도록 지원합니다. `seed-base` 및 `seed-components` 레이어로 감싼 상태의 SEED CSS를 사용할 수 있습니다. ([문서](https://seed-design.io/react/getting-started/styling/cascade-layers)) - 751e952: 컴포넌트에 키보드 상호작용이 가능한 요소가 있는 경우 적절한 포커스 스타일을 갖도록 수정합니다. ## 1.2.3 ### Patch Changes - eb390cf: BottomSheet의 title padding을 수정합니다. - left 헤더 정렬 시 오른쪽 padding을 `50px` -> `56px`로 수정합니다. - center 헤더 정렬 시 양쪽 padding을 `60px` -> `56px`로 수정합니다. - 7c3bbe7: Slider의 Value Indicator 가시성 및 트랜지션을 개선합니다. - 6db2515: AppBar의 `bg` 또는 `background` 프로퍼티로 배경 색상을 조절할 수 있는 옵션을 추가합니다. (`tone="layer"`인 경우 적용) - 12789e6: Segmented Control pressed, selected-pressed 상태에서 stroke border 가 나타나도록 합니다. - 6fb6dc2: AspectRatio 스타일시트의 `position: relative`와 `overflow: hidden` 선언을 React 레이어로 이동하여 CSS specificity 문제를 수정합니다. ## 1.2.2 ### Patch Changes - 15010c3: ImageFrame 내부 요소에 width, height 100%를 추가합니다 - a3e6859: ImageFrame `rounded` 옵션을 제거하고, borderRadius를 받을 수 있게 변경합니다 - 4287600: BottomSheet title 영역에서 닫기 버튼 간격에 따라 조정된 padding 기준으로 정리하고, word-break 추가 및 description 영역 너비를 정리했습니다. ## 1.2.1 ### Patch Changes - 9446f2c: ImageFrameReactionButton에 fillIcon, lineIcon spec을 추가합니다 - 8ad9484: ImageFrameReactionButton의 SVG 사이즈와 Rootage 정의를 수정합니다. - 9cbeba0: BottomSheet `showCloseButton` variant 변경 및 animation 버그 수정 - `showCloseButton` variant 변경: CloseButton 미사용 시 header padding 조정 가능 - `hasEntered` 상태 추가: modal prop 변경 시 enter animation 재실행 방지 ## 1.2.0 ### Minor Changes - 0ecb893: [Help Bubble](/react/components/help-bubble) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet 업데이트 필요**: `HelpBubbleTrigger` 및 `HelpBubbleAnchor`의 내부 구조가 변경되었습니다. snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:help-bubble` - **인터페이스 변경사항이 없으므로 `HelpBubbleAnchor`와 `HelpBubbleTrigger`를 사용하는 기존 코드를 변경할 필요가 없습니다.** - `HelpBubble.Body`를 사용하여 `HelpBubble.Title`과 `HelpBubble.Description`을 감싸도록 변경되었습니다. - `zIndexOffset`을 활용하여 `HelpBubble.Positioner`의 z-index를 조정할 수 있습니다. ([예시](/react/components/help-bubble#z-index-offset)) - 98dbac4: [Checkbox](/react/components/checkbox) 관련 컴포넌트를 추가합니다. - `CheckboxGroup` snippet 컴포넌트가 추가되었습니다. 사용하려면 snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:checkbox` - `CheckboxGroup`은 자체적으로 gap과 100% width를 갖습니다. `VStack`을 사용하여 `Checkbox`를 묶지 않아도 됩니다. - 기존 `Checkbox`를 `CheckboxGroup`으로 감쌀 필요는 없습니다. `CheckboxGroup`은 선택적으로 사용할 수 있습니다. - `label`, `description`, `errorMessage`, `indicator`, `showRequiredIndicator`, `labelWeight` prop을 사용할 수 있습니다. [Radio Group](/react/components/radio-group) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet 업데이트 필요**: `RadioGroup` snippet의 내부 구조가 변경되었습니다. snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:radio-group` - `RadioGroup`이 자체적으로 gap과 100% width를 갖습니다. `VStack`을 사용하여 `RadioGroupItem`을 묶는 코드를 제거합니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `RadioGroupItem`을 묶어서 사용하던 `VStack`을 제거하여 `RadioGroupItem`이 `RadioGroup`의 direct child가 되도록 변경하세요. - `label`, `description`, `errorMessage`, `indicator`, `showRequiredIndicator`, `labelWeight` prop을 사용할 수 있습니다. - `@seed-design/react`의 `RadioGroup.Root`를 레이아웃 컴포넌트로 변경합니다. - `@seed-design/react`에서 직접 import해서 사용하는 코드가 있다면 `RadioGroup.Root`를 `@seed-design/react/primitive`의 `RadioGroup.Root`로 변경해주세요. ```tsx // 전 import { VStack } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; ; ``` ```tsx // 후 import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; { /* aria-label 대신 label을 사용하여 시각적으로 레이블을 표시할 수도 있습니다. */ } ; ``` ```tsx // 전 import { RadioGroup } from "@seed-design/react"; import { ListRadioItem } from "seed-design/ui/list"; } /> ; ``` ```tsx // 후 import { RadioGroup } from "@seed-design/react/primitive"; import { ListRadioItem } from "seed-design/ui/list"; } /> ; ``` RadioGroupItem, RadioChipItem, RadioSelectBoxItem, ListRadioItem에서 `invalid` prop이 제거되었습니다. - **1.1 → 1.2 업그레이드 시 확인 필요**: `invalid` 상태는 group/field 레벨에서 설정해주세요. 각 항목을 `data-invalid` 속성으로 스타일링하는 경우 확인이 필요합니다. - a58022d: `SwitchMark`를 `Switchmark`로, `RadioMark`를 `Radiomark`로 Snippet 컴포넌트 이름을 변경합니다. - **1.1 → 1.2 업그레이드 시 변경 권장**: snippet을 다시 내려받고, `SwitchMark`, `RadioMark`를 사용하는 코드를 아래와 같이 변경하세요. - `npx @seed-design/cli@latest add ui:switch ui:radio-group` - snippet에 `SwitchMark`, `RadioMark` 정의가 존재하지만, 1.3 릴리즈 시 snippet에서 해당 맵핑이 제거될 예정이므로 미리 변경해두시길 권장드립니다. ```tsx // 전 import { ListSwitchItem, ListRadioItem } from "seed-design/ui/list"; import { SwitchMark } from "seed-design/ui/switch"; import { RadioMark } from "seed-design/ui/radio-group"; } />; } value="option" title="옵션" />; ``` ```tsx // 후 import { ListSwitchItem, ListRadioItem } from "seed-design/ui/list"; import { Switchmark } from "seed-design/ui/switch"; import { Radiomark } from "seed-design/ui/radio-group"; } />; } value="option" title="옵션" />; ``` - 2643d17: [Select Box](/react/components/select-box) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet을 다시 내려받아 주세요.** - `npx @seed-design/cli@latest add ui:select-box` - `CheckSelectBoxGroup`, `RadioSelectBoxRoot`의 children이 기본적으로 gap이 포함된 그리드 레이아웃으로 정렬됩니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `CheckSelectBox`, `RadioSelectBoxItem`을 묶어서 사용하던 `VStack`을 제거하여 `CheckSelectBox`와 `RadioSelectBoxItem`이 `CheckSelectBoxGroup` 또는 `RadioSelectBoxRoot`의 direct child가 되도록 변경하세요. `VStack`에 `gap` 이외의 스타일이 적용된 경우 `...`와 같이 `VStack`을 외부에 남겨두세요. - **기능 추가**: `CheckSelectBoxGroup`와 `RadioSelectBoxRoot`에 `columns`를 지정할 수 있습니다. `columns`가 `2` 이상인 경우 하위 항목에 기본적으로 `layout="vertical"`이 적용됩니다. 기본 `layout`은 하위 항목에서 오버라이드할 수 있습니다. - **기능 추가**: `CheckSelectBoxGroup`과 `RadioSelectBoxRoot`에 `label`, `description`, `errorMessage`, `indicator` 등 Fieldset 관련 prop을 사용할 수 있습니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `CheckSelectBox`, `RadioSelectBoxItem`에 기본적으로 표시되던 `Checkmark`와 `RadioMark`가 이제 표시되지 않습니다. `suffix` prop을 통해 선택적으로 추가할 수 있습니다. - 단순 마이그레이션 시 `suffix={}`와 `suffix={}`를 추가하세요. - **기능 추가**: `prefixIcon`, `footer`, `footerVisibility` prop 추가 - `footer`에 넣는 요소는 기본적으로 해당 `CheckSelectBox` 또는 `RadioSelectBoxItem`가 선택된 상태일 때 표시됩니다. `footerVisibility="always"`를 설정하여 footer 요소를 항상 표시할 수 있습니다. - `label`이 기본적으로 가로 나열되며 `$dimension.x2` gap을 갖는 flex container로 변경되었습니다. - **1.1 → 1.2 업그레이드 시 확인 권장**: `label={{/* ... */}}`와 같은 코드는 `HStack`을 `Fragment` 등으로 대체할 수 있습니다. - **문제 수정**: `CheckSelectBox`와 `RadioSelectBoxItem`에서 사용되지 않는 `children`을 타입 정의에서 제거합니다. - `CheckSelectBoxGroup`에 `label`, `aria-label`, `aria-labelledby` 중 아무것도 설정하지 않은 경우 경고를 표시합니다. (`RadioSelectBoxRoot`는 기존에도 표시) - a0e40ca: [Tag Group](/react/components/tag-group) 관련 컴포넌트를 업데이트합니다. - `TagGroupItem` 레이블 내부에서 줄바꿈이 발생할 수 있도록 수정합니다. (기존: `TagGroupItem` 또는 separator 전후에서 줄바꿈 발생) - 한 줄 레이아웃 및 우선순위 옵션을 추가합니다. - `TagGroupRoot`에 `truncate` prop을 사용하여 한 줄로 유지하고 말줄임 처리를 할 수 있습니다. (기본값: `false`) - `TagGroupItem`에 `flexShrink` prop을 사용하여 말줄임 우선순위를 조정할 수 있습니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `TagGroupItem` 내부 레이블을 `TagGroupItemLabel`로 감싸거나, 신규로 제공되는 Snippet에서 제공하는 API로 교체해주세요. - `npx @seed-design/cli@latest add ui:tag-group` 명령어로 Snippet을 추가할 수 있습니다. ```tsx // 전 import { TagGroupRoot, TagGroupItem } from "@seed-design/react"; { /* TagGroup.Root, TagGroup.Item처럼 namespace import하는 코드가 있을 수 있습니다. */ } } /> 서초4동 광고 } color="fg.brand" /> {/* ... */} ; ``` ```tsx // 후 (Compound Component 유지) import { TagGroupRoot, TagGroupItem, TagGroupItemLabel, } from "@seed-design/react"; } /> {/* TagGroupItemLabel 사용 */} 서초4동 {/* TagGroupItemLabel 사용 */} 광고 } color="fg.brand" /> {/* ... */} ; ``` ```tsx // 후 (snippet API로 교체) // snippet 없는 경우, `npx @seed-design/cli@latest add ui:tag-group` import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; import { TagGroupRoot as SeedTagGroupRoot, TagGroupItem as SeedTagGroupItem, TagGroupItemLabel as SeedTagGroupItemLabel, } from "@seed-design/react"; } /> 광고 {/* 아이콘 커스터마이징이 필요한 경우 snippet 대신 Compound Component를 사용합니다. */} } color="fg.brand" /> {/* ... */} ; ``` - 358a1e4: [Menu Sheet](/react/components/menu-sheet) 관련 컴포넌트를 업데이트합니다. - `MenuSheetContent`에 설명을 추가할 수 있는 `description` prop이 추가되었습니다. - `MenuSheetItem`에 설명을 추가할 수 있는 `description` prop이 추가되었습니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: snippet을 다시 내려받고, `MenuSheetItem`을 사용하는 코드를 아래와 같이 변경하세요. - `npx @seed-design/cli@latest add ui:menu-sheet` - `children` 대신 `label` prop을 사용합니다. - `description`, `prefixIcon` prop이 추가되었습니다. ```tsx // 전 } /> 메뉴 항목 // 후 } label="메뉴 항목" description="이제 설명도 추가할 수 있어요" /> ``` ### Patch Changes - 477ec8a: [`Grid` 및 `GridItem`](/react/components/layout/grid) 레이아웃 유틸리티 컴포넌트를 추가합니다. - 8fb7038: [BottomSheetContent](/react/components/bottom-sheet)와 [MenuSheetContent](/react/components/menu-sheet)가 기본적으로 bottom safe area만큼 하단 padding을 갖도록 수정합니다. - 별도로 safe area padding을 지정하는 경우 제거할 수 있습니다. BottomSheetContent 내부에서의 `` 등 - 17c0ebd: Text Field (text-input)과 Field Button (input-button)의 포커스 및 에러 상태 border 트랜지션을 수정합니다. ## 1.1.19 ### Patch Changes - 6697fbe: BottomSheetRoot `headerAlign="center"` variant에서 텍스트 정렬 문제를 수정합니다 ([예제](/react/components/bottom-sheet#header-align)). MenuSheetTitle의 내용이 길어질 때 MenuSheetHeader가 가운데 정렬되지 않는 문제를 수정합니다. ## 1.1.17 ### Patch Changes - db49a84: Chip 컴포넌트 스펙에 `layout=withText` variant를 명시합니다. (스타일 변경사항 없음) - 6fab0e7: Skeleton 가시성 향상을 위해 `$gradient.shimmer-magic` 및 `$gradient.shimmer-neutral` 토큰의 색상을 업데이트합니다. - 5faef3a: 주석, 참고 사항 및 상세 리스트 등 부가 정보에 사용할 수 있는 시맨틱 텍스트 스타일 `articleNote`를 추가합니다. - 50ee0a6: `@seed-design/css@1.3` 및 `@seed-design/react@1.3`에서 제거되는 토큰 및 옵션에 관한 경고를 추가합니다. - 1.3에서 제거 예정인 색상 토큰 - [`$color.bg.layer-fill`](/docs/foundation/design-token/%24color.bg.layer-fill) - [`$gradient.fade-layer-floating`](/docs/foundation/design-token/%24gradient.fade-layer-floating) - [`$gradient.fade-layer-default`](/docs/foundation/design-token/%24gradient.fade-layer-default) - 1.3에서 제거 예정인 컴포넌트 variant - [ChipTabs](/react/components/chip-tabs) - `variant="brandSolid"` - 1.2까지 사용 가능, 1.3부터 디자인 변경 필요 - [Checkbox](/react/components/checkbox) - `weight="default"` - 0.2.4부터 `weight="regular"` 사용 가능 - `weight="default"`는 1.2까지 사용 가능, 1.3부터 `weight="regular"`만 허용 - `weight="stronger"` - 0.2.4부터 `weight="bold"` 사용 가능 - `weight="stronger"`는 1.2까지 사용 가능, 1.3부터 `weight="bold"`만 허용 - [Switch](/react/components/switch) - `size="small"` - 0.1.9부터 `size="16"` 사용 가능 - `size="small"`은 1.2까지 사용 가능, 1.3부터 `size="16"`만 허용 - `size="medium"` - 0.1.9부터 `size="32"` 사용 가능 - `size="medium"`은 1.2까지 사용 가능, 1.3부터 `size="32"`만 허용 - `StyleProps`를 상속하는 컴포넌트 - `display`, `justifyContent/justify`, `alignItems/align`, `alignContent`, `alignSelf`, `flexDirection/direction` 프로퍼티에서의 `camelCase` 값 제거 예정 - 0.0.15부터 `kebab-case` 값 사용 가능 - `camelCase` 값은 1.2까지 사용 가능, 1.3부터 `kebab-case` 값만 허용 - 예: `justifyContent="spaceBetween"` → `justifyContent="space-between"` - 영향 범위인 컴포넌트: [Box](/react/components/layout/box), [Flex](/react/components/layout/flex), [HStack](/react/components/layout/h-stack), [VStack](/react/components/layout/v-stack), [Article](/react/components/article), [List (List.Root) 및 ListItem, ListButtonItem, ListLinkItem, ListSwitchItem, ListCheckItem, ListRadioItem (List.Item)](/react/components/list), [BottomSheetBody (BottomSheet.Body)](/react/components/bottom-sheet), ResponsivePair, [Inline (deprecated)](/react/components/inline), [Columns, Column (deprecated)](/react/components/columns), [Stack (deprecated)](/react/components/stack) - 94bebf8: `$color.bg.layer-basement` 위에서 컴포넌트의 가시성을 보장하기 위해 `$color.bg.neutral-weak-alpha` 토큰을 추가합니다. - Chip `variant=solid`에 적용 - ChipTab `variant=neutralSolid`에 적용 - SegmentedControl root에 적용 - 8495fae: 잘못된 pseudo selector를 수정합니다 ## 1.1.16 ### Patch Changes - 2f29fe8: 정적 텍스트 스타일 추가 (t1Static* ~ t10Static*) - 9119723: Checkmark `variant=ghost` `tone=neutral`에서 icon의 색상을 $color.fg.neutral로 변경하고 색상 트랜지션을 추가합니다. - 6d30b72: Stackflow와 함께 AppScreen 사용 시 최상위 AppScreen이 push/pop될 때, 이외의 AppScreen이 고유한 `transitionStyle`을 재생하는 문제를 수정합니다. 같은 스택 내에 여러 `transitionStyle`이 공존할 때 자연스러운 트랜지션을 제공하기 위해 최상위 AppScreen의 `transitionStyle`을 재생합니다. ([데모](https://seed-design.io/react/stackflow/app-screen#transition-styles)) - 예를 들면, `transitionStyle="fadeFromBottomAndroid"`인 0번 AppScreen 위에 `transitionStyle="slideFromLeftIOS"`인 1번 AppScreen이 push되는 경우, 0번 AppScreen이 `slideFromLeftIOS` 트랜지션을 재생하도록 수정합니다. - 0번 AppScreen이 자연스럽게 좌측으로 조금 밀려나며 어두워지고(`slideFromLeftIOS`) 1번 AppScreen이 우측에서 슬라이드 인(`slideFromLeftIOS`) - 10c0765: 배너 템플릿에 사용되는 `$color.banner.*` 색상 토큰을 추가합니다. - 5e462db: `AppScreen`에 신규 `transitionStyle` `fadeIn`을 추가합니다. ## 1.1.15 ### Patch Changes - 76acd7e: iOS 15 이하 기기에서 MenuSheetItem이 의도하지 않은 margin을 갖는 문제를 수정합니다. - 7a428ec: `theme=“android”`인 AppScreen 또는 AppBar에 속한 AppBarMain의 title과 subtitle 말줄임(truncation)이 적용되지 않는 문제를 수정합니다. - 498a9e7: iOS 17 이하 기기에서 SegmentedControlItem의 높이가 잘못 설정될 수 있는 문제를 수정합니다. ## 1.1.13 ### Patch Changes - 9be0581: `radiomark` Rootage 정의를 명확화합니다. (스타일 변경사항 없음) - cc4a45a: 신규 [Elevation 가이드](https://seed-design.io/docs/foundation/elevation)에 맞는 shadow 토큰을 추가합니다. - React: Box, Flex, HStack 등 StyleProps를 사용하는 컴포넌트에서 `boxShadow` prop을 사용하여 shadow 토큰을 쉽게 사용할 수 있습니다. - 739937f: Button들의 xsmall variant의 텍스트 사이즈를 t4에서 t3로 변경해요. ## 1.1.12 ### Patch Changes - 8d0ad90: seedFontMultiplier, seedFontScaling attribute를 추가합니다 - seedFontMultiplier: 폰트 크기 스케일링 계수 (e.g. 0.8, 1.35, 1.5, 1) - seedFontScaling: 폰트 크기 스케일링 활성화 여부 (e.g. "enabled") - 8f31f93: cssnano 등 CSS 최적화 도구를 사용할 때, `bleedX`와 `bleedY` 중 한 가지 속성만 정의하는 경우 bleed가 적용되지 않던 문제를 수정합니다. - 69ccc6e: Overlay 컴포넌트에 skipAnimation 옵션을 추가합니다 - 279001a: Badge의 크기와 Field의 indicator 크기 및 여백이 폰트 스케일링 제한의 영향을 받도록 업데이트합니다. ## 1.1.10 ### Patch Changes - db5de74: PageBanner에 tone="magic" 스타일을 추가합니다. - 70d11b8: Segmented Control의 스타일을 업데이트합니다: Indicator에서 shadow 정의를 제거합니다. - c03a3dd: TextFieldInput 및 TextFieldTextarea (text-input 스타일시트)에서 브라우저 기본 자동 완성 스타일이 표시되지 않도록 수정합니다. - a12e49b: Field(TextField)의 스타일을 수정합니다. - `maxGraphemeCount`를 사용하지만 `description`을 사용하지 않는 경우 `maxGraphemeCount`가 우측이 아닌 좌측에 표시되는 문제를 수정합니다. - Tailwind Preflight 사용 시 Character Count 영역이 디자인 의도보다 높이를 더 많이 차지하는 문제를 수정합니다. ## 1.1.9 ### Patch Changes - 37d332d: `AppBar`에 적용되는 그라디언트를 `AppScreen`으로 이동합니다. - ``를 사용하는 경우 `tone="transparent"`를 `AppScreen`으로 옮겨 그라디언트를 표시할 수 있습니다. - `AppScreen`에 `gradient={false}`를 설정하여 그라디언트를 숨길 수 있습니다. - 77517f1: iOS 웹뷰 환경에서 Avatar가 정상적으로 렌더링되지 않는 문제를 수정합니다. ## 1.1.8 ### Patch Changes - 8752805: List Item에 신규 active(pressed) 스타일을 적용하고, disabled 상태에서 detail 영역의 색상을 수정합니다. - ee98674: Help Bubble이 기본적으로 z-index 99를 가지도록 수정합니다. (HelpBubblePositioner 또는 HelpBubblePositionerPortal에 직접 인라인 스타일을 적용한 경우 인라인 스타일이 우선 적용됩니다.) ## 1.1.7 ### Patch Changes - bee919c: 스타일시트에서 logical property(padding-block 등)를 physical property(padding-left 및 padding-right 등)로 교체합니다. (브라우저 지원) - 1340675: Slider Value Indicator가 표시되는 조건을 설정하는 `valueIndicatorTrigger` prop을 추가합니다. ("active"|"hover", 기본값: "active") - 1340675: Slider Value Indicator가 Track 양 끝에 있을 때 Track 바깥 영역을 차지하지 않도록 수정합니다. ## 1.1.6 ### Patch Changes - dfe6c1e: transparent 상태 컬러 추가, 컴포넌트 상태 컬러 변경, transition 추가 - `$color.bg.transparent-pressed` 컬러와 `$color.bg.transparent` 컬러가 추가되었습니다. - 다음 컴포넌트들의 색상이 transparent 관련 토큰으로 변경되었습니다. - `Chip` (outlineStrong, outlineWeak) - `Action Button` (neutralOutline, brandOutline, ghost) - `Checkmark` - `Tabs` (outline) - `List Item` - `Radiomark` - `Reaction Button` - `Select Box` - 다음 컴포넌트들의 color transition이 추가되었습니다. (duration: $duration.d3, timing-function: $timing-function.easing) - `Checkmark` - `Radiomark` - `Reaction Button` - `Select Box` - a09e6b4: 다음 컴포넌트들의 color transition을 `$duration.color-transition` (d3) 토큰으로 변경합니다 - `Action Button` - `Bottom Sheet Handle` - `Checkmark` - `Tabs` (outline) - `Chip` - `Contextual Floating Button` - `Floating Action Button` - `Input Button` - `List Item` - `Radiomark` - `Reaction Button` - `Segmented Control Item` - `Select Box` - `Text Input` - `Toggle Button` ## 1.1.5 ### Patch Changes - 53290ab: FieldButton에 Read Only 상태를 추가합니다. Disabled 상태인 FieldButton은 내부 ``도 `disabled` 속성을 갖도록 수정합니다. ## 1.1.4 ### Patch Changes - 795668c: Stackflow의 AppScreen에 있던 gradient 스타일을 AppBar로 이동합니다 ## 1.1.3 ### Patch Changes - 15c658b: 일반적인 CSS Reset을 사용하지 않는 환경에서도 스타일이 의도한 대로 표시될 수 있도록 수정합니다. - `list-header`에 `box-sizing: border-box;` 추가 - `button`으로 렌더링되는 컴포넌트 스타일 수정 - f4e07bb: 1.1 이전 버전과 호환 가능하도록 임시적으로 사용할 text-input size=medium variant를 추가합니다. - 영향 받는 React 컴포넌트: TextFieldInput, TextFieldTextarea - 114dafd: text-input의 readonly 스타일을 업데이트합니다. - 영향 받는 React 컴포넌트: TextFieldInput, TextFieldTextarea - bc3cd6f: ScrollFog 컴포넌트를 추가합니다 ## 1.1.0 ### Minor Changes - a55f584: Slider 컴포넌트를 추가합니다. - 191005f: Action Button 컴포넌트를 `variant=ghost`로 사용하는 경우 `fontWeight`를 사용자화할 수 있도록 업데이트합니다. (BREAKING CHANGE: Error State snippet을 다시 설치해야 합니다.) Error State 스니펫에서 Action Button을 활용하도록 업데이트합니다. - 33def2d: (BREAKING CHANGE: BottomSheet snippet을 다시 설치해야 합니다.) BottomSheet에 드래그를 통해 닫는 기능을 추가합니다. - vaul headless 코드 기반으로 seed에 맞게 커스텀하여 구현했습니다. - vaul과 동일한 인터페이스를 가지고 있습니다. (snap-points, fade-from-index, etc.) - `npx @seed-design/cli@latest add ui:bottom-sheet`로 snippet을 최신화하세요. ### Patch Changes - d6bb84d: (BREAKING CHANGE: TextField snippet을 다시 설치해야 합니다.) Text Field 관련 컴포넌트를 업데이트합니다. - 스타일 업데이트 - size 통일 및 variant (underline) 추가 - 내부적으로 Field 컴포넌트를 사용하도록 변경하여 스타일 일관성 향상 Field Button 컴포넌트를 추가합니다. - b131282: AppScreen에 `tone` 속성을 조절해 그라데이션이 들어간 투명한 배경을 사용할 수 있습니다. - AppBar에 있던 `tone` 속성을 AppScreen에서도 사용할 수 있도록 이동합니다. - AppScreen, AppBar 둘 다 `tone` 속성을 사용할 수 있도록 합니다. - 6af6501: (BREAKING CHANGE: PageBanner snippet을 다시 설치해야 합니다.) Page Banner 스니펫을 업데이트합니다. - Box를 사용하여 스타일링하던 부분을 `PageBanner.Body`로 교체합니다. - `PageBanner.TextContent`를 `PageBanner.Content`로 이름 변경합니다. ## 1.0.7 ### Patch Changes - e52d6d1: Article에서 selection 색상에 대한 정의를 제거합니다. - 97669bc: Tailwind Preflight 사용 시 Page Banner가 디자인 의도와 다르게 표시되는 문제를 수정합니다. - 15ab93a: List Item 컴포넌트의 상하 여백을 `$dimension.x2_5`에서 `$dimension.x3`로 늘립니다. - 50366c0: Tailwind Preflight 사용 시 Text Field의 Header/Footer와 Callout이 디자인 의도와 다르게 표시되는 문제를 수정합니다. ## 1.0.6 ### Patch Changes - 6aafce0: Tag Group 컴포넌트를 추가합니다. Tag Group은 아이콘 및 텍스트로 이루어진 태그를 구분 기호와 함께 수평 레이아웃으로 표시하는 컴포넌트입니다. - 1902dfa: AppBar의 스타일을 업데이트합니다. - Top Navigation의 title 및 description에 `lineHeight` 값을 정의합니다. (React AppBar 컴포넌트에 반영됩니다.) - `` 컴포넌트를 활용하여 `` 내부 아이콘을 커스터마이징할 수 있도록 수정합니다. - f2ddf29: Article 유틸리티 컴포넌트를 추가하고 Text 컴포넌트를 업데이트합니다. - Article 컴포넌트는 일관된 selection 스타일 및 줄바꿈 정책을 사용할 수 있게 돕습니다. - Text 컴포넌트에서 textDecorationLine="underline" 및 whiteSpace, userSelect prop을 지원합니다. - 4c33f07: Switch가 checked 상태가 아닐 때 thumb 크기를 줄여 상태를 인지하기 쉽도록 합니다. enabled-disabled 상태 간 트랜지션을 추가합니다. - 3df657f: Switch와 Switch Mark의 disabled 상태를 더 잘 구별할 수 있도록 스타일을 수정합니다. ## 1.0.5 ### Patch Changes - f1cf4cd: Text Field와 Multiline Text Field가 기본적으로 배경 색을 갖지 않도록 수정합니다. - 9b91751: AppScreen에서 스와이프로 pop할 때 AppScreen이 한번 깜빡거리고 닫히는 버그를 수정합니다. - 3898183: 매너온도 L9 전경 및 배경 색상의 채도를 낮춥니다. ## 1.0.3 ### Patch Changes - 0b8a02e: HelpBubble의 스타일을 업데이트하고, 신규 기능을 지원합니다. - arrowTip이 content에서 떨어져 보이던 문제를 수정합니다. - title과 description에서 `\n`을 줄바꿈으로 렌더링하도록 수정합니다. - `closeOnInteractOutside`를 `false`로 설정하여 Help Bubble 외부와 상호작용 시에도 닫히지 않도록 설정할 수 있습니다. (기본값: `true`) - 6c6099d: Callout에 tone=positive variant를 추가합니다. ## 1.0.2 ### Patch Changes - 6d2e13d: MannerTemp 컴포넌트가 레이아웃에서 너비를 덜 차지하도록 업데이트합니다. ## 1.0.1 ### Patch Changes - 1420b68: MannerTemp 컴포넌트가 레이아웃에서 높이를 덜 차지하도록 업데이트합니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Minor Changes - 39a96f1: (**BREAKING CHANGE**: Snackbar Snippet을 다시 설치해야합니다) Snackbar 컴포넌트 변경 - Snackbar의 배경색이 다크모드에서 흰색으로 변경됩니다. - Prefix 요소유무에 따라 여백이 변경됩니다. - `npx @seed-design/cli@latest add ui:snackbar` 명령어로 설치하세요. ### Patch Changes - e038490: (**BREAKING CHANGE**: Snippet을 다시 설치해야 합니다.) Manner Temp, Manner Temp Badge 컴포넌트를 업데이트합니다. - snippet 내 오타 수정 - 신규 10단계 반영 - 업데이트 가이드 1. `@seed-design/css@latest @seed-design/react@latest` 설치 2. `npx @seed-design/cli@latest add ui:manner-temp ui:manner-temp-badge`로 snippet 최신화 3. 온도 범위가 변경되었으므로, `` 혹은 ``과 같이 `level`을 직접 지정하여 사용하고 있는 경우가 있는지 확인 - 4153ca5: HelpBubble 컴포넌트의 배경색이 다크모드에서 흰색으로 변경됩니다. - a7d07f0: (**BREAKING CHANGE**: `SwitchMark` 사용을 위해서는 Snippet을 다시 설치해야 합니다.) Switch의 토글 영역만을 정의한 Switch Mark 컴포넌트를 추가합니다. - `npx @seed-design/cli@latest add ui:switch` 명령어로 설치하세요. (**BREAKING CHANGE**: `ListHeader` 사용을 위해서는 Snippet을 다시 설치해야 합니다.) List Header 컴포넌트를 추가합니다. - `npx @seed-design/cli@latest add ui:list` 명령어로 설치하세요. ## 0.2.5 ### Patch Changes - 0ca19c0: Segmented Control 컴포넌트를 업데이트합니다. - Notification Badge를 표시하는 notification prop을 추가합니다. - `SegmentedControlItem`의 `children`을 `string`에서 `ReactNode`로 확대합니다. - 스타일을 업데이트합니다. ## 0.2.4 ### Patch Changes - 8ebe8a5: Switch, Checkmark, Radio Mark의 스타일을 업데이트합니다. - tone=neutral variant를 추가합니다. - Switch의 thumb 크기를 조정합니다. Checkbox와 Radio의 weight variant를 default, stronger에서 regular, bold로 수정합니다. - f61b80d: 다크 모드에서의 색상 대비 보장을 위해 시맨틱 색상을 수정하고 컴포넌트에서의 색상을 변경합니다. - **$color.bg.warning-solid**: theme-dark에서 $color.palette.yellow-600 → $color.palette.yellow-800 - **$color.bg.warning-solid-pressed**: theme-dark에서 $color.palette.yellow-700 → $color.palette.yellow-900 - Badge, Page Banner의 tone=warning, variant=solid variant에서 전경 항목 색상 변경: $color.fg.neutral → $color.palette.static-black-alpha-900 ## 0.2.3 ### Patch Changes - a22b8b9: ChipTabs 컴포넌트 Variant, Size 변경 및 디자인 수정 - variant `neutralOutline` 추가 - variant `brandSolid` deprecated - size(`medium(default)` | `large`) 추가 - 5836976: text-field의 value slot이 input의 size attribute로 인해 기본적으로 width를 가지는 문제를 해결합니다. - 12faf5a: List 컴포넌트를 추가하고, Checkbox 및 Radio 컴포넌트를 개선합니다. - List 컴포넌트를 제공하여, 정보를 구조화된 목록 형태로 표시할 수 있도록 합니다. - Checkbox와 Radio의 컨트롤 영역만을 표시하는 Checkmark와 RadioMark를 제공합니다. - Select Box에서 컨트롤 영역을 Checkmark와 RadioMark로 교체합니다. - RadioGroup 컴포넌트를 제공합니다. ## 0.2.1 ### Patch Changes - 35984d0: Chip 컴포넌트를 업데이트합니다. - 아이콘에 트랜지션 효과가 적용되지 않던 현상을 수정합니다. - Button, Toggle 등 사용되는 방식에 따라 적절한 data prop을 받도록 수정합니다. ## 0.2.0 ### Minor Changes - 8448880: 시맨틱 stroke 컬러 토큰을 업데이트합니다. **이름이 변경되는 stroke 토큰** - [Color Role 규칙](https://seed-design.io/docs/foundation/color/color-role)에 맞춰 일관적인 토큰 이름을 유지할 수 있도록 업데이트합니다. - 이름이 변경되는 stroke 토큰을 사용하고 있는 경우, 간단한 Find & Replace 마이그레이션이 필요합니다. | 기존 | 신규 | 비고 | | ------------------------------- | ------------------------------- | ---------------------------------- | | **$color.stroke.neutral-muted** | $color.stroke.neutral-subtle | 가장 먼저 마이그레이션해야 합니다. | | $color.stroke.on-image | $color.stroke.neutral-subtle | | $color.stroke.neutral | **$color.stroke.neutral-muted** | | $color.stroke.field-focused | $color.stroke.neutral-contrast | | $color.stroke.control | $color.stroke.neutral-weak | | $color.stroke.field | $color.stroke.neutral-weak | | $color.stroke.brand | $color.stroke.brand-weak | | $color.stroke.positive | $color.stroke.positive-weak | | $color.stroke.informative | $color.stroke.informative-weak | | $color.stroke.warning | $color.stroke.warning-weak | | $color.stroke.critical | $color.stroke.critical-weak | **색상이 변경되는 stroke 토큰 (마이그레이션 불필요)** `$color.stroke.neutral-contrast` (이름 변경 전 `$color.stroke.field-focused`) 모든 theme mode에서 `$color.palette.gray-800` → `$color.palette.gray-1000`로 변경되었습니다. **신규 stroke 토큰 (마이그레이션 불필요)** | 신규 | | ------------------------------- | | $color.stroke.neutral-solid | | $color.stroke.brand-solid | | $color.stroke.positive-solid | | $color.stroke.informative-solid | | $color.stroke.warning-solid | | $color.stroke.critical-solid | ## 0.1.15 ### Patch Changes - c51a261: font-size, line-height 토큰에 static variant를 추가합니다. - `--seed-font-size-t1-static` ~ `--seed-font-size-t10-static` - `--seed-line-height-t1-static` ~ `--seed-line-height-t10-static` - 5f2ee39: CSS 최적화 도구(e.g. cssnano)가 CSS variable로 정의된 longhand declaration을 병합하지 않도록 합니다. (workaround, [관련 issue](https://github.com/cssnano/cssnano/issues/1472)) - 8299ba9: Snackbar 컴포넌트를 업데이트합니다. - root 영역에 maxWidth 스펙을 추가합니다. - `pauseOnInteraction`의 기본값을 `false`에서 `true`로 변경합니다. - 3de4cec: 플랫폼별 조건부 폰트 스케일링 제한 (iOS: 135%, Android: 150%) 적용 - CSS 변수 `--seed-{font-size|line-height}-limit-{min|max}` 도입 - 빌드 타임 basePx 계산을 런타임 static 토큰 참조로 대체 - global.ts에 폰트 스케일링 변수 통합 ## 0.1.14 ### Patch Changes - f806356: Page Banner 컴포넌트를 추가합니다. Inline Banner 컴포넌트를 deprecate합니다. - Inline Banner 컴포넌트 대비 모든 `tone`에서 모든 `variant`를 지원하며, 내부 Button의 충분한 터치 영역을 보장합니다. ```tsx 새 탭에서 열기 } /> ``` 시맨틱 색상 토큰을 추가하고 수정합니다. - `$color.bg.positive-solid-pressed`: theme-dark에서 `$color.palette.green-500` → `$color.palette.green-600` - `$color.bg.warning-solid-pressed` 추가 - 1982494: Badge 컴포넌트를 업데이트합니다. - `tone=warning` variant를 추가합니다. - `maxWidth` 스펙을 추가합니다. 신규 시맨틱 색상 토큰을 추가합니다. - `$color.fg.warning` - `$color.stroke.warning` - `$color.fg.brand-contrast` - `$color.bg.brand-weak` - `$color.bg.brand-weak-pressed` ## 0.1.13 ### Patch Changes - 0be9b00: Avatar, Avatar Stack 컴포넌트에 `size=108` variant를 추가합니다. ## 0.1.12 ### Patch Changes - 62094b6: Help Bubble의 스타일 문제를 수정합니다. - `placement=left-*` / `placement=right-*`에서 arrow가 content와 떨어져 표시되는 문제를 수정합니다. ## 0.1.10 ### Patch Changes - ef91c21: Bottom Sheet의 스타일 문제를 수정해요. - Close Button에 브라우저 기본 스타일이 표시되는 문제를 수정해요. ## 0.1.9 ### Patch Changes - 5a025b7: Switch 컴포넌트를 업데이트합니다. - size: medium → 32, small → 16으로 rename합니다. - (React) `size="medium"`으로 `32`, `size="small"`로 `16`을 사용할 수 있습니다. (deprecated) - size: 24를 추가합니다. - 모든 size에 대해 레이블 스타일을 추가합니다. (기존: small에만 존재) - ac35731: Chip.Root `position: relative` 속성 추가 - 이제 Chip.Toggle을 사용해도 예상치 못한 스크롤이 발생하지 않습니다. - f9041e9: `CheckSelectBox`, `RadioSelectBox`의 `label`, `description` 영역을 수정합니다. - `span` 대신 `div`를 렌더링합니다. - 기본적으로 grow하도록 만들어 Badge 등 추가 요소를 넣기 쉽게 만듭니다. ## 0.1.8 ### Patch Changes - 609b8f3: iOS의 `더 큰 텍스트` 기능에 제한을 둡니다. - iOS는 7단계(XS ~ XXXL)의 텍스트 크기 조절 이외에도, 보다 더 큰 텍스트를 위한 `더 큰 텍스트` 기능을 제공합니다. - iOS 네이티브에서는 `더 큰 텍스트`의 UI 레이아웃 대응이 어렵다고 결정하여, XXXL(135%) 이상의 텍스트 크기 조절을 지원하지 않습니다. - 웹뷰도 iOS와 동일한 제한을 위한 기능이 추가되었습니다. ## 0.1.7 ### Patch Changes - 4afe80b: MultilineTextField의 스타일 문제를 수정합니다. - 스크롤바가 요소 끝에 표시되도록 수정합니다. ## 0.1.6 ### Patch Changes - 235147d: action-button: `size=medium, layout=withText` variant에서 gap을 1 → 1.5로 수정합니다. - 3c13ad7: `highlight-magic-pressed` 그라디언트 토큰을 추가합니다. ## 0.1.5 ### Patch Changes - 861ecb4: Menu Sheet 컴포넌트를 추가하는 동시에 Action Sheet과 Extended Action Sheet 컴포넌트를 deprecate합니다. - [Menu Sheet React 문서](https://seed-design.io/react/components/menu-sheet) - Menu Sheet는 기존 Extended Action Sheet의 모든 기능을 포함하는 동시에, `labelAlign` prop으로 `MenuSheetItem`를 `left` 또는 `center`로 정렬할 수 있습니다. - 3889eb6: Inline Banner의 스타일 문제를 수정합니다. - `title`과 `description`이 `inline-flex`로 레이아웃되던 문제를 해결합니다. - `title`과 `description` 간의 간격을 조정합니다. - 닫기 버튼(Dismissible)과 `suffix icon`, `link label`이 상단으로 레이아웃되던 문제를 해결합니다. Callout의 스타일 문제를 수정합니다. - `title`과 `description` 간의 간격을 조정합니다. Chip의 스타일 문제를 수정합니다. - `Chip.Button`의 `label`이 의도한 글꼴로 표시되도록 수정합니다. ## 0.1.4 ### Patch Changes - 0ffcd48: Chip 컴포넌트가 추가되고, ActionChip, ControlChip 컴포넌트가 Deprecated 되었습니다. - [Chip 컴포넌트](https://seed-design.io/react/components/chip) - Chip 컴포넌트는 버튼과 토글 컴포넌트를 모두 포함하고 있습니다. ## 0.1.3 ### Patch Changes - cdc0930: `@seed-design/stackflow` 백스와이프 애니메이션 개선 - iOS 스타일 화면 전환 애니메이션의 지속 시간과 타이밍 함수가 `300ms`에서 `350ms`로 조정되어 더 부드러운 전환 효과를 제공합니다. - 스와이프 백 제스처 시 애니메이션이 보다 자연스럽고 일관되게 표현됩니다. - 스와이프 백 종료 시 CSS 변수를 활용해 전환 상태를 명확히 하여 사용자 경험이 개선되었습니다. - 946faf7: 그라디언트 토큰 추가 및 변경 - `fade-layer-floating`, `fade-layer-default` 토큰이 추가되었습니다. - `$gradient.shimmer-magic` 토큰 stop color가 변경되었습니다. - 71c58fd: iOS Font Scaling - iOS 기기에서 시스템 폰트 크기 설정에 따라 동적으로 폰트 크기와 줄 높이를 조정하는 폰트 스케일링 옵션이 추가되었습니다. - 플러그인(webpack, vite, rsbuild)에서 `fontScaling` 옵션을 통해 폰트 스케일링 기능을 활성화할 수 있습니다. - `data-seed-font-scaling='enabled'` 일 때, 폰트 크기를 조정합니다. ## 0.1.2 ### Patch Changes - 7b2c0f3: Updated dependencies - @seed-design/react@0.1.1 ## 0.1.1 ### Patch Changes - e3b782d: `stroke.neutral`, `stroke.neutral-muted`, `stroke.on-image`의 컬러를 alpha 값으로 변경합니다. ## 0.1.0 ### Minor Changes - 7cc6087: HelpBubble의 arrow가 상위 요소의 font-size에 영향을 받는 것을 수정합니다 - bdca898: BottomSheet의 description font-size를 t5로 변경합니다 ## 0.0.41 ### Patch Changes - 561f74c: Text 컴포넌트에 `textDecorationLine` 옵션을 추가합니다. - b43de05: Gradient 컬러를 추가합니다 ## 0.0.39 ### Patch Changes - f801300: 새로운 black, white alpha 값을 추가합니다 `$color.palette.static-black-alpha-50` (예전 값) - 예전 값: #0000000d (투명도 약 5.1%) - 변경 값: `$color.palette.static-black-alpha-200` (투명도 4.7%) `$color.palette.static-black-alpha-200` (예전 값) - 예전 값: #00000033 (투명도 20%) - 변경 값: `$color.palette.static-black-alpha-500` (투명도 17.3%) `$color.palette.static-black-alpha-500` (예전 값) - 예전 값: #00000080 (투명도 약 50.2%) - 변경 값: `$color.palette.static-black-alpha-700` (투명도 45.5%) `$color.palette.static-white-alpha-200` (예전 값) - 예전 값: #ffffff33 (투명도 20%) - 변경 값: `$color.palette.static-white-alpha-300` (투명도 18%) `$color.palette.static-white-alpha-800` (예전 값) - 예전 값: #ffffffcc (투명도 약 80%) - 변경 값: `$color.palette.static-white-alpha-800` (투명도 87.1%) - (참고: 이 값은 이름은 같지만 실제 투명도 값은 80%에서 87.1%로 변경되었습니다.) ## 0.0.38 ### Patch Changes - 70fbaaf: Action Button에 type="ghost"를 추가합니다. ## 0.0.35 ### Patch Changes - 0789dc8: `_active` style prop이 값이 없는 경우에 기존 style prop을 제거하는 버그를 수정합니다. ## 0.0.34 ### Patch Changes - 92801a2: `_active` style prop이 상태가 없는 값보다 우선순위가 낮게 적용되는 문제를 수정합니다. ## 0.0.33 ### Patch Changes - fbdb091: Style prop에 `_active`를 추가합니다. background 속성만을 지원합니다. ## 0.0.31 ### Patch Changes - fd7c569: - Tabs.Carousel을 사용하는 경우 Hydration 이후 스크롤 애니매이션이 발생하는 문제를 수정합니다. - Tabs.Carousel의 드래그 제스처를 방지하는 영역을 선언할 수 있는 `Tabs.carouselPreventDrag` api를 추가합니다. - layout=hug일 때 Indicator에서 발생하는 Layout Shift를 수정합니다. - lazyMount 옵션이 의도와 다르게 모든 탭이 한꺼번에 마운트되는 문제를 수정합니다. ## 0.0.30 ### Patch Changes - 285cb9b: - `ContextualFloatingButton`과 `FloatingActionButton` 컴포넌트를 제공합니다. - 기존의 `Fab` 및 `ExtendedFab`를 deprecate합니다. - Floating 요소들의 위치를 편리하게 제어하도록 `Float` 유틸리티 컴포넌트를 제공합니다. ## 0.0.29 ### Patch Changes - 116ee2c: ActionButton의 min-width variable 기본값을 수정합니다. ## 0.0.28 ### Patch Changes - 5337e14: Callout의 wrapping 동작을 수정합니다. ## 0.0.27 ### Patch Changes - 9d85c16: InlineBanner의 title 영역에 flex-shrink: 0을 추가해요 - d951317: Color 토큰을 업데이트합니다. - b3f964d: Avatar의 디자인 업데이트를 반영합니다. (stroke 추가) ## 0.0.25 ### Patch Changes - c87ede9: Avatar Stack의 디자인을 업데이트합니다. ## 0.0.24 ### Patch Changes - 4da536f: ActionSheet의 header가 렌더링되지 않을 때 상단 radius가 누락되는 버그를 수정합니다. ## 0.0.23 ### Patch Changes - 63e1541: AppBar의 배경이 상단 safe-area를 덮도록 수정합니다. ## 0.0.21 ### Patch Changes - 5d69d1d: Button, Chip 컴포넌트들의 누락된 line-height를 추가합니다. Button, Chip 컴포넌트들의 white-space를 nowrap으로 설정합니다. - 4d34760: 상단 내비게이션의 아이콘 버튼 터치영역을 44px로 변경합니다. - 7ae87f8: 2개의 컨텐츠를 동일한 비율로 나누어 배치하되, 너무 긴 경우 세로로 접는 `` 컴포넌트를 추가합니다. - f144d28: BottomSheet, Dialog의 배경 색상을 layer-floating으로 변경합니다. - e368c69: 패키지 의존성을 최신화합니다. ## 0.0.19 ### Patch Changes - 3c9ec66: feat: 와일드카드 지원하지 않는 곳을 위해 CSS 파일 명시적 export - b3bb6e7: LoadingIndicator를 사용하는 컴포넌트에 position: relative를 추가합니다. ## 0.0.17 ### Patch Changes - c042f90: recipe에서 직접 스타일시트 의존성을 표현하도록 변경합니다. ## 0.0.15 ### Patch Changes - 1bb9f7b: - vite dev에서 컴포넌트 스타일시트가 로드되지 않는 버그를 수정합니다. - 플러그인이 컴포넌트 스타일시트를 로드하는 방식을 변경합니다. - 4511814: - 레이아웃 및 flex 관련 shorthand prop을 추가합니다. (px, py, wrap, align, justify, direction) - ActionButton에 flexGrow prop을 추가합니다. - VStack, HStack 컴포넌트를 추가합니다. - Stack, Inline, Columns 컴포넌트를 deprecated 처리합니다. - 디자인 토큰이 아닌 css prop의 value가 유효한 css value가 되도록 변경합니다. - flexStart, spaceBetween 등 camelCase로 제공되는 값을 deprecated 처리합니다. - f4b0723: HelpBubble 디자인 스펙 업데이트 (shadow) - f4b0723: HelpBubble의 enter, exit 모션을 추가합니다. ## 0.0.14 ### Patch Changes - 92c0b80: HelpBubble 디자인 스펙 업데이트 (shadow) - c1d94d0: HelpBubble의 enter, exit 모션을 추가합니다. ## 0.0.13 ### Patch Changes - 7fca755: Avatar의 Badge 스펙을 최신화합니다. ## 0.0.12 ### Patch Changes - 6426379: 유틸리티 컴포넌트에 사용되는 ScopedColorFg, ScopedColorBg, ScopedColorPalette, ScopedColorStroke 타입을 제공합니다. - ee41f37: close button의 위치가 의도와 다르게 설정된 것을 수정합니다. ## 0.0.11 ### Patch Changes - e70f340: Dialog 및 Sheet 컴포넌트 레이아웃 스펙 업데이트 - 72f344f: `$dimension.spacing-y.screen-bottom` 토큰을 추가합니다. `$dimension.spacing-y.between-text` 토큰을 추가합니다. ## 0.0.10 ### Patch Changes - e4b704c: Avatar size=42를 추가합니다. ## 0.0.9 ### Patch Changes - 63f8651: MannerTemp 컴포넌트를 추가합니다. - d9b01a9: feat: 다크모드에서의 gray200, gray300 색상, 라이트모드 carrot700 색상을 변경해요 - (light) carrot700: #e84500 -> #e04f00 - (dark) gray300: #2c2f35 -> #2b2e35 - (dark) gray200: #1b1c22 -> #22252b ## 0.0.8 ### Patch Changes - 1424700: Notification Badge를 추가합니다. - Tabs의 Notification 슬롯을 Notification Badge로 변경합니다. - 0efeea1: change help-bubble paddingY, lineHeight ## 0.0.7 ### Patch Changes - 8aca3de: remove text maxLines none display unset ## 0.0.6 ### Patch Changes - bf198e8: Skeleton의 width, height가 동작하지 않는 버그를 수정합니다. - 3d66c5b: visuallyHidden을 recipe에서 제거합니다. - a8d5242: callout, inline banner 디자인 스펙 수정 - ccf3989: fix: add --seed-safe-area-top in app-bar ## 0.0.5 ### Patch Changes - e3234e7: single-slot recipe를 위한 간소화된 인터페이스를 추가합니다. - 5502bed: add textStyles (t6, t7 regular, medium) ## 0.0.4 ### Patch Changes - 6df5d19: Badge 디자인 업데이트 - neutral tone 색상 변경 - pill shape 삭제 - 5cb50e7: recipe 스타일시트의 exports map을 수정합니다. ## 0.0.3 ### Patch Changes - a33af94: Fixes an issue where the theming script was injecting the wrong color mode data-attr. - b180822: Inject data-seed in theming script instead of plugin ## 0.0.2 ### Patch Changes - d04e344: theming script의 color mode data attribute 수정 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. ## 0.0.1-rc.4 ### Patch Changes - 93cfc30: feat: change theming data attribute names - theming에 사용되는 data attribute 이름을 변경합니다. - 유저가 선호하는 color scheme과 사전에 지정된 color mode를 구분하기 쉽도록 이름을 부여합니다. - 파편화된 platform 관련 네이밍을 통일합니다. - 테마 관련 data attribute가 지정되지 않은 경우 light theme로 fallback하는 동작을 추가합니다. ## 0.0.1-rc.3 ### Patch Changes - cc4b2c5: fix: externalize subpath imports from `@seed-design/css` refactor: streamline package configurations refactor(qvism): generate recipe-shared module from cli ## 0.0.1-rc.1 ### Patch Changes - 14c9983: change package.json exports map ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate --- ## @seed-design/design-token ## 1.0.4 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. ## 1.0.3 ### Patch Changes - 5751b08: fix missing export lineHeight ## 1.0.2 ### Patch Changes - cd0f24e: add types field in exports map ## 1.0.1 ### Patch Changes - 420299b: fix missing letterSpacing scale export ## 1.0.0 ### Major Changes - 1.0.0 Release ## Karrot UI → Seed Design 주요 변경 사항 - 프로젝트/디자인 시스템 명이 **Seed Design**으로 리브랜딩 됩니다. - 패키지 명이 `@seed-design/*` 으로 변경됩니다. - `@karrot-ui/*` packages are deprecated. - `color-scheme: light dark` 지원이 기본값이 됩니다. - 디자인 토큰이 [KDT](https://github.com/daangn/kdt/tree/main/language) 의미론을 따릅니다. --- ## @seed-design/docs-mcp ## 0.5.4 ### Patch Changes - 78a01b2: 아이콘 정보를 최신으로 업데이트합니다. ## 0.5.3 ### Patch Changes - c5012d1: @seed-design/docs-mcp가 MCP SDK의 신규 API를 사용하도록 업데이트합니다. ## 0.5.2 ### Patch Changes - 41cd943: 아이콘 데이터 및 Figma Codegen 컴포넌트 핸들러를 최신 버전으로 업데이트합니다. ## 0.5.1 ### Patch Changes - e92892a: 아이콘 정보를 업데이트합니다. ## 0.5.0 ### Minor Changes - c300110: Tool의 개수를 유지보수 가능한 형태로 줄였습니다 ## 0.4.0 ### Minor Changes - 0315e98: 아이콘 라이브러리 관련 새로운 도구 3개를 추가합니다. (아이콘 목록 조회, 검색, 상세 정보 조회) ## 0.3.0 ### Minor Changes - 63b65db: - `get_rootage` tool 추가 (design token 및 component spec 조회) - React 문서 조회 기능 개선 및 fetch 함수 통합 ## 0.2.0 ### Minor Changes - f385599: `list_foundation`, `get_foundation`, `list_docs_components`, `get_docs_component` tools 추가 ## 0.1.0 ### Minor Changes - 8661d79: `@seed-design/docs-mcp` 패키지 추가 Tools: - `get_react_component` - `get_breeze_component` - `get_react_changelog` - `list_react_components` - `list_breeze_components` - `get_react_changelog` --- ## @seed-design/dom-utils ## 2.0.0 이 패키지의 코드 변경은 없으며, SEED React 2 릴리즈에 맞춰 앞으로의 semver 정합성을 위해 버전을 올립니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ## 0.0.2 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate ## 0.0.0-alpha-20241030023710 ### Patch Changes - alpha ## 0.0.0-alpha-20241004093556 ### Patch Changes - prerelease --- ## @seed-design/figma ## 2.0.0 이 패키지의 코드 변경은 없으며, SEED React 2 릴리즈에 맞춰 앞으로의 semver 정합성을 위해 버전을 올립니다. ## 1.3.18 ### Patch Changes - Updated dependencies [234e8c7] - @seed-design/css@1.2.16 ## 1.3.17 ### Patch Changes - Updated dependencies [26b1de5] - @seed-design/css@1.2.15 ## 1.3.16 ### Patch Changes - Updated dependencies [8dc8f4f] - Updated dependencies [e48f021] - @seed-design/css@1.2.14 ## 1.3.15 ### Patch Changes - Updated dependencies [9cacf29] - @seed-design/css@1.2.13 ## 1.3.14 ### Patch Changes - 546f1fc: Figma 코드젠 컴포넌트 핸들러 및 아이콘 데이터를 업데이트합니다. - Updated dependencies [d8860f4] - Updated dependencies [7588e59] - @seed-design/css@1.2.12 ## 1.3.13 ### Patch Changes - 87f6f45: Figma 엔티티 및 Codegen을 업데이트합니다. - Updated dependencies [be7022a] - @seed-design/css@1.2.11 ## 1.3.12 ### Patch Changes - Updated dependencies [46bdf17] - Updated dependencies [79c7aa3] - @seed-design/css@1.2.10 ## 1.3.11 ### Patch Changes - 41cd943: 아이콘 데이터 및 Figma Codegen 컴포넌트 핸들러를 최신 버전으로 업데이트합니다. - Updated dependencies [5bf2e8a] - @seed-design/css@1.2.9 ## 1.3.10 ### Patch Changes - Updated dependencies [c02bfba] - Updated dependencies [f391668] - @seed-design/css@1.2.8 ## 1.3.9 ### Patch Changes - 72dfd8f: Figma Codegen 및 MCP에서 [Slot 레이어](https://help.figma.com/hc/en-us/articles/38231200344599-Use-slots-to-build-flexible-components-in-Figma)을 지원하도록 업데이트합니다. - Updated dependencies [846036b] - Updated dependencies [e78d460] - @seed-design/css@1.2.7 ## 1.3.8 ### Patch Changes - 7cbe9f3: Figma 레이어 normalizer 과정에서, 일부 텍스트 레이어에서 `fontFamily` 관련 크래시가 발생할 수 있는 문제를 수정합니다. ## 1.3.7 ### Patch Changes - 534eb1c: 신규 컴포넌트 `ContentPlaceholder`에 대한 Figma Codegen을 지원합니다. `ImageFrame` codegen 시 `fallback` 값으로 `ContentPlaceholder` 코드를 생성합니다. - Updated dependencies [4f56e76] - Updated dependencies [32938de] - Updated dependencies [67a7780] - Updated dependencies [3273312] - Updated dependencies [9d9b891] - @seed-design/css@1.2.6 ## 1.3.6 ### Patch Changes - Updated dependencies [c9fb0c5] - @seed-design/css@1.2.5 ## 1.3.5 ### Patch Changes - d6df976: Figma Codegen 관련 개선사항을 적용합니다. - TabsTrigger, ChipTabsTrigger Codegen 시 label 값이 누락되던 문제를 수정합니다. - MenuSheet Codegen 시 컴포넌트 이름이 `MenuSheet`로 잘못 생성되던 문제를 `MenuSheetRoot`로 수정합니다. - BottomSheet Codegen 시 정적 컴포넌트 키 대신 props에서 동적으로 키를 참조하도록 수정하여 인스턴스 매칭 오류를 해결합니다. - 지원되지 않는 종류의 레이어를 발견하는 경우 어떤 종류인지 결과에 명시합니다. - Updated dependencies [cd9a46c] - Updated dependencies [23e369d] - Updated dependencies [103830e] - Updated dependencies [77cdc0e] - Updated dependencies [7d6c910] - Updated dependencies [751e952] - @seed-design/css@1.2.4 ## 1.3.4 ### Patch Changes - 934a877: Figma Codegen 관련 개선 사항을 적용합니다. - Image Frame 컴포넌트의 React Codegen을 지원합니다. - OS 상단바 등 불필요한 UI 요소를 codegen 대상에서 제외합니다. - 너비가 Fill로 설정된 Action Button이 `flexGrow`를 갖도록 개선합니다. - Updated dependencies [eb390cf] - Updated dependencies [7c3bbe7] - Updated dependencies [6db2515] - Updated dependencies [12789e6] - Updated dependencies [6fb6dc2] - @seed-design/css@1.2.3 ## 1.3.3 ### Patch Changes - Updated dependencies [15010c3] - Updated dependencies [a3e6859] - Updated dependencies [4287600] - @seed-design/css@1.2.2 ## 1.3.2 ### Patch Changes - 934bea0: Figma 엔티티를 업데이트합니다. 모든 구성 요소에 대해 generated 데이터를 사용하여 안정성을 개선합니다. ## 1.2.1 ### Patch Changes - 15d9587: Figma 엔티티를 최신 버전으로 업데이트합니다. - Updated dependencies [9446f2c] - Updated dependencies [8ad9484] - Updated dependencies [9cbeba0] - @seed-design/css@1.2.1 ## 1.2.0 ### Minor Changes - a58022d: `SwitchMark`를 `Switchmark`로, `RadioMark`를 `Radiomark`로 Snippet 컴포넌트 이름을 변경합니다. - **1.1 → 1.2 업그레이드 시 변경 권장**: snippet을 다시 내려받고, `SwitchMark`, `RadioMark`를 사용하는 코드를 아래와 같이 변경하세요. - `npx @seed-design/cli@latest add ui:switch ui:radio-group` - snippet에 `SwitchMark`, `RadioMark` 정의가 존재하지만, 1.3 릴리즈 시 snippet에서 해당 맵핑이 제거될 예정이므로 미리 변경해두시길 권장드립니다. ```tsx // 전 import { ListSwitchItem, ListRadioItem } from "seed-design/ui/list"; import { SwitchMark } from "seed-design/ui/switch"; import { RadioMark } from "seed-design/ui/radio-group"; } />; } value="option" title="옵션" />; ``` ```tsx // 후 import { ListSwitchItem, ListRadioItem } from "seed-design/ui/list"; import { Switchmark } from "seed-design/ui/switch"; import { Radiomark } from "seed-design/ui/radio-group"; } />; } value="option" title="옵션" />; ``` - a0e40ca: [Tag Group](/react/components/tag-group) 관련 컴포넌트를 업데이트합니다. - `TagGroupItem` 레이블 내부에서 줄바꿈이 발생할 수 있도록 수정합니다. (기존: `TagGroupItem` 또는 separator 전후에서 줄바꿈 발생) - 한 줄 레이아웃 및 우선순위 옵션을 추가합니다. - `TagGroupRoot`에 `truncate` prop을 사용하여 한 줄로 유지하고 말줄임 처리를 할 수 있습니다. (기본값: `false`) - `TagGroupItem`에 `flexShrink` prop을 사용하여 말줄임 우선순위를 조정할 수 있습니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `TagGroupItem` 내부 레이블을 `TagGroupItemLabel`로 감싸거나, 신규로 제공되는 Snippet에서 제공하는 API로 교체해주세요. - `npx @seed-design/cli@latest add ui:tag-group` 명령어로 Snippet을 추가할 수 있습니다. ```tsx // 전 import { TagGroupRoot, TagGroupItem } from "@seed-design/react"; { /* TagGroup.Root, TagGroup.Item처럼 namespace import하는 코드가 있을 수 있습니다. */ } } /> 서초4동 광고 } color="fg.brand" /> {/* ... */} ; ``` ```tsx // 후 (Compound Component 유지) import { TagGroupRoot, TagGroupItem, TagGroupItemLabel, } from "@seed-design/react"; } /> {/* TagGroupItemLabel 사용 */} 서초4동 {/* TagGroupItemLabel 사용 */} 광고 } color="fg.brand" /> {/* ... */} ; ``` ```tsx // 후 (snippet API로 교체) // snippet 없는 경우, `npx @seed-design/cli@latest add ui:tag-group` import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; import { TagGroupRoot as SeedTagGroupRoot, TagGroupItem as SeedTagGroupItem, TagGroupItemLabel as SeedTagGroupItemLabel, } from "@seed-design/react"; } /> 광고 {/* 아이콘 커스터마이징이 필요한 경우 snippet 대신 Compound Component를 사용합니다. */} } color="fg.brand" /> {/* ... */} ; ``` - 358a1e4: [Menu Sheet](/react/components/menu-sheet) 관련 컴포넌트를 업데이트합니다. - `MenuSheetContent`에 설명을 추가할 수 있는 `description` prop이 추가되었습니다. - `MenuSheetItem`에 설명을 추가할 수 있는 `description` prop이 추가되었습니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: snippet을 다시 내려받고, `MenuSheetItem`을 사용하는 코드를 아래와 같이 변경하세요. - `npx @seed-design/cli@latest add ui:menu-sheet` - `children` 대신 `label` prop을 사용합니다. - `description`, `prefixIcon` prop이 추가되었습니다. ```tsx // 전 } /> 메뉴 항목 // 후 } label="메뉴 항목" description="이제 설명도 추가할 수 있어요" /> ``` ### Patch Changes - Updated dependencies [0ecb893] - Updated dependencies [98dbac4] - Updated dependencies [a58022d] - Updated dependencies [477ec8a] - Updated dependencies [2643d17] - Updated dependencies [8fb7038] - Updated dependencies [a0e40ca] - Updated dependencies [17c0ebd] - Updated dependencies [358a1e4] - @seed-design/css@1.2.0 ## 1.1.19 ### Patch Changes - Updated dependencies [6697fbe] - @seed-design/css@1.1.19 ## 1.1.18 ### Patch Changes - e92892a: 아이콘 정보를 업데이트합니다. ## 1.1.17 ### Patch Changes - Updated dependencies [db49a84] - Updated dependencies [6fab0e7] - Updated dependencies [5faef3a] - Updated dependencies [50ee0a6] - Updated dependencies [94bebf8] - Updated dependencies [8495fae] - @seed-design/css@1.1.17 ## 1.1.16 ### Patch Changes - Updated dependencies [2f29fe8] - Updated dependencies [9119723] - Updated dependencies [6d30b72] - Updated dependencies [10c0765] - Updated dependencies [5e462db] - @seed-design/css@1.1.16 ## 1.1.15 ### Patch Changes - Updated dependencies [76acd7e] - Updated dependencies [7a428ec] - Updated dependencies [498a9e7] - @seed-design/css@1.1.15 ## 1.1.14 ### Patch Changes - c1f818f: Figma Codegen & MCP에서 `boxShadow` 코드 생성을 지원하고 엔티티 정보를 최신화합니다. ## 1.1.13 ### Patch Changes - Updated dependencies [9be0581] - Updated dependencies [cc4a45a] - Updated dependencies [739937f] - @seed-design/css@1.1.13 ## 1.1.12 ### Patch Changes - 6d680ba: Figma Codegen 정보를 최신화합니다. - Updated dependencies [8d0ad90] - Updated dependencies [8f31f93] - Updated dependencies [69ccc6e] - Updated dependencies [279001a] - @seed-design/css@1.1.12 ## 1.1.10 ### Patch Changes - 23e9246: Figma 엔티티 및 Codegen을 업데이트합니다. (신규 아이콘 및 `ResultSection` Codegen 지원) - Updated dependencies [db5de74] - Updated dependencies [70d11b8] - Updated dependencies [c03a3dd] - Updated dependencies [a12e49b] - @seed-design/css@1.1.10 ## 1.1.9 ### Patch Changes - Updated dependencies [37d332d] - Updated dependencies [77517f1] - @seed-design/css@1.1.9 ## 1.1.8 ### Patch Changes - Updated dependencies [8752805] - Updated dependencies [ee98674] - @seed-design/css@1.1.8 ## 1.1.7 ### Patch Changes - Updated dependencies [bee919c] - Updated dependencies [1340675] - Updated dependencies [1340675] - @seed-design/css@1.1.7 ## 1.1.6 ### Patch Changes - Updated dependencies [dfe6c1e] - Updated dependencies [a09e6b4] - @seed-design/css@1.1.6 ## 1.1.5 ### Patch Changes - Updated dependencies [53290ab] - @seed-design/css@1.1.5 ## 1.1.4 ### Patch Changes - Updated dependencies [795668c] - @seed-design/css@1.1.4 ## 1.1.3 ### Patch Changes - d986fd5: Figma Codegen 컴포넌트 핸들러를 업데이트합니다. 레거시 Text Field에 대한 Codegen을 한시적으로 지원합니다. - Updated dependencies [15c658b] - Updated dependencies [f4e07bb] - Updated dependencies [114dafd] - Updated dependencies [bc3cd6f] - @seed-design/css@1.1.3 ## 1.1.2 ### Patch Changes - 4c5d7c4: Figma 토큰 정보 및 Codegen 핸들러를 최신화합니다. ## 1.1.0 ### Patch Changes - 191005f: Action Button 컴포넌트를 `variant=ghost`로 사용하는 경우 `fontWeight`를 사용자화할 수 있도록 업데이트합니다. (BREAKING CHANGE: Error State snippet을 다시 설치해야 합니다.) Error State 스니펫에서 Action Button을 활용하도록 업데이트합니다. - Updated dependencies [d6bb84d] - Updated dependencies [a55f584] - Updated dependencies [191005f] - Updated dependencies [b131282] - Updated dependencies [6af6501] - Updated dependencies [33def2d] - @seed-design/css@1.1.0 ## 1.0.7 ### Patch Changes - Updated dependencies [e52d6d1] - Updated dependencies [97669bc] - Updated dependencies [15ab93a] - Updated dependencies [50366c0] - @seed-design/css@1.0.7 ## 1.0.6 ### Patch Changes - 6aafce0: Tag Group 컴포넌트를 추가합니다. Tag Group은 아이콘 및 텍스트로 이루어진 태그를 구분 기호와 함께 수평 레이아웃으로 표시하는 컴포넌트입니다. - Updated dependencies [6aafce0] - Updated dependencies [1902dfa] - Updated dependencies [f2ddf29] - Updated dependencies [4c33f07] - Updated dependencies [3df657f] - @seed-design/css@1.0.6 ## 1.0.5 ### Patch Changes - Updated dependencies [f1cf4cd] - Updated dependencies [9b91751] - Updated dependencies [3898183] - @seed-design/css@1.0.5 ## 1.0.3 ### Patch Changes - ac1fd00: Figma Codegen이 Bottom Sheet의 `hideCloseButton` prop을 사용하는 코드를 반환하도록 수정합니다. - 8b07555: Figma 아이콘 및 스타일 정보를 최신화합니다. - Updated dependencies [0b8a02e] - Updated dependencies [6c6099d] - @seed-design/css@1.0.3 ## 1.0.2 ### Patch Changes - Updated dependencies [6d2e13d] - @seed-design/css@1.0.2 ## 1.0.1 ### Patch Changes - Updated dependencies [1420b68] - @seed-design/css@1.0.1 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [39a96f1] - Updated dependencies [34f92f2] - Updated dependencies [e038490] - Updated dependencies [4153ca5] - Updated dependencies [a7d07f0] - @seed-design/css@1.0.0 ## 0.2.5 ### Patch Changes - bef65a6: Figma Variable & Variable Collection 정보를 최신화합니다. - Updated dependencies [0ca19c0] - @seed-design/css@0.2.5 ## 0.2.4 ### Patch Changes - afdd1ee: Figma Codegen을 위한 컴포넌트 핸들러를 업데이트합니다. - List, Checkmark, RadioMark, Radio, Tabs, ChipTabs 지원 - Chip, Divider, HelpBubble, Switch, Checkbox 업데이트 - Updated dependencies [8ebe8a5] - Updated dependencies [f61b80d] - @seed-design/css@0.2.4 ## 0.2.3 ### Patch Changes - Updated dependencies [a22b8b9] - Updated dependencies [5836976] - Updated dependencies [12faf5a] - @seed-design/css@0.2.3 ## 0.2.1 ### Patch Changes - Updated dependencies [35984d0] - @seed-design/css@0.2.1 ## 0.2.0 ### Patch Changes - Updated dependencies [8448880] - @seed-design/css@0.2.0 ## 0.1.15 ### Patch Changes - Updated dependencies [c51a261] - Updated dependencies [5f2ee39] - Updated dependencies [8299ba9] - Updated dependencies [3de4cec] - @seed-design/css@0.1.15 ## 0.1.14 ### Patch Changes - f806356: Page Banner 컴포넌트를 추가합니다. Inline Banner 컴포넌트를 deprecate합니다. - Inline Banner 컴포넌트 대비 모든 `tone`에서 모든 `variant`를 지원하며, 내부 Button의 충분한 터치 영역을 보장합니다. ```tsx 새 탭에서 열기 } /> ``` 시맨틱 색상 토큰을 추가하고 수정합니다. - `$color.bg.positive-solid-pressed`: theme-dark에서 `$color.palette.green-500` → `$color.palette.green-600` - `$color.bg.warning-solid-pressed` 추가 - 1982494: Badge 컴포넌트를 업데이트합니다. - `tone=warning` variant를 추가합니다. - `maxWidth` 스펙을 추가합니다. 신규 시맨틱 색상 토큰을 추가합니다. - `$color.fg.warning` - `$color.stroke.warning` - `$color.fg.brand-contrast` - `$color.bg.brand-weak` - `$color.bg.brand-weak-pressed` - Updated dependencies [f806356] - Updated dependencies [1982494] - @seed-design/css@0.1.14 ## 0.1.13 ### Patch Changes - Updated dependencies [0be9b00] - @seed-design/css@0.1.13 ## 0.1.12 ### Patch Changes - Updated dependencies [62094b6] - @seed-design/css@0.1.12 ## 0.1.11 ### Patch Changes - 9993e0e: 레이아웃 컴포넌트를 사용할 때 `flexGrow`, `flexShrink`, `flexWrap`에도 `true`를 사용할 수 있도록 수정합니다. Figma 레이어가 이미지 Fill을 가지고 있는 경우 `` 요소를 prepend합니다. ## 0.1.10 ### Patch Changes - aa40f66: Figma 컴포넌트 정보 및 핸들러를 최신화하고 codegen을 업데이트합니다. - Instance에 오버라이드한 내용이 있는 경우 주석으로 알립니다. - Updated dependencies [ef91c21] - @seed-design/css@0.1.10 ## 0.1.9 ### Patch Changes - 5a025b7: Switch 컴포넌트를 업데이트합니다. - size: medium → 32, small → 16으로 rename합니다. - (React) `size="medium"`으로 `32`, `size="small"`로 `16`을 사용할 수 있습니다. (deprecated) - size: 24를 추가합니다. - 모든 size에 대해 레이블 스타일을 추가합니다. (기존: small에만 존재) - f9379e0: Figma 컴포넌트 핸들러 정보를 최신화하고 정확한 prop을 생성하도록 수정합니다. - Chip, Progress Circle, Switch 컴포넌트 핸들러를 업데이트합니다. - `grow` 대신 `flexGrow`가 생성되도록, `borderWidth`가 `string`으로 생성되도록 수정합니다. - Updated dependencies [5a025b7] - Updated dependencies [ac35731] - Updated dependencies [f9041e9] - @seed-design/css@0.1.9 ## 0.1.8 ### Patch Changes - 2e2cc53: - Figma 컴포넌트 핸들러 및 스타일 정보를 최신화합니다. - normalize 과정에서 그라디언트 정보를 포함하도록 수정합니다. - Updated dependencies [609b8f3] - @seed-design/css@0.1.8 ## 0.1.7 ### Patch Changes - Updated dependencies [4afe80b] - @seed-design/css@0.1.7 ## 0.1.6 ### Patch Changes - Updated dependencies [235147d] - Updated dependencies [3c13ad7] - @seed-design/css@0.1.6 ## 0.1.5 ### Patch Changes - Updated dependencies [861ecb4] - Updated dependencies [3889eb6] - @seed-design/css@0.1.5 ## 0.1.4 ### Patch Changes - Updated dependencies [0ffcd48] - @seed-design/css@0.1.4 ## 0.1.3 ### Patch Changes - Updated dependencies [cdc0930] - Updated dependencies [946faf7] - Updated dependencies [71c58fd] - @seed-design/css@0.1.3 ## 0.1.2 ### Patch Changes - 7b2c0f3: Updated dependencies - @seed-design/react@0.1.1 - Updated dependencies [7b2c0f3] - @seed-design/css@0.1.2 ## 0.1.1 ### Patch Changes - Updated dependencies [e3b782d] - @seed-design/css@0.1.1 ## 0.1.0 ### Patch Changes - Updated dependencies [7cc6087] - Updated dependencies [bdca898] - @seed-design/css@0.1.0 ## 0.0.41 ### Patch Changes - Updated dependencies [561f74c] - Updated dependencies [b43de05] - @seed-design/css@0.0.41 ## 0.0.40 ### Patch Changes - 5a55fb3: Instance Swap의 대상 노드가 visible: false일 때 REST API에서 원본 컴포넌트 정보를 제공하지 않아 발생하는 참조 오류를 수정합니다. ## 0.0.39 ### Patch Changes - Updated dependencies [f801300] - @seed-design/css@0.0.39 ## 0.0.38 ### Patch Changes - Updated dependencies [70fbaaf] - @seed-design/css@0.0.38 ## 0.0.35 ### Patch Changes - Updated dependencies [0789dc8] - @seed-design/css@0.0.35 ## 0.0.34 ### Patch Changes - Updated dependencies [92801a2] - @seed-design/css@0.0.34 ## 0.0.33 ### Patch Changes - Updated dependencies [fbdb091] - @seed-design/css@0.0.33 ## 0.0.31 ### Patch Changes - Updated dependencies [fd7c569] - @seed-design/css@0.0.31 ## 0.0.30 ### Patch Changes - Updated dependencies [285cb9b] - @seed-design/css@0.0.30 ## 0.0.29 ### Patch Changes - Updated dependencies [116ee2c] - @seed-design/css@0.0.29 ## 0.0.28 ### Patch Changes - b3da758: Figma XML 타겟에서 instance 노드의 컴포넌트 정보를 제공합니다. - Updated dependencies [5337e14] - @seed-design/css@0.0.28 ## 0.0.27 ### Patch Changes - 4133c5e: 레이아웃 컴포넌트의 codegen이 default value를 정상적으로 제외하도록 수정합니다. - Updated dependencies [9d85c16] - Updated dependencies [d951317] - Updated dependencies [b3f964d] - @seed-design/css@0.0.27 ## 0.0.25 ### Patch Changes - c8a6d41: codegen 결과물이 import 문을 함께 반환하는 기능을 추가합니다. - Updated dependencies [c87ede9] - @seed-design/css@0.0.25 ## 0.0.24 ### Patch Changes - Updated dependencies [4da536f] - @seed-design/css@0.0.24 ## 0.0.23 ### Patch Changes - Updated dependencies [63e1541] - @seed-design/css@0.0.23 ## 0.0.22 ### Patch Changes - 6c0133a: 커스텀 컴포넌트를 등록할 수 있도록 extend.componentHandlers 설정을 제공합니다. ## 0.0.21 ### Patch Changes - b167e95: NormalizedInstanceNode의 componentProperties에 componentSetKey를 추가합니다. - 2f2f9b3: TextField codegen이 아이콘을 인식하지 못하는 문제를 수정합니다. - 4d34760: 상단 내비게이션의 아이콘 버튼 터치영역을 44px로 변경합니다. - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [5d69d1d] - Updated dependencies [4d34760] - Updated dependencies [7ae87f8] - Updated dependencies [f144d28] - Updated dependencies [e368c69] - @seed-design/css@0.0.21 ## 0.0.20 ### Patch Changes - 38ece6a: Text style, 아이콘을 찾지 못했을 때 fallback을 추가합니다. ## 0.0.19 ### Patch Changes - Updated dependencies [3c9ec66] - Updated dependencies [b3bb6e7] - @seed-design/css@0.0.19 ## 0.0.18 ### Patch Changes - b28303c: borderRadius codegen에 radius prefix가 붙지 않도록 수정합니다. ## 0.0.17 ### Patch Changes - Updated dependencies [c042f90] - @seed-design/css@0.0.17 ## 0.0.15 ### Patch Changes - 4511814: - 레이아웃 및 flex 관련 shorthand prop을 추가합니다. (px, py, wrap, align, justify, direction) - ActionButton에 flexGrow prop을 추가합니다. - VStack, HStack 컴포넌트를 추가합니다. - Stack, Inline, Columns 컴포넌트를 deprecated 처리합니다. - 디자인 토큰이 아닌 css prop의 value가 유효한 css value가 되도록 변경합니다. - flexStart, spaceBetween 등 camelCase로 제공되는 값을 deprecated 처리합니다. - Updated dependencies [1bb9f7b] - Updated dependencies [4511814] - Updated dependencies [f4b0723] - Updated dependencies [f4b0723] - @seed-design/css@0.0.15 ## 0.0.6 ### Patch Changes - Updated dependencies [92c0b80] - Updated dependencies [c1d94d0] - @seed-design/css@0.0.14 ## 0.0.5 ### Patch Changes - Updated dependencies [7fca755] - @seed-design/css@0.0.13 ## 0.0.4 ### Patch Changes - Updated dependencies [6426379] - Updated dependencies [ee41f37] - @seed-design/css@0.0.12 ## 0.0.3 ### Patch Changes - 9ff6487: - 숨겨진 노드 및 Fill을 무시하도록 수정합니다. - BOOLEAN_OPERATION 노드를 지원합니다. - Updated dependencies [e70f340] - Updated dependencies [72f344f] - @seed-design/css@0.0.11 ## 0.0.2 ### Patch Changes - 1d9e06a: SEED Design의 Figma 통합을 위한 패키지를 제공합니다. - REST API와 Plugin API를 normalize 합니다. 플러그인 및 서버 환경 모두 동일한 방식으로 사용할 수 있도록 합니다. - Figma Plugin으로 제공되었던 Codegen 기능을 패키지로 제공합니다. - Updated dependencies [e4b704c] - @seed-design/css@0.0.10 --- ## @seed-design/icon-deprecated ## 0.2.1 ### Patch Changes - aa25955: change svg width, height to 100% ## 0.2.0 ### Minor Changes - 4b334ca: change sprite svg to inline svg ## 0.1.13 ### Patch Changes - b8933f7: chore: remove navigator in component template ## 0.1.12 ### Patch Changes - 5e5145c: fix: escape variable name in component template ## 0.1.11 ### Patch Changes - 03b5e65: fix mobile safari not working ## 0.1.10 ### Patch Changes - ec0e449: add tag in sprite.svg ## 0.1.9 ### Patch Changes - 90724ac: change template ## 0.1.8 ### Patch Changes - 5601faf: remove ts-nocheck, add string type in size, sum withContext and contextPath ## 0.1.7 ### Patch Changes - f878d22: `eslint-disabled`, `ts-nocheck` in component template, check tsconfig jsx option. ## 0.1.6 ### Patch Changes - ee461d4: automatic yml schema versioning ## 0.1.5 ### Patch Changes - 7b87056: json schema versioning ## 0.1.4 ### Patch Changes - 86ef6a4: update schema ## 0.1.2 ### Patch Changes - e73aa6f: version up karrot-ui-icon ## 0.1.1 ### Patch Changes - cff2c8a: # 한국어 라이브러리 제공자가 `seed-icon`을 사용할 때 필요한 `provider`가 추가되었습니다. **라이브러리 제공자가 아닌 기존 프로젝트는 그대로 사용할 수 있습니다.** ### provider - `withContext`와 `contextPath` 옵션을 추가했습니다. - `withContext` 옵션은 `true`로 설정하면 `contextPath`에 설정된 경로로 `context`를 생성합니다. - 라이브러리 제공자는 유저가 어떤 sprite.svg 경로를 사용할 지 모릅니다. 어떤 loader를 쓰는지 라이브러리 제작자 입장에서는 알 수 없습니다. - 그래서 `seed-icon` 에서 제공되는 `provider`를 노출시켜 유저가 `spriteUrl`을 설정하도록 하였습니다. ### etc - 코드 리팩토링 - Readme.md 내용 추가 # English Added `provider`, which is required when a library provider uses `seed-icon`. **Existing projects that are not library providers can still be used as is**. ### provider - Added `withContext` and `contextPath` options. - The `withContext` option, when set to `true`, creates a `context` with the path set in `contextPath`. - The library provider doesn't know which sprite.svg path the user will use. The library provider doesn't know what loader user use. - So we expose the `provider` provided by `seed-icon` and let the user set the `spriteUrl`. ### etc - Code refactoring - Added `Readme.md` content ## 0.1.0 ### Minor Changes - 64d5540: ### Config - add `vite` config template - add `yaml schema` in config template ### Icon Component - change component span style to `inline-flex` - remove component props `width`, `height` and add `size` prop ### etc - logs more detail - update `readme` ## 0.0.8 ### Patch Changes - 4634f52: - change chalk to kleur - remove unnecessary options ## 0.0.7 ### Patch Changes - 7dcf86a: support monorepo ## 0.0.6 ### Patch Changes - 633ba69: remove assert ## 0.0.5 ### Patch Changes - 4ce85ad: version dataset, width, height - 4ce85ad: assert json - 4ce85ad: add size props - 4ce85ad: update ## 0.0.4 ### Patch Changes - 53fde00: version dataset, width, height - 53fde00: add size props - 53fde00: update ## 0.0.3 ### Patch Changes - f1abcb5: add size props - 89fb8bf: update ## 0.0.2 ### Patch Changes - a79399f: init project - a79399f: fix svg relative url ## 0.0.1 ### Patch Changes - 8dbbbc8: init project --- ## @seed-design/lynx-css ## 0.4.1 ### Patch Changes - 7224b94: ImageFrameReactionButton root에 반투명 배경색과 그림자를 추가하여 밝은 이미지 위에서의 가시성을 개선합니다. - c4ce431: Side Panel Close Button 및 Side Panel Header의 스타일을 디자인 스펙에 맞게 수정하고, Side Panel body에 `box-sizing: border-box`를 적용하여, body에 지정한 `height`/`minHeight`/`maxHeight`가 padding을 포함해 계산되도록 수정합니다. ## 0.4.0 ### Minor Changes - f2b198d: `$color.bg.layer-fill` 토큰을 다시 추가합니다. `2.0.0`에서 제거했으나 마땅한 대체 토큰이 없어 deprecated 상태로 되살립니다. 값은 제거 이전과 동일합니다. 추후 동일한 값의 새 이름 토큰으로 대체된 뒤 `3.0.0`에서 제거될 예정입니다. `@seed-design/tailwind3-plugin`과 `@seed-design/tailwind4-theme`의 `@seed-design/css` peer dependency 버전 범위를 `^2.1.0`으로 올립니다. ## 0.3.0 ### Minor Changes - 555525a: Attachment Field 관련 컴포넌트를 추가합니다. - Attachment Field: 파일을 선택하거나 드래그 앤 드롭으로 직접 업로드할 때 사용합니다. - Attachment Display Field: 외부 소스에서 URL로 제공된 미디어를 표시·관리할 때 사용합니다. - d1a8d7c: 신규 타이포그래피 토큰을 추가합니다. - **폰트 크기 토큰 (`$font-size`)** - `t11`, `t11-static` - `t12`, `t12-static` - `t13`, `t13-static` - `t14`, `t14-static` - **줄 간격 토큰 (`$line-height`)** - `t11`, `t11-static` - `t12`, `t12-static` - `t13`, `t13-static` - `t14`, `t14-static` - **텍스트 스타일 (`textStyle`)** - `t8Regular`, `t8Medium`, `t8StaticRegular`, `t8StaticMedium` - `t9Regular`, `t9Medium`, `t9StaticRegular`, `t9StaticMedium` - `t10Regular`, `t10Medium`, `t10StaticRegular`, `t10StaticMedium` - `t11Regular`, `t11Medium`, `t11Bold`, `t11StaticRegular`, `t11StaticMedium`, `t11StaticBold` - `t12Regular`, `t12Medium`, `t12Bold`, `t12StaticRegular`, `t12StaticMedium`, `t12StaticBold` - `t13Regular`, `t13Medium`, `t13Bold`, `t13StaticRegular`, `t13StaticMedium`, `t13StaticBold` - `t14Regular`, `t14Medium`, `t14Bold`, `t14StaticRegular`, `t14StaticMedium`, `t14StaticBold` - 69e3b97: Menu 컴포넌트를 추가합니다. Drawer를 연 뒤 Drawer 뒤 요소에 포커스가 남아 있는 문제를 수정합니다. Drawer가 열리는 경우 `Drawer.Content`에 자동으로 포커스가 이동합니다. - `Drawer.RootProps`의 `autoFocus` 기본값을 `false`에서 `true`로 변경합니다. - 스크린 리더가 `modal=true` (기본값)인 Dialog 및 Drawer 뒤 요소를 읽을 수 있는 문제를 수정합니다. Dialog(AlertDialog, MenuSheet)와 Drawer(BottomSheet)를 `@radix-ui/react-dismissable-layer`에서 자체 `useDismissibleLayer` 훅 기반으로 리팩터링하고 불필요하게 외부로 노출되던 내부 옵션들을 제거합니다. - `DialogRoot` 및 `DrawerRoot`의 `onOpenChange` 두 번째 인자 `details.reason`이 `interactOutside`인 경우 `details.event`의 타입을 `PointerEvent | FocusEvent`에서 `PointerEvent | TouchEvent | FocusEvent`로 변경합니다. - `DialogRoot` 및 `DrawerRoot` 두 번째 인자 `details`의 `reason`으로 `cascadeDismiss`를 추가합니다. 두 개 이상의 오버레이 컴포넌트를 표시한 상황에서 하위 컴포넌트가 dismiss되는 경우 상위 컴포넌트는 `cascadeDismiss`와 함께 `onOpenChange`가 호출됩니다. - `@seed-design/react` 패키지에서 `@radix-ui/react-dialog` 의존성을 제거합니다. - 57e4f1a: Chip Tabs Trigger가 disabled 상태일 때 opacity를 사용하여 스타일을 조절하도록 수정합니다. - 270b277: 컴포넌트 및 파운데이션 전반에 JSDoc 주석을 강화합니다. ## 0.2.1 ### Patch Changes - 234e8c7: deprecated 항목의 제거 예정 버전 안내를 1.3.0에서 2.0.0으로 변경합니다. - `ImageFrame`의 `rounded` 옵션, `Switch`/`Checkbox`/`ChipTabs`의 deprecated 옵션, 유틸리티 스타일 prop(`display`, `flexDirection`, `justifyContent`, `alignItems`)의 deprecated 값의 제거 예정 버전을 2.0.0으로 안내합니다. - `$color.bg.layer-fill`, `$gradient.fade-layer-floating`, `$gradient.fade-layer-default` 토큰의 제거 예정 버전을 2.0.0으로 안내합니다. - 2.0.0부터 deprecated 항목 제거를 포함한 breaking change는 메이저 릴리스에서만 수행됩니다. ## 0.2.0 ### Minor Changes - c8d4c26: Lynx AppBar 컴포넌트를 추가합니다. - `AppBar.Root`, `AppBar.Left`, `AppBar.Main`, `AppBar.Right`, `AppBar.IconButton` 등 compound API를 제공합니다. - `SystemInfo.platform`에 따라 Android에서는 `android`, 그 외 플랫폼에서는 `cupertino` theme을 기본으로 적용합니다. - `theme` prop으로 platform 기본값을 수동 override할 수 있습니다. - 26d3517: Lynx Badge 컴포넌트를 추가합니다. - `@seed-design/lynx-react`에서 `Badge`를 직접 import해 사용할 수 있습니다. - `@seed-design/lynx-css/recipes/badge`에서 Badge recipe 스타일을 제공합니다. ```tsx import { Badge } from "@seed-design/lynx-react"; 거래 가능 ; ``` ### Patch Changes - 4e2ee69: Lynx BottomSheet가 앱 셸 위에 안정적으로 표시되고, 본문 스크롤 제스처를 내부에서 처리하도록 개선합니다. - BottomSheet overlay의 기본 `z-index`를 높여 앱바 같은 상단 영역까지 backdrop이 덮이도록 수정합니다. - `BottomSheetBody`가 세로 스크롤 영역으로 렌더링되어 본문을 스크롤할 때 뒤쪽 화면이 함께 스크롤되는 문제를 방지합니다. - 스크롤 가능한 BottomSheet에서는 `BottomSheetRoot`의 `handleOnly`를 함께 사용할 수 있습니다. - BottomSheet handle의 드래그 target size를 보이는 handle보다 크게 보장합니다. - 8dc8f4f: Menu Sheet의 디자인 스펙에서 핸들 여백 확보를 위해 `content` 상단 패딩을 `x4` → `x6`으로 늘리고 `header.paddingTop`을 제거합니다 (핸들 여백 확보). React/CSS `MenuSheet`의 경우 핸들이 존재하지 않으므로 변경사항이 없습니다. - 0fa9a1f: Lynx에서 테마(라이트/다크) 전환 시 텍스트 색상이 갱신되지 않던 문제를 수정합니다. ## 0.1.0 ### Minor Changes - 699af2c: SEED Design Lynx의 첫 stable 버전을 릴리즈합니다. - `@seed-design/lynx-css`: Lynx 환경에서 사용할 수 있는 SEED 디자인 토큰, base CSS, component recipe CSS를 제공합니다. - `@seed-design/lynx-react`: Lynx용 SEED React 컴포넌트와 hook을 제공합니다. - 컴포넌트: `ActionButton`, `BottomSheet`, `Box`, `Checkbox`, `HStack`, `ProgressCircle`, `RadioGroup`, `Switch`, `TagGroup`, `Text`, `VStack` - Hook: `useControllableState`, `useIconColor`, `usePressTap`, `useSafeArea` - Safe Area: `useSafeArea` API로 `env(safe-area-inset-*)`와 host 앱의 `lynx.__globalProps` fallback을 함께 지원합니다. - `@seed-design/cli`: Lynx framework 감지, registry snippet 추가, compatibility check, docs lookup 흐름을 지원합니다. --- ## @seed-design/lynx-react ## 0.3.0 ### Minor Changes - 2ad8e8c: (BREAKING CHANGE: `@lynx-js/react`를 `0.117.0` 이상으로 업그레이드해야 합니다.) 테마 변경에 반응하는 `useSeedClassName` 훅을 추가합니다. - root ``에서 `getSeedClassName()` 대신 `useSeedClassName()`을 사용하면, host가 `lynx.__globalProps.theme`을 변경할 때 자동으로 리렌더되어 테마가 즉시 반영됩니다. - `getSeedClassName()` 순수 함수는 그대로 유지됩니다. 테마 변경에 반응할 필요가 없는 정적 컨텍스트에서 계속 사용할 수 있습니다. - `useGlobalProps()`에 의존하므로 `@lynx-js/react` `0.117.0` 이상이 필요합니다. ```tsx import { useSeedClassName } from "@seed-design/lynx-react"; function Root() { const seedClassName = useSeedClassName({ colorMode: "system" }); return ( ); } ``` ## 0.2.0 ### Minor Changes - c8d4c26: Lynx AppBar 컴포넌트를 추가합니다. - `AppBar.Root`, `AppBar.Left`, `AppBar.Main`, `AppBar.Right`, `AppBar.IconButton` 등 compound API를 제공합니다. - `SystemInfo.platform`에 따라 Android에서는 `android`, 그 외 플랫폼에서는 `cupertino` theme을 기본으로 적용합니다. - `theme` prop으로 platform 기본값을 수동 override할 수 있습니다. - f039fb3: native element의 접근성 속성을 표준화한 `LynxAccessibilityProps` 타입을 추가했습니다. 컴포넌트가 이 타입을 확장해 `accessibility-label`/`accessibility-role-description`/`accessibility-value`/`accessibility-elements-hidden` 등을 받아 native element에 전달할 수 있습니다. - 26d3517: Lynx Badge 컴포넌트를 추가합니다. - `@seed-design/lynx-react`에서 `Badge`를 직접 import해 사용할 수 있습니다. - `@seed-design/lynx-css/recipes/badge`에서 Badge recipe 스타일을 제공합니다. ```tsx import { Badge } from "@seed-design/lynx-react"; 거래 가능 ; ``` ### Patch Changes - 4e2ee69: Lynx BottomSheet가 앱 셸 위에 안정적으로 표시되고, 본문 스크롤 제스처를 내부에서 처리하도록 개선합니다. - BottomSheet overlay의 기본 `z-index`를 높여 앱바 같은 상단 영역까지 backdrop이 덮이도록 수정합니다. - `BottomSheetBody`가 세로 스크롤 영역으로 렌더링되어 본문을 스크롤할 때 뒤쪽 화면이 함께 스크롤되는 문제를 방지합니다. - 스크롤 가능한 BottomSheet에서는 `BottomSheetRoot`의 `handleOnly`를 함께 사용할 수 있습니다. - BottomSheet handle의 드래그 target size를 보이는 handle보다 크게 보장합니다. ## 0.1.0 ### Minor Changes - 699af2c: SEED Design Lynx의 첫 stable 버전을 릴리즈합니다. - `@seed-design/lynx-css`: Lynx 환경에서 사용할 수 있는 SEED 디자인 토큰, base CSS, component recipe CSS를 제공합니다. - `@seed-design/lynx-react`: Lynx용 SEED React 컴포넌트와 hook을 제공합니다. - 컴포넌트: `ActionButton`, `BottomSheet`, `Box`, `Checkbox`, `HStack`, `ProgressCircle`, `RadioGroup`, `Switch`, `TagGroup`, `Text`, `VStack` - Hook: `useControllableState`, `useIconColor`, `usePressTap`, `useSafeArea` - Safe Area: `useSafeArea` API로 `env(safe-area-inset-*)`와 host 앱의 `lynx.__globalProps` fallback을 함께 지원합니다. - `@seed-design/cli`: Lynx framework 감지, registry snippet 추가, compatibility check, docs lookup 흐름을 지원합니다. --- ## @seed-design/mcp ## 2.0.0 이 패키지의 코드 변경은 없으며, SEED React 2 릴리즈에 맞춰 앞으로의 semver 정합성을 위해 버전을 올립니다. ## 1.3.18 ### Patch Changes - @seed-design/figma@1.3.18 ## 1.3.17 ### Patch Changes - @seed-design/figma@1.3.17 ## 1.3.16 ### Patch Changes - @seed-design/figma@1.3.16 ## 1.3.15 ### Patch Changes - 91f2e12: CLI 기반 도구의 `cac` 의존성을 v7로 업데이트합니다. - `@seed-design/cli`와 `@seed-design/codemod`의 Node.js 요구사항을 `>=20.19.0`으로 맞춥니다. - `@seed-design/mcp`와 `@seed-design/figma-extractor`에서도 최신 `cac` 런타임을 사용합니다. - @seed-design/figma@1.3.15 ## 1.3.14 ### Patch Changes - Updated dependencies [546f1fc] - @seed-design/figma@1.3.14 ## 1.3.13 ### Patch Changes - 95d0aa2: `uuid` 의존성을 v14로 업데이트합니다. - Updated dependencies [87f6f45] - @seed-design/figma@1.3.13 ## 1.3.12 ### Patch Changes - @seed-design/figma@1.3.12 ## 1.3.11 ### Patch Changes - 7ca8e6c: axios 의존성을 업데이트합니다. - Updated dependencies [41cd943] - @seed-design/figma@1.3.11 ## 1.3.10 ### Patch Changes - @seed-design/figma@1.3.10 ## 1.3.9 ### Patch Changes - Updated dependencies [72dfd8f] - @seed-design/figma@1.3.9 ## 1.3.8 ### Patch Changes - 9f11b31: SEED Figma MCP에 `find_nodes` 도구를 추가합니다. 하위 레이어를 이름(정규식)으로 검색하여 목록을 반환합니다. - Updated dependencies [7cbe9f3] - @seed-design/figma@1.3.8 ## 1.3.7 ### Patch Changes - 9f039c5: Figma MCP 플러그인이 MCP 웹소켓 서버에 연결할 수 없던 문제를 해결합니다. - Updated dependencies [534eb1c] - @seed-design/figma@1.3.7 ## 1.3.6 ### Patch Changes - @seed-design/figma@1.3.6 ## 1.3.5 ### Patch Changes - Updated dependencies [d6df976] - @seed-design/figma@1.3.5 ## 1.3.4 ### Patch Changes - Updated dependencies [934a877] - @seed-design/figma@1.3.4 ## 1.3.3 ### Patch Changes - @seed-design/figma@1.3.3 ## 1.3.2 ### Patch Changes - Updated dependencies [934bea0] - @seed-design/figma@1.3.2 ## 1.3.1 ### Patch Changes - 2f9690a: MCP를 Node.js 프로젝트에서 직접 구성할 수 있도록 `registerTools`를 비롯한 export를 추가합니다. ## 1.3.0 ### Minor Changes - 423d604: Figma Personal Access Token을 활용하여 WebSocket 서버와 Figma 플러그인 없이 MCP 일부 기능을 활용할 수 있도록 합니다. `--mode` 옵션을 통해 필요한 도구만 등록할 수 있습니다. ## 1.2.1 ### Patch Changes - Updated dependencies [15d9587] - @seed-design/figma@1.2.1 ## 1.2.0 ### Patch Changes - 41c4e31: MCP `get_node(s)_info` 및 `get_node_react_code` 툴 호출이 실패하는 경우 Figma 라이브러리 업데이트 안내를 추가합니다. - Updated dependencies [a58022d] - Updated dependencies [a0e40ca] - Updated dependencies [358a1e4] - @seed-design/figma@1.2.0 ## 1.1.19 ### Patch Changes - @seed-design/figma@1.1.19 ## 1.1.18 ### Patch Changes - Updated dependencies [e92892a] - @seed-design/figma@1.1.18 ## 1.1.17 ### Patch Changes - @seed-design/figma@1.1.17 ## 1.1.16 ### Patch Changes - @seed-design/figma@1.1.16 ## 1.1.15 ### Patch Changes - @seed-design/figma@1.1.15 ## 1.1.14 ### Patch Changes - Updated dependencies [c1f818f] - @seed-design/figma@1.1.14 ## 1.1.13 ### Patch Changes - @seed-design/figma@1.1.13 ## 1.1.12 ### Patch Changes - Updated dependencies [6d680ba] - @seed-design/figma@1.1.12 ## 1.1.11 ### Patch Changes - b58ac46: Figma MCP가 `export_node_as_image` 툴과 `clone_node` 툴 호출 후 반환 결과를 정상적으로 파싱하지 못하는 문제를 수정합니다. ## 1.1.10 ### Patch Changes - Updated dependencies [23e9246] - @seed-design/figma@1.1.10 ## 1.1.9 ### Patch Changes - @seed-design/figma@1.1.9 ## 1.1.8 ### Patch Changes - @seed-design/figma@1.1.8 ## 1.1.7 ### Patch Changes - @seed-design/figma@1.1.7 ## 1.1.6 ### Patch Changes - @seed-design/figma@1.1.6 ## 1.1.5 ### Patch Changes - @seed-design/figma@1.1.5 ## 1.1.4 ### Patch Changes - @seed-design/figma@1.1.4 ## 1.1.3 ### Patch Changes - Updated dependencies [d986fd5] - @seed-design/figma@1.1.3 ## 1.1.2 ### Patch Changes - Updated dependencies [4c5d7c4] - @seed-design/figma@1.1.2 ## 1.1.0 ### Patch Changes - Updated dependencies [191005f] - @seed-design/figma@1.1.0 ## 1.0.7 ### Patch Changes - @seed-design/figma@1.0.7 ## 1.0.6 ### Patch Changes - Updated dependencies [6aafce0] - @seed-design/figma@1.0.6 ## 1.0.5 ### Patch Changes - @seed-design/figma@1.0.5 ## 1.0.3 ### Patch Changes - Updated dependencies [ac1fd00] - Updated dependencies [8b07555] - @seed-design/figma@1.0.3 ## 1.0.2 ### Patch Changes - @seed-design/figma@1.0.2 ## 1.0.1 ### Patch Changes - @seed-design/figma@1.0.1 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/figma@1.0.0 ## 0.2.5 ### Patch Changes - Updated dependencies [bef65a6] - @seed-design/figma@0.2.5 ## 0.2.4 ### Patch Changes - Updated dependencies [afdd1ee] - @seed-design/figma@0.2.4 ## 0.2.3 ### Patch Changes - @seed-design/figma@0.2.3 ## 0.2.1 ### Patch Changes - @seed-design/figma@0.2.1 ## 0.2.0 ### Patch Changes - @seed-design/figma@0.2.0 ## 0.1.15 ### Patch Changes - @seed-design/figma@0.1.15 ## 0.1.14 ### Patch Changes - Updated dependencies [f806356] - Updated dependencies [1982494] - @seed-design/figma@0.1.14 ## 0.1.13 ### Patch Changes - @seed-design/figma@0.1.13 ## 0.1.12 ### Patch Changes - @seed-design/figma@0.1.12 ## 0.1.11 ### Patch Changes - Updated dependencies [9993e0e] - @seed-design/figma@0.1.11 ## 0.1.10 ### Patch Changes - Updated dependencies [aa40f66] - @seed-design/figma@0.1.10 ## 0.1.9 ### Patch Changes - Updated dependencies [5a025b7] - Updated dependencies [f9379e0] - @seed-design/figma@0.1.9 ## 0.1.8 ### Patch Changes - Updated dependencies [2e2cc53] - @seed-design/figma@0.1.8 ## 0.1.7 ### Patch Changes - @seed-design/figma@0.1.7 ## 0.1.6 ### Patch Changes - @seed-design/figma@0.1.6 ## 0.1.5 ### Patch Changes - @seed-design/figma@0.1.5 ## 0.1.4 ### Patch Changes - @seed-design/figma@0.1.4 ## 0.1.3 ### Patch Changes - @seed-design/figma@0.1.3 ## 0.1.2 ### Patch Changes - 7b2c0f3: Updated dependencies - @seed-design/react@0.1.1 - Updated dependencies [7b2c0f3] - @seed-design/figma@0.1.2 ## 0.1.1 ### Patch Changes - @seed-design/figma@0.1.1 ## 0.1.0 ### Patch Changes - @seed-design/figma@0.1.0 ## 0.0.41 ### Patch Changes - @seed-design/figma@0.0.41 ## 0.0.40 ### Patch Changes - 5a55fb3: Instance Swap의 대상 노드가 visible: false일 때 REST API에서 원본 컴포넌트 정보를 제공하지 않아 발생하는 참조 오류를 수정합니다. - Updated dependencies [5a55fb3] - @seed-design/figma@0.0.40 ## 0.0.39 ### Patch Changes - @seed-design/figma@0.0.39 ## 0.0.38 ### Patch Changes - @seed-design/figma@0.0.38 ## 0.0.35 ### Patch Changes - @seed-design/figma@0.0.35 ## 0.0.34 ### Patch Changes - @seed-design/figma@0.0.34 ## 0.0.33 ### Patch Changes - @seed-design/figma@0.0.33 ## 0.0.31 ### Patch Changes - @seed-design/figma@0.0.31 ## 0.0.30 ### Patch Changes - @seed-design/figma@0.0.30 ## 0.0.29 ### Patch Changes - @seed-design/figma@0.0.29 ## 0.0.28 ### Patch Changes - Updated dependencies [b3da758] - @seed-design/figma@0.0.28 ## 0.0.27 ### Patch Changes - Updated dependencies [4133c5e] - @seed-design/figma@0.0.27 ## 0.0.25 ### Patch Changes - c8a6d41: codegen 결과물이 import 문을 함께 반환하는 기능을 추가합니다. - Updated dependencies [c8a6d41] - @seed-design/figma@0.0.25 ## 0.0.24 ### Patch Changes - @seed-design/figma@0.0.24 ## 0.0.23 ### Patch Changes - bf38ec2: 기본적으로 local-default 채널을 사용하도록 변경합니다. join_channel을 매 세션마다 요청할 필요를 없앱니다. - @seed-design/figma@0.0.23 ## 0.0.22 ### Patch Changes - 6c0133a: 커스텀 컴포넌트를 등록할 수 있도록 extend.componentHandlers 설정을 제공합니다. - Updated dependencies [6c0133a] - @seed-design/figma@0.0.22 ## 0.0.21 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [b167e95] - Updated dependencies [2f2f9b3] - Updated dependencies [4d34760] - Updated dependencies [e368c69] - @seed-design/figma@0.0.21 ## 0.0.20 ### Patch Changes - Updated dependencies [38ece6a] - @seed-design/figma@0.0.20 ## 0.0.19 ### Patch Changes - @seed-design/figma@0.0.19 ## 0.0.18 ### Patch Changes - 4619fde: 번들에 의도치 않은 외부 의존성이 포함되는 문제를 수정합니다. - Updated dependencies [b28303c] - @seed-design/figma@0.0.18 ## 0.0.17 ### Patch Changes - @seed-design/figma@0.0.17 ## 0.0.16 ### Patch Changes - 7671445: npx, bunx로 사용하기 쉽도록 bin 진입점을 통합합니다. experimental 기능을 플래그로 분리합니다. ## 0.0.15 ### Patch Changes - Updated dependencies [4511814] - @seed-design/figma@0.0.15 ## 0.0.6 ### Patch Changes - @seed-design/figma@0.0.6 ## 0.0.5 ### Patch Changes - @seed-design/figma@0.0.5 ## 0.0.4 ### Patch Changes - @seed-design/figma@0.0.4 ## 0.0.3 ### Patch Changes - Updated dependencies [9ff6487] - @seed-design/figma@0.0.3 ## 0.0.2 ### Patch Changes - 1d9e06a: SEED Design의 MCP 서버를 제공합니다. - Updated dependencies [1d9e06a] - @seed-design/figma@0.0.2 --- ## @seed-design/migration-index ## 2.0.0 ### Major Changes - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ## 0.0.30 ### Patch Changes - 8448880: 시맨틱 stroke 컬러 토큰을 업데이트합니다. **이름이 변경되는 stroke 토큰** - [Color Role 규칙](https://seed-design.io/docs/foundation/color/color-role)에 맞춰 일관적인 토큰 이름을 유지할 수 있도록 업데이트합니다. - 이름이 변경되는 stroke 토큰을 사용하고 있는 경우, 간단한 Find & Replace 마이그레이션이 필요합니다. | 기존 | 신규 | 비고 | | ------------------------------- | ------------------------------- | ---------------------------------- | | **$color.stroke.neutral-muted** | $color.stroke.neutral-subtle | 가장 먼저 마이그레이션해야 합니다. | | $color.stroke.on-image | $color.stroke.neutral-subtle | | $color.stroke.neutral | **$color.stroke.neutral-muted** | | $color.stroke.field-focused | $color.stroke.neutral-contrast | | $color.stroke.control | $color.stroke.neutral-weak | | $color.stroke.field | $color.stroke.neutral-weak | | $color.stroke.brand | $color.stroke.brand-weak | | $color.stroke.positive | $color.stroke.positive-weak | | $color.stroke.informative | $color.stroke.informative-weak | | $color.stroke.warning | $color.stroke.warning-weak | | $color.stroke.critical | $color.stroke.critical-weak | **색상이 변경되는 stroke 토큰 (마이그레이션 불필요)** `$color.stroke.neutral-contrast` (이름 변경 전 `$color.stroke.field-focused`) 모든 theme mode에서 `$color.palette.gray-800` → `$color.palette.gray-1000`로 변경되었습니다. **신규 stroke 토큰 (마이그레이션 불필요)** | 신규 | | ------------------------------- | | $color.stroke.neutral-solid | | $color.stroke.brand-solid | | $color.stroke.positive-solid | | $color.stroke.informative-solid | | $color.stroke.warning-solid | | $color.stroke.critical-solid | ## 0.0.28 ### Patch Changes - f801300: 새로운 black, white alpha 값을 추가합니다 `$color.palette.static-black-alpha-50` (예전 값) - 예전 값: #0000000d (투명도 약 5.1%) - 변경 값: `$color.palette.static-black-alpha-200` (투명도 4.7%) `$color.palette.static-black-alpha-200` (예전 값) - 예전 값: #00000033 (투명도 20%) - 변경 값: `$color.palette.static-black-alpha-500` (투명도 17.3%) `$color.palette.static-black-alpha-500` (예전 값) - 예전 값: #00000080 (투명도 약 50.2%) - 변경 값: `$color.palette.static-black-alpha-700` (투명도 45.5%) `$color.palette.static-white-alpha-200` (예전 값) - 예전 값: #ffffff33 (투명도 20%) - 변경 값: `$color.palette.static-white-alpha-300` (투명도 18%) `$color.palette.static-white-alpha-800` (예전 값) - 예전 값: #ffffffcc (투명도 약 80%) - 변경 값: `$color.palette.static-white-alpha-800` (투명도 87.1%) - (참고: 이 값은 이름은 같지만 실제 투명도 값은 80%에서 87.1%로 변경되었습니다.) ## 0.0.20 ### Patch Changes - f17f842: - static 색상들의 맵핑을 추가해요 - `needsVerification` 필드를 추가해요 ## 0.0.18 ### Patch Changes - a7e2571: fix color mapping (divider-1, on-gray-overlay-50) ## 0.0.3 ### Patch Changes - 4f465ba: remove static next token ## 0.0.2 ### Patch Changes - 9f55b8f: add iconography index ## 0.0.1 ## 0.0.1-rc.0 ### Patch Changes - f83bbf8: migration index, codemod (vars, tailwind) --- ## @seed-design/react ## 2.0.2 ### Patch Changes - d71f402: 구형 iOS Safari(16.4 미만)에서 중첩된 `Box` 및 일부 컴포넌트가 상위 요소의 레이아웃 값을 잘못 상속하던 문제를 수정합니다. - 값을 지정하지 않은 자식 `Box`가 상위의 `width`, `height`, `gap`, `margin` 등을 물려받아 의도와 다르게 렌더링되던 문제를 해결합니다. 모던 브라우저의 동작에는 변화가 없습니다. - `SidePanel`, `BottomSheet`, `Skeleton`, `HelpBubble`에서도 동일한 상속 문제를 바로잡습니다. - `bleed`를 지정한 요소가 구형 Safari에서도 정상 동작하도록 개선합니다. ## 2.0.1 ### Patch Changes - e97a89c: `@seed-design/react`에서 `@seed-design/css/vars/component` 의존을 제거하여 ImageFrame `offsetX` `offsetY` 및 ScrollFog `size`의 기본값을 기존과 동일한 값으로 하드코딩합니다. - 7224b94: ImageFrameReactionButton root에 반투명 배경색과 그림자를 추가하여 밝은 이미지 위에서의 가시성을 개선합니다. ## 2.0.0 ### Major Changes - 4ad24cc: Box, Flex, Grid, VStack, HStack 등 레이아웃 컴포넌트에 margin 관련 프로퍼티를 추가합니다. - `margin`/`m`, `marginX`/`mx`, `marginY`/`my`, `marginTop`/`mt`, `marginRight`/`mr`, `marginBottom`/`mb`, `marginLeft`/`ml` - `"auto"` 리터럴 및 breakpoint 기반 반응형 스타일링을 지원합니다. - `margin*`과 `bleed*`를 동시에 사용할 수 없도록 타입 레벨 제약(discriminated union)을 추가합니다. - @seed-design/react를 통해 제공되는 다음 인터페이스가 `interface`에서 `type`으로 전환됩니다. - `BoxProps`, `FlexProps`, `GridProps`, `StackProps`, `VStackProps`, `HStackProps`, `ArticleProps`, `AspectRatioProps`, `ColumnsProps`, `ColumnProps`, `GridItemProps`, `ImageFrameProps`, `InlineProps`, `ResponsivePairProps`. - 위 인터페이스를 `extend`하는 타입은 `type X = BoxProps & {...}`처럼 전환할 수 있습니다. Box, Flex, Grid, VStack, HStack 등 레이아웃 컴포넌트에 4개 방향 모두에 negative margin을 적용하는 `bleed` 프로퍼티를 추가합니다. - 555525a: Attachment Field 관련 컴포넌트를 추가합니다. - Attachment Field: 파일을 선택하거나 드래그 앤 드롭으로 직접 업로드할 때 사용합니다. - Attachment Display Field: 외부 소스에서 URL로 제공된 미디어를 표시·관리할 때 사용합니다. - 029d052: Help Bubble Tooltip 컴포넌트를 추가합니다. - 54a2ff8: Layout 컴포넌트를 추가합니다. - 69e3b97: Menu 컴포넌트를 추가합니다. Drawer를 연 뒤 Drawer 뒤 요소에 포커스가 남아 있는 문제를 수정합니다. Drawer가 열리는 경우 `Drawer.Content`에 자동으로 포커스가 이동합니다. - `Drawer.RootProps`의 `autoFocus` 기본값을 `false`에서 `true`로 변경합니다. - 스크린 리더가 `modal=true` (기본값)인 Dialog 및 Drawer 뒤 요소를 읽을 수 있는 문제를 수정합니다. Dialog(AlertDialog, MenuSheet)와 Drawer(BottomSheet)를 `@radix-ui/react-dismissable-layer`에서 자체 `useDismissibleLayer` 훅 기반으로 리팩터링하고 불필요하게 외부로 노출되던 내부 옵션들을 제거합니다. - `DialogRoot` 및 `DrawerRoot`의 `onOpenChange` 두 번째 인자 `details.reason`이 `interactOutside`인 경우 `details.event`의 타입을 `PointerEvent | FocusEvent`에서 `PointerEvent | TouchEvent | FocusEvent`로 변경합니다. - `DialogRoot` 및 `DrawerRoot` 두 번째 인자 `details`의 `reason`으로 `cascadeDismiss`를 추가합니다. 두 개 이상의 오버레이 컴포넌트를 표시한 상황에서 하위 컴포넌트가 dismiss되는 경우 상위 컴포넌트는 `cascadeDismiss`와 함께 `onOpenChange`가 호출됩니다. - `@seed-design/react` 패키지에서 `@radix-ui/react-dialog` 의존성을 제거합니다. - 2abd3ed: Side Navigation에서 내부적으로 사용되는 `NavigationMenu` 컴포넌트를 추가합니다. - 9369ff9: Breakpoint 기반 반응형 스타일링을 지원합니다. - Box, Flex, Grid, VStack 등 유틸리티 컴포넌트의 레이아웃 관련 프로퍼티에 breakpoint 기반 반응형 객체를 사용할 수 있습니다. ```tsx ``` - `@seed-design/react`에서 `useBreakpoint` 훅과 `useBreakpointValue` 훅을 제공합니다. - `useBreakpoint()` — 현재 활성 breakpoint 이름을 반환합니다. (`"base"` | `"sm"` | `"md"` | `"lg"` | `"xl"`) - `useBreakpointValue(values)` — 반응형 객체에서 현재 breakpoint에 해당하는 값을 반환합니다. ```tsx const actionButtonProps = useBreakpointValue({ base: { variant: "neutralWeak" }, lg: { variant: "brandSolid" }, }); ``` ``으로 Grid를 숨길 수 없던 문제를 수정합니다. - 4839a64: `selectBoxCheckmark` recipe의 생성 클래스명과 import 경로를 다른 recipe와 동일하게 kebab-case로 정정합니다. - 클래스명: `seed-selectBoxCheckmark__*` → `seed-select-box-checkmark__*` - import 경로: `@seed-design/css/recipes/selectBoxCheckmark` → `@seed-design/css/recipes/select-box-checkmark` - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. - ec33023: Side Navigation 컴포넌트를 추가합니다. - 98b52a7: Side Panel 컴포넌트를 추가합니다. - 화면 좌/우 가장자리에서 슬라이드해 보조 콘텐츠, 상세 정보, 설정 흐름을 표시할 수 있습니다. - afb77c5: Snackbar 내부 액션 버튼 클릭 시 `onAction` 핸들러 호출 이후 스낵바가 닫히는 동작을 기본값으로 제공합니다. - snippet에 존재하던 deprecate된 `shouldCloseOnAction` 옵션(default: `true`)을 제거합니다. - 해당 동작을 React 컴포넌트로 이전하여 항상 `true`처럼 동작하도록 변경합니다. - f982d61: `SwipeableMenuSheet` 컴포넌트를 추가하고, 기존 `MenuSheet` 관련 API를 deprecate합니다. **`SwipeableMenuSheet` 추가** - Drawer 기반의 `SwipeableMenuSheet` 관련 컴포넌트를 추가합니다. - 전체 영역을 스와이프하여 시트를 닫을 수 있으며, 해당 동작의 힌트로 상단에 드래그 핸들이 표시됩니다. - `MenuSheet`에서는 항상 표시되었던 닫기 버튼을 기본적으로 UI에 표시하지 않습니다. `showCloseButton` prop을 통해 닫기 버튼의 노출 여부를 제어할 수 있습니다. **`MenuSheet` 관련 컴포넌트 및 API Deprecate** - Dialog 기반의 `MenuSheet` 관련 컴포넌트 및 API를 deprecate합니다. 신규로 Menu Sheet 사용이 필요한 경우 `SwipeableMenuSheet`를 사용합니다. - `open`/`defaultOpen`/`onOpenChange`등 기본적인 `MenuSheet`의 API는 `SwipeableMenuSheet`에서 동일하게 유지됩니다. - 7f1dbe3: `Footer.LinkText` 컴포넌트 및 `Footer` Block 예시를 추가합니다. - c0fd999: Text Input(Text Field)과 Input Button(Field Button)에 `size="medium"` variant를 추가합니다. - 31bf164: Accordion 컴포넌트를 추가합니다. - 여러 개의 관련된 콘텐츠 섹션을 수직으로 나열하고, 각 섹션을 펼치거나 접어 정보를 탐색할 수 있는 컴포넌트입니다. ### Patch Changes - Updated dependencies [555525a] - Updated dependencies [029d052] - Updated dependencies [69e3b97] - Updated dependencies [2abd3ed] - Updated dependencies [60d1a82] - Updated dependencies [ec33023] - Updated dependencies [afb77c5] - Updated dependencies [31bf164] - @seed-design/react-attachment-display@1.0.0 - @seed-design/react-middle-truncate@1.0.0 - @seed-design/react-file-upload@1.0.0 - @seed-design/react-tooltip@1.0.0 - @seed-design/react-popover@2.0.0 - @seed-design/react-dialog@2.0.0 - @seed-design/react-drawer@2.0.0 - @seed-design/react-menu@1.0.0 - @seed-design/react-navigation-menu@1.0.0 - @seed-design/react-side-navigation@1.0.0 - @seed-design/react-primitive@2.0.0 - @seed-design/react-snackbar@2.0.0 - @seed-design/react-accordion@1.0.0 - @seed-design/react-avatar@1.0.1 - @seed-design/react-checkbox@1.0.2 - @seed-design/react-collapsible@0.1.1 - @seed-design/react-field@1.0.2 - @seed-design/react-field-button@1.0.3 - @seed-design/react-fieldset@0.1.1 - @seed-design/react-image@0.1.2 - @seed-design/react-progress@1.0.1 - @seed-design/react-pull-to-refresh@1.0.2 - @seed-design/react-radio-group@1.1.1 - @seed-design/react-segmented-control@1.0.2 - @seed-design/react-slider@1.0.3 - @seed-design/react-switch@1.0.2 - @seed-design/react-tabs@1.0.6 - @seed-design/react-text-field@1.1.2 - @seed-design/react-toggle@1.0.2 ## 1.2.15 ### Patch Changes - 47690e2: SEED React 2.0.0에서 제거될 예정인 BottomSheet의 `direction` 옵션을 deprecated 처리합니다. 2.0.0부터 BottomSheet는 항상 아래에서 올라오며 `direction`을 받지 않습니다. - Updated dependencies [306673a] - @seed-design/react-drawer@1.0.11 ## 1.2.14 ### Patch Changes - 234e8c7: deprecated 항목의 제거 예정 버전 안내를 1.3.0에서 2.0.0으로 변경합니다. - `ImageFrame`의 `rounded` 옵션, `Switch`/`Checkbox`/`ChipTabs`의 deprecated 옵션, 유틸리티 스타일 prop(`display`, `flexDirection`, `justifyContent`, `alignItems`)의 deprecated 값의 제거 예정 버전을 2.0.0으로 안내합니다. - `$color.bg.layer-fill`, `$gradient.fade-layer-floating`, `$gradient.fade-layer-default` 토큰의 제거 예정 버전을 2.0.0으로 안내합니다. - 2.0.0부터 deprecated 항목 제거를 포함한 breaking change는 메이저 릴리스에서만 수행됩니다. - Updated dependencies [59887b7] - @seed-design/react-drawer@1.0.10 ## 1.2.13 ### Patch Changes - Updated dependencies [26b1de5] - @seed-design/react-dialog@1.0.3 ## 1.2.12 ### Patch Changes - Updated dependencies [e48f021] - @seed-design/react-popover@1.0.4 ## 1.2.11 ### Patch Changes - 81ec9f5: lazyMount를 사용하는 Tabs · ChipTabs에서 멀리 떨어진 탭을 선택하면 다른 탭이 열리던 문제를 해결합니다. lazyMount 및 TabsCarousel 사용 시 TabsContent 자체를 항상 렌더하고, TabsContent의 children만 lazy mount하도록 변경합니다. - 136a150: iOS 환경에서 ChipTabs + ScrollFog 사용 시 가로 스크롤이 동작하지 않는 문제를 해결합니다. - Updated dependencies [81ec9f5] - Updated dependencies [136a150] - @seed-design/react-tabs@1.0.5 ## 1.2.10 ### Patch Changes - 840f5d5: Snackbar가 표시된 상태에서 새 Snackbar를 create할 때의 동작을 정의하는 `strategy` 옵션을 추가합니다. 기본값은 `immediate`로, 새 Snackbar가 기존 Snackbar를 즉시 교체합니다. - 기존 스낵바에 할당된 시간이 모두 지난 뒤 새 Snackbar가 표시되는 이전 버전의 기존 동작을 선호하는 경우 `SnackbarProvider` 또는 `useSnackbarAdapter` 옵션으로 `queued`를 사용할 수 있습니다. - `immediate` 옵션을 모방하기 위해 `dismiss()` 후 `setTimeout(() => create(...), 0)`하던 workaround와 함께 사용해도 정상 동작하지만, 동작이 동일하므로 workaround는 제거하는 것을 권장합니다. - 934a0ba: Snackbar의 `timeout` 기본값을 5초에서 4초로 변경합니다. `timeout`을 명시적으로 지정한 경우에는 기존 동작이 유지됩니다. - Updated dependencies [840f5d5] - Updated dependencies [934a0ba] - @seed-design/react-snackbar@1.0.2 ## 1.2.9 ### Patch Changes - Updated dependencies [db6ea04] - @seed-design/react-tabs@1.0.4 ## 1.2.8 ### Patch Changes - 67a7780: Content Placeholder 컴포넌트를 추가합니다. - 9d9b891: ImageFrame 컴포넌트 개선 - `fallback` prop이 이미지 로딩 실패 시 대체 콘텐츠를 올바르게 표시하도록 개선합니다. - Reaction Button이 iOS에서 렌더링되지 않는 문제를 수정합니다. - Reaction Button uncontrolled 상태에서 클릭 시 상태가 변경되지 않는 문제를 수정합니다. ## 1.2.7 ### Patch Changes - e903fac: TagGroupRoot 내부에서 `""`, 0 등 falsy한 값으로 조건부 렌더링 시 불필요한 separator가 표시될 수 있는 문제를 수정합니다. ```tsx {/* distance === 0인 경우 separator 표시되는 문제 수정*/} {distance && } {/* label === ""인 경우 separator 표시되는 문제 수정 */} {label && } ``` - c9fb0c5: Box의 배경 색상을 class 기반으로 지정하는 경우 `:active` 스타일 선언이 배경 색상을 덮어쓰는 문제를 수정합니다. - Updated dependencies [2552b1d] - Updated dependencies [a465d74] - @seed-design/react-tabs@1.0.3 ## 1.2.6 ### Patch Changes - 77cdc0e: IdentityPlaceholder의 스타일과 글리프를 업데이트하고, `identity="business"` variant를 추가합니다. - Updated dependencies [fe1cdb3] - Updated dependencies [751e952] - Updated dependencies [7e0728a] - @seed-design/react-slider@1.0.2 - @seed-design/react-field-button@1.0.2 - @seed-design/react-toggle@1.0.1 ## 1.2.5 ### Patch Changes - 434de88: PullToRefresh.Root의 ref 타입을 실제 렌더링 요소인 HTMLDivElement로 수정해 잘못된 SVG ref 타입 요구를 해결했습니다. - 6fb6dc2: AspectRatio 스타일시트의 `position: relative`와 `overflow: hidden` 선언을 React 레이어로 이동하여 CSS specificity 문제를 수정합니다. - Updated dependencies [c46d593] - Updated dependencies [0420c89] - @seed-design/react-image@0.1.1 - @seed-design/react-drawer@1.0.9 ## 1.2.4 ### Patch Changes - a3e6859: ImageFrame `rounded` 옵션을 제거하고, borderRadius를 받을 수 있게 변경합니다 - 4287600: BottomSheet title 영역에서 닫기 버튼 간격에 따라 조정된 padding 기준으로 정리하고, word-break 추가 및 description 영역 너비를 정리했습니다. ## 1.2.3 ### Patch Changes - Updated dependencies [acae645] - @seed-design/react-snackbar@1.0.1 ## 1.2.2 ### Patch Changes - 576c2e6: `AlertDialogRoot`, `MenuSheetRoot` 및 `BottomSheetRoot`의 `onOpenChange` 두 번째 인자로 `details`를 제공합니다. `details.reason`과 `details.event`를 사용할 수 있습니다. `DialogAction`을 `DialogPrimitive.CloseButton`으로 교체합니다. `AlertDialogAction` `onClick` 핸들러에서 `event.preventDefault()`를 호출하여 닫기 동작을 방지할 수 있습니다. [(예제)](https://seed-design.io/react/components/alert-dialog#prevent-close) - Updated dependencies [576c2e6] - @seed-design/react-dialog@1.0.2 - @seed-design/react-drawer@1.0.8 ## 1.2.1 ### Patch Changes - 8ad9484: ImageFrameReactionButton의 SVG 사이즈와 Rootage 정의를 수정합니다. - Updated dependencies [8188130] - Updated dependencies [9cbeba0] - @seed-design/react-drawer@1.0.6 ## 1.2.0 ### Minor Changes - 0ecb893: [Help Bubble](/react/components/help-bubble) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet 업데이트 필요**: `HelpBubbleTrigger` 및 `HelpBubbleAnchor`의 내부 구조가 변경되었습니다. snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:help-bubble` - **인터페이스 변경사항이 없으므로 `HelpBubbleAnchor`와 `HelpBubbleTrigger`를 사용하는 기존 코드를 변경할 필요가 없습니다.** - `HelpBubble.Body`를 사용하여 `HelpBubble.Title`과 `HelpBubble.Description`을 감싸도록 변경되었습니다. - `zIndexOffset`을 활용하여 `HelpBubble.Positioner`의 z-index를 조정할 수 있습니다. ([예시](/react/components/help-bubble#z-index-offset)) - 98dbac4: [Checkbox](/react/components/checkbox) 관련 컴포넌트를 추가합니다. - `CheckboxGroup` snippet 컴포넌트가 추가되었습니다. 사용하려면 snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:checkbox` - `CheckboxGroup`은 자체적으로 gap과 100% width를 갖습니다. `VStack`을 사용하여 `Checkbox`를 묶지 않아도 됩니다. - 기존 `Checkbox`를 `CheckboxGroup`으로 감쌀 필요는 없습니다. `CheckboxGroup`은 선택적으로 사용할 수 있습니다. - `label`, `description`, `errorMessage`, `indicator`, `showRequiredIndicator`, `labelWeight` prop을 사용할 수 있습니다. [Radio Group](/react/components/radio-group) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet 업데이트 필요**: `RadioGroup` snippet의 내부 구조가 변경되었습니다. snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:radio-group` - `RadioGroup`이 자체적으로 gap과 100% width를 갖습니다. `VStack`을 사용하여 `RadioGroupItem`을 묶는 코드를 제거합니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `RadioGroupItem`을 묶어서 사용하던 `VStack`을 제거하여 `RadioGroupItem`이 `RadioGroup`의 direct child가 되도록 변경하세요. - `label`, `description`, `errorMessage`, `indicator`, `showRequiredIndicator`, `labelWeight` prop을 사용할 수 있습니다. - `@seed-design/react`의 `RadioGroup.Root`를 레이아웃 컴포넌트로 변경합니다. - `@seed-design/react`에서 직접 import해서 사용하는 코드가 있다면 `RadioGroup.Root`를 `@seed-design/react/primitive`의 `RadioGroup.Root`로 변경해주세요. ```tsx // 전 import { VStack } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; ; ``` ```tsx // 후 import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; { /* aria-label 대신 label을 사용하여 시각적으로 레이블을 표시할 수도 있습니다. */ } ; ``` ```tsx // 전 import { RadioGroup } from "@seed-design/react"; import { ListRadioItem } from "seed-design/ui/list"; } /> ; ``` ```tsx // 후 import { RadioGroup } from "@seed-design/react/primitive"; import { ListRadioItem } from "seed-design/ui/list"; } /> ; ``` RadioGroupItem, RadioChipItem, RadioSelectBoxItem, ListRadioItem에서 `invalid` prop이 제거되었습니다. - **1.1 → 1.2 업그레이드 시 확인 필요**: `invalid` 상태는 group/field 레벨에서 설정해주세요. 각 항목을 `data-invalid` 속성으로 스타일링하는 경우 확인이 필요합니다. - a58022d: `SwitchMark`를 `Switchmark`로, `RadioMark`를 `Radiomark`로 Snippet 컴포넌트 이름을 변경합니다. - **1.1 → 1.2 업그레이드 시 변경 권장**: snippet을 다시 내려받고, `SwitchMark`, `RadioMark`를 사용하는 코드를 아래와 같이 변경하세요. - `npx @seed-design/cli@latest add ui:switch ui:radio-group` - snippet에 `SwitchMark`, `RadioMark` 정의가 존재하지만, 1.3 릴리즈 시 snippet에서 해당 맵핑이 제거될 예정이므로 미리 변경해두시길 권장드립니다. ```tsx // 전 import { ListSwitchItem, ListRadioItem } from "seed-design/ui/list"; import { SwitchMark } from "seed-design/ui/switch"; import { RadioMark } from "seed-design/ui/radio-group"; } />; } value="option" title="옵션" />; ``` ```tsx // 후 import { ListSwitchItem, ListRadioItem } from "seed-design/ui/list"; import { Switchmark } from "seed-design/ui/switch"; import { Radiomark } from "seed-design/ui/radio-group"; } />; } value="option" title="옵션" />; ``` - 2643d17: [Select Box](/react/components/select-box) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet을 다시 내려받아 주세요.** - `npx @seed-design/cli@latest add ui:select-box` - `CheckSelectBoxGroup`, `RadioSelectBoxRoot`의 children이 기본적으로 gap이 포함된 그리드 레이아웃으로 정렬됩니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `CheckSelectBox`, `RadioSelectBoxItem`을 묶어서 사용하던 `VStack`을 제거하여 `CheckSelectBox`와 `RadioSelectBoxItem`이 `CheckSelectBoxGroup` 또는 `RadioSelectBoxRoot`의 direct child가 되도록 변경하세요. `VStack`에 `gap` 이외의 스타일이 적용된 경우 `...`와 같이 `VStack`을 외부에 남겨두세요. - **기능 추가**: `CheckSelectBoxGroup`와 `RadioSelectBoxRoot`에 `columns`를 지정할 수 있습니다. `columns`가 `2` 이상인 경우 하위 항목에 기본적으로 `layout="vertical"`이 적용됩니다. 기본 `layout`은 하위 항목에서 오버라이드할 수 있습니다. - **기능 추가**: `CheckSelectBoxGroup`과 `RadioSelectBoxRoot`에 `label`, `description`, `errorMessage`, `indicator` 등 Fieldset 관련 prop을 사용할 수 있습니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `CheckSelectBox`, `RadioSelectBoxItem`에 기본적으로 표시되던 `Checkmark`와 `RadioMark`가 이제 표시되지 않습니다. `suffix` prop을 통해 선택적으로 추가할 수 있습니다. - 단순 마이그레이션 시 `suffix={}`와 `suffix={}`를 추가하세요. - **기능 추가**: `prefixIcon`, `footer`, `footerVisibility` prop 추가 - `footer`에 넣는 요소는 기본적으로 해당 `CheckSelectBox` 또는 `RadioSelectBoxItem`가 선택된 상태일 때 표시됩니다. `footerVisibility="always"`를 설정하여 footer 요소를 항상 표시할 수 있습니다. - `label`이 기본적으로 가로 나열되며 `$dimension.x2` gap을 갖는 flex container로 변경되었습니다. - **1.1 → 1.2 업그레이드 시 확인 권장**: `label={{/* ... */}}`와 같은 코드는 `HStack`을 `Fragment` 등으로 대체할 수 있습니다. - **문제 수정**: `CheckSelectBox`와 `RadioSelectBoxItem`에서 사용되지 않는 `children`을 타입 정의에서 제거합니다. - `CheckSelectBoxGroup`에 `label`, `aria-label`, `aria-labelledby` 중 아무것도 설정하지 않은 경우 경고를 표시합니다. (`RadioSelectBoxRoot`는 기존에도 표시) - a0e40ca: [Tag Group](/react/components/tag-group) 관련 컴포넌트를 업데이트합니다. - `TagGroupItem` 레이블 내부에서 줄바꿈이 발생할 수 있도록 수정합니다. (기존: `TagGroupItem` 또는 separator 전후에서 줄바꿈 발생) - 한 줄 레이아웃 및 우선순위 옵션을 추가합니다. - `TagGroupRoot`에 `truncate` prop을 사용하여 한 줄로 유지하고 말줄임 처리를 할 수 있습니다. (기본값: `false`) - `TagGroupItem`에 `flexShrink` prop을 사용하여 말줄임 우선순위를 조정할 수 있습니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `TagGroupItem` 내부 레이블을 `TagGroupItemLabel`로 감싸거나, 신규로 제공되는 Snippet에서 제공하는 API로 교체해주세요. - `npx @seed-design/cli@latest add ui:tag-group` 명령어로 Snippet을 추가할 수 있습니다. ```tsx // 전 import { TagGroupRoot, TagGroupItem } from "@seed-design/react"; { /* TagGroup.Root, TagGroup.Item처럼 namespace import하는 코드가 있을 수 있습니다. */ } } /> 서초4동 광고 } color="fg.brand" /> {/* ... */} ; ``` ```tsx // 후 (Compound Component 유지) import { TagGroupRoot, TagGroupItem, TagGroupItemLabel, } from "@seed-design/react"; } /> {/* TagGroupItemLabel 사용 */} 서초4동 {/* TagGroupItemLabel 사용 */} 광고 } color="fg.brand" /> {/* ... */} ; ``` ```tsx // 후 (snippet API로 교체) // snippet 없는 경우, `npx @seed-design/cli@latest add ui:tag-group` import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; import { TagGroupRoot as SeedTagGroupRoot, TagGroupItem as SeedTagGroupItem, TagGroupItemLabel as SeedTagGroupItemLabel, } from "@seed-design/react"; } /> 광고 {/* 아이콘 커스터마이징이 필요한 경우 snippet 대신 Compound Component를 사용합니다. */} } color="fg.brand" /> {/* ... */} ; ``` - 358a1e4: [Menu Sheet](/react/components/menu-sheet) 관련 컴포넌트를 업데이트합니다. - `MenuSheetContent`에 설명을 추가할 수 있는 `description` prop이 추가되었습니다. - `MenuSheetItem`에 설명을 추가할 수 있는 `description` prop이 추가되었습니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: snippet을 다시 내려받고, `MenuSheetItem`을 사용하는 코드를 아래와 같이 변경하세요. - `npx @seed-design/cli@latest add ui:menu-sheet` - `children` 대신 `label` prop을 사용합니다. - `description`, `prefixIcon` prop이 추가되었습니다. ```tsx // 전 } /> 메뉴 항목 // 후 } label="메뉴 항목" description="이제 설명도 추가할 수 있어요" /> ``` ### Patch Changes - 477ec8a: [`Grid` 및 `GridItem`](/react/components/layout/grid) 레이아웃 유틸리티 컴포넌트를 추가합니다. - Updated dependencies [98dbac4] - Updated dependencies [2643d17] - Updated dependencies [cfd2df4] - Updated dependencies [cfd2df4] - @seed-design/react-radio-group@1.1.0 - @seed-design/react-collapsible@0.1.0 - @seed-design/react-fieldset@0.1.0 - @seed-design/react-image@0.1.0 ## 1.1.17 ### Patch Changes - 50ee0a6: `@seed-design/css@1.3` 및 `@seed-design/react@1.3`에서 제거되는 토큰 및 옵션에 관한 경고를 추가합니다. - 1.3에서 제거 예정인 색상 토큰 - [`$color.bg.layer-fill`](/docs/foundation/design-token/%24color.bg.layer-fill) - [`$gradient.fade-layer-floating`](/docs/foundation/design-token/%24gradient.fade-layer-floating) - [`$gradient.fade-layer-default`](/docs/foundation/design-token/%24gradient.fade-layer-default) - 1.3에서 제거 예정인 컴포넌트 variant - [ChipTabs](/react/components/chip-tabs) - `variant="brandSolid"` - 1.2까지 사용 가능, 1.3부터 디자인 변경 필요 - [Checkbox](/react/components/checkbox) - `weight="default"` - 0.2.4부터 `weight="regular"` 사용 가능 - `weight="default"`는 1.2까지 사용 가능, 1.3부터 `weight="regular"`만 허용 - `weight="stronger"` - 0.2.4부터 `weight="bold"` 사용 가능 - `weight="stronger"`는 1.2까지 사용 가능, 1.3부터 `weight="bold"`만 허용 - [Switch](/react/components/switch) - `size="small"` - 0.1.9부터 `size="16"` 사용 가능 - `size="small"`은 1.2까지 사용 가능, 1.3부터 `size="16"`만 허용 - `size="medium"` - 0.1.9부터 `size="32"` 사용 가능 - `size="medium"`은 1.2까지 사용 가능, 1.3부터 `size="32"`만 허용 - `StyleProps`를 상속하는 컴포넌트 - `display`, `justifyContent/justify`, `alignItems/align`, `alignContent`, `alignSelf`, `flexDirection/direction` 프로퍼티에서의 `camelCase` 값 제거 예정 - 0.0.15부터 `kebab-case` 값 사용 가능 - `camelCase` 값은 1.2까지 사용 가능, 1.3부터 `kebab-case` 값만 허용 - 예: `justifyContent="spaceBetween"` → `justifyContent="space-between"` - 영향 범위인 컴포넌트: [Box](/react/components/layout/box), [Flex](/react/components/layout/flex), [HStack](/react/components/layout/h-stack), [VStack](/react/components/layout/v-stack), [Article](/react/components/article), [List (List.Root) 및 ListItem, ListButtonItem, ListLinkItem, ListSwitchItem, ListCheckItem, ListRadioItem (List.Item)](/react/components/list), [BottomSheetBody (BottomSheet.Body)](/react/components/bottom-sheet), ResponsivePair, [Inline (deprecated)](/react/components/inline), [Columns, Column (deprecated)](/react/components/columns), [Stack (deprecated)](/react/components/stack) - Updated dependencies [db49a84] - Updated dependencies [6fab0e7] - Updated dependencies [5faef3a] - Updated dependencies [50ee0a6] - Updated dependencies [94bebf8] - Updated dependencies [8495fae] - @seed-design/css@1.1.17 ## 1.1.16 ### Patch Changes - 10c0765: 배너 템플릿에 사용되는 `$color.banner.*` 색상 토큰을 추가합니다. - Updated dependencies [2f29fe8] - Updated dependencies [9119723] - Updated dependencies [6d30b72] - Updated dependencies [10c0765] - Updated dependencies [5e462db] - @seed-design/css@1.1.16 ## 1.1.13 ### Patch Changes - cc4a45a: 신규 [Elevation 가이드](https://seed-design.io/docs/foundation/elevation)에 맞는 shadow 토큰을 추가합니다. - React: Box, Flex, HStack 등 StyleProps를 사용하는 컴포넌트에서 `boxShadow` prop을 사용하여 shadow 토큰을 쉽게 사용할 수 있습니다. - 8f54b80: unicode-segmenter 0.14.4 버전을 설치합니다. - fce8668: Divider 자체적으로 16px의 여백을 가지는 `inset` 옵션을 추가합니다. - Updated dependencies [9be0581] - Updated dependencies [cc4a45a] - Updated dependencies [739937f] - @seed-design/css@1.1.13 ## 1.1.12 ### Patch Changes - Updated dependencies [8d0ad90] - Updated dependencies [8f31f93] - Updated dependencies [69ccc6e] - Updated dependencies [279001a] - @seed-design/css@1.1.12 - @seed-design/react-dialog@1.0.1 - @seed-design/react-drawer@1.0.5 ## 1.1.10 ### Patch Changes - a2b874b: `TagGroupRoot`의 children이 `null` 또는 `undefined`를 포함하는 경우 불필요한 separator가 표시되는 문제를 수정합니다. - 12ffece: peerDeps에 `@seed-design/css` 패키지가 추가됩니다. - db5de74: PageBanner에 tone="magic" 스타일을 추가합니다. - a12e49b: Field(TextField)의 스타일을 수정합니다. - `maxGraphemeCount`를 사용하지만 `description`을 사용하지 않는 경우 `maxGraphemeCount`가 우측이 아닌 좌측에 표시되는 문제를 수정합니다. - Tailwind Preflight 사용 시 Character Count 영역이 디자인 의도보다 높이를 더 많이 차지하는 문제를 수정합니다. - Updated dependencies [db5de74] - Updated dependencies [70d11b8] - Updated dependencies [938bf0b] - Updated dependencies [c03a3dd] - Updated dependencies [a12e49b] - @seed-design/css@1.1.10 - @seed-design/react-tabs@1.0.2 ## 1.1.8 ### Patch Changes - 8752805: List Item에 신규 active(pressed) 스타일을 적용하고, disabled 상태에서 detail 영역의 색상을 수정합니다. - 8edbf00: @seed-design/react에서 unicode-segmenter가 externalize되지 않는 문제를 수정합니다. - e3806c1: BottomSheet에 handleOnly 옵션이 정상적으로 동작하지 않는 이슈를 수정합니다 - Updated dependencies [e3806c1] - @seed-design/react-drawer@1.0.4 ## 1.1.7 ### Patch Changes - f4c62f6: Scroll Fog 컴포넌트가 항상 fog를 표시하게 변경하고 padding 가이드라인을 추가합니다 - 1340675: Slider Value Indicator가 표시되는 조건을 설정하는 `valueIndicatorTrigger` prop을 추가합니다. ("active"|"hover", 기본값: "active") - 1340675: Slider Value Indicator가 Track 양 끝에 있을 때 Track 바깥 영역을 차지하지 않도록 수정합니다. - Updated dependencies [1340675] - Updated dependencies [1340675] - @seed-design/react-slider@1.0.1 ## 1.1.5 ### Patch Changes - 03ff678: BottomSheetBody에 제공한 style 관련 prop(`paddingX` 등)이 적용되지 않고 DOM으로 bleed되는 문제를 수정합니다. - ae1b768: :focus-visible selector를 사용하기 전 브라우저에서 selector를 지원하는지 확인합니다. - Updated dependencies [53290ab] - Updated dependencies [cc8864d] - Updated dependencies [ae1b768] - @seed-design/react-field-button@1.0.1 - @seed-design/react-drawer@1.0.3 - @seed-design/react-segmented-control@1.0.1 - @seed-design/react-radio-group@1.0.1 - @seed-design/react-text-field@1.1.1 - @seed-design/react-checkbox@1.0.1 - @seed-design/react-switch@1.0.1 - @seed-design/react-field@1.0.1 - @seed-design/react-tabs@1.0.1 ## 1.1.4 ### Patch Changes - 77d304d: `@radix-ui/react-dialog` 의존성을 추가해 React 패키지를 Portable하게 수정합니다 ## 1.1.3 ### Patch Changes - 2c302a5: PopoverPositionerPortal과 HelpBubblePositionerPortal을 추가합니다. - bc3cd6f: ScrollFog 컴포넌트를 추가합니다 - Updated dependencies [2c302a5] - Updated dependencies [bc3cd6f] - Updated dependencies [4102a4b] - Updated dependencies [e272ef8] - Updated dependencies [fbc9cb0] - Updated dependencies [4971dcc] - @seed-design/react-popover@1.0.3 - @seed-design/react-scrollable@1.0.0 - @seed-design/react-drawer@1.0.2 ## 1.1.1 ### Patch Changes - Updated dependencies [68b5eab] - @seed-design/react-drawer@1.0.1 ## 1.1.0 ### Minor Changes - a55f584: Slider 컴포넌트를 추가합니다. - 191005f: Action Button 컴포넌트를 `variant=ghost`로 사용하는 경우 `fontWeight`를 사용자화할 수 있도록 업데이트합니다. (BREAKING CHANGE: Error State snippet을 다시 설치해야 합니다.) Error State 스니펫에서 Action Button을 활용하도록 업데이트합니다. - 33def2d: (BREAKING CHANGE: BottomSheet snippet을 다시 설치해야 합니다.) BottomSheet에 드래그를 통해 닫는 기능을 추가합니다. - vaul headless 코드 기반으로 seed에 맞게 커스텀하여 구현했습니다. - vaul과 동일한 인터페이스를 가지고 있습니다. (snap-points, fade-from-index, etc.) - `npx @seed-design/cli@latest add ui:bottom-sheet`로 snippet을 최신화하세요. ### Patch Changes - d6bb84d: (BREAKING CHANGE: TextField snippet을 다시 설치해야 합니다.) Text Field 관련 컴포넌트를 업데이트합니다. - 스타일 업데이트 - size 통일 및 variant (underline) 추가 - 내부적으로 Field 컴포넌트를 사용하도록 변경하여 스타일 일관성 향상 Field Button 컴포넌트를 추가합니다. - 6af6501: (BREAKING CHANGE: PageBanner snippet을 다시 설치해야 합니다.) Page Banner 스니펫을 업데이트합니다. - Box를 사용하여 스타일링하던 부분을 `PageBanner.Body`로 교체합니다. - `PageBanner.TextContent`를 `PageBanner.Content`로 이름 변경합니다. - Updated dependencies [d6bb84d] - Updated dependencies [a55f584] - Updated dependencies [33def2d] - Updated dependencies [0c1ab6a] - @seed-design/react-field-button@1.0.0 - @seed-design/react-text-field@1.1.0 - @seed-design/react-slider@1.0.0 - @seed-design/react-field@1.0.0 - @seed-design/react-drawer@1.0.0 - @seed-design/react-popover@1.0.2 ## 1.0.7 ### Patch Changes - Updated dependencies [e52d6d1] - Updated dependencies [97669bc] - Updated dependencies [15ab93a] - Updated dependencies [50366c0] - @seed-design/css@1.0.7 ## 1.0.6 ### Patch Changes - 6aafce0: Tag Group 컴포넌트를 추가합니다. Tag Group은 아이콘 및 텍스트로 이루어진 태그를 구분 기호와 함께 수평 레이아웃으로 표시하는 컴포넌트입니다. - f2ddf29: Article 유틸리티 컴포넌트를 추가하고 Text 컴포넌트를 업데이트합니다. - Article 컴포넌트는 일관된 selection 스타일 및 줄바꿈 정책을 사용할 수 있게 돕습니다. - Text 컴포넌트에서 textDecorationLine="underline" 및 whiteSpace, userSelect prop을 지원합니다. - Updated dependencies [6aafce0] - Updated dependencies [1902dfa] - Updated dependencies [f2ddf29] - Updated dependencies [4c33f07] - Updated dependencies [3df657f] - @seed-design/css@1.0.6 ## 1.0.5 ### Patch Changes - 687b261: `PullToRefresh.preventPull`을 활용하여 `PullToRefreshContent` 내부에서 당겨서 새로고침(PTR) 동작을 비활성화할 수 있습니다. - a839fd2: 실제 기본값을 표시하도록 JSDoc을 업데이트합니다. - Updated dependencies [f1cf4cd] - Updated dependencies [687b261] - Updated dependencies [9b91751] - Updated dependencies [3898183] - @seed-design/css@1.0.5 - @seed-design/react-pull-to-refresh@1.0.1 ## 1.0.4 ### Patch Changes - Updated dependencies [b10ff0b] - @seed-design/react-popover@1.0.1 ## 1.0.3 ### Patch Changes - Updated dependencies [0b8a02e] - Updated dependencies [6c6099d] - @seed-design/css@1.0.3 ## 1.0.2 ### Patch Changes - Updated dependencies [6d2e13d] - @seed-design/css@1.0.2 ## 1.0.1 ### Patch Changes - Updated dependencies [1420b68] - @seed-design/css@1.0.1 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Minor Changes - 39a96f1: (**BREAKING CHANGE**: Snackbar Snippet을 다시 설치해야합니다) Snackbar 컴포넌트 변경 - Snackbar의 배경색이 다크모드에서 흰색으로 변경됩니다. - Prefix 요소유무에 따라 여백이 변경됩니다. - `npx @seed-design/cli@latest add ui:snackbar` 명령어로 설치하세요. ### Patch Changes - e038490: (**BREAKING CHANGE**: Snippet을 다시 설치해야 합니다.) Manner Temp, Manner Temp Badge 컴포넌트를 업데이트합니다. - snippet 내 오타 수정 - 신규 10단계 반영 - 업데이트 가이드 1. `@seed-design/css@latest @seed-design/react@latest` 설치 2. `npx @seed-design/cli@latest add ui:manner-temp ui:manner-temp-badge`로 snippet 최신화 3. 온도 범위가 변경되었으므로, `` 혹은 ``과 같이 `level`을 직접 지정하여 사용하고 있는 경우가 있는지 확인 - a7d07f0: (**BREAKING CHANGE**: `SwitchMark` 사용을 위해서는 Snippet을 다시 설치해야 합니다.) Switch의 토글 영역만을 정의한 Switch Mark 컴포넌트를 추가합니다. - `npx @seed-design/cli@latest add ui:switch` 명령어로 설치하세요. (**BREAKING CHANGE**: `ListHeader` 사용을 위해서는 Snippet을 다시 설치해야 합니다.) List Header 컴포넌트를 추가합니다. - `npx @seed-design/cli@latest add ui:list` 명령어로 설치하세요. - Updated dependencies [39a96f1] - Updated dependencies [34f92f2] - Updated dependencies [e038490] - Updated dependencies [4153ca5] - Updated dependencies [a7d07f0] - @seed-design/css@1.0.0 - @seed-design/react-avatar@1.0.0 - @seed-design/react-checkbox@1.0.0 - @seed-design/react-dialog@1.0.0 - @seed-design/react-popover@1.0.0 - @seed-design/react-portal@1.0.0 - @seed-design/react-primitive@1.0.0 - @seed-design/react-progress@1.0.0 - @seed-design/react-pull-to-refresh@1.0.0 - @seed-design/react-radio-group@1.0.0 - @seed-design/react-segmented-control@1.0.0 - @seed-design/react-snackbar@1.0.0 - @seed-design/react-switch@1.0.0 - @seed-design/react-tabs@1.0.0 - @seed-design/react-text-field@1.0.0 - @seed-design/react-toggle@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.2.5 ### Patch Changes - Updated dependencies [0ca19c0] - Updated dependencies [11f5e76] - @seed-design/css@0.2.5 - @seed-design/react-snackbar@0.0.7 ## 0.2.4 ### Patch Changes - 8ebe8a5: Switch, Checkmark, Radio Mark의 스타일을 업데이트합니다. - tone=neutral variant를 추가합니다. - Switch의 thumb 크기를 조정합니다. Checkbox와 Radio의 weight variant를 default, stronger에서 regular, bold로 수정합니다. - Updated dependencies [8ebe8a5] - Updated dependencies [f61b80d] - Updated dependencies [ce047f5] - @seed-design/css@0.2.4 - @seed-design/react-tabs@0.0.9 ## 0.2.3 ### Patch Changes - 12faf5a: List 컴포넌트를 추가하고, Checkbox 및 Radio 컴포넌트를 개선합니다. - List 컴포넌트를 제공하여, 정보를 구조화된 목록 형태로 표시할 수 있도록 합니다. - Checkbox와 Radio의 컨트롤 영역만을 표시하는 Checkmark와 RadioMark를 제공합니다. - Select Box에서 컨트롤 영역을 Checkmark와 RadioMark로 교체합니다. - RadioGroup 컴포넌트를 제공합니다. - Updated dependencies [a22b8b9] - Updated dependencies [5836976] - Updated dependencies [12faf5a] - @seed-design/css@0.2.3 ## 0.2.2 ### Patch Changes - 9d93518: Text 컴포넌트의 fontSize, lineHeight, color 속성에 string도 사용 가능하도록 변경했습니다. ## 0.2.1 ### Patch Changes - 35984d0: Chip 컴포넌트를 업데이트합니다. - 아이콘에 트랜지션 효과가 적용되지 않던 현상을 수정합니다. - Button, Toggle 등 사용되는 방식에 따라 적절한 data prop을 받도록 수정합니다. - c5bed96: Divider 컴포넌트가 `$color.stroke.neutral-muted` 색상을 기본값으로 사용하도록 수정합니다. - Updated dependencies [35984d0] - @seed-design/css@0.2.1 ## 0.2.0 ### Patch Changes - Updated dependencies [8448880] - @seed-design/css@0.2.0 ## 0.1.15 ### Patch Changes - c51a261: font-size, line-height 토큰에 static variant를 추가합니다. - `--seed-font-size-t1-static` ~ `--seed-font-size-t10-static` - `--seed-line-height-t1-static` ~ `--seed-line-height-t10-static` - 9a3c76a: Divider 컴포넌트를 업데이트합니다. - `orientation`을 지정할 수 있습니다. - Divider를 `li`로 렌더링하여 `ol`, `ul` 내부에서 사용할 수 있습니다. - Divider를 `div` 또는 `li`로 렌더링하는 경우에도 `role="separator"`를 지정하여 스크린 리더가 Divider를 읽도록 할 수 있습니다. - Updated dependencies [c51a261] - Updated dependencies [5f2ee39] - Updated dependencies [8299ba9] - Updated dependencies [3de4cec] - @seed-design/css@0.1.15 - @seed-design/react-snackbar@0.0.6 ## 0.1.14 ### Patch Changes - f806356: Page Banner 컴포넌트를 추가합니다. Inline Banner 컴포넌트를 deprecate합니다. - Inline Banner 컴포넌트 대비 모든 `tone`에서 모든 `variant`를 지원하며, 내부 Button의 충분한 터치 영역을 보장합니다. ```tsx 새 탭에서 열기 } /> ``` 시맨틱 색상 토큰을 추가하고 수정합니다. - `$color.bg.positive-solid-pressed`: theme-dark에서 `$color.palette.green-500` → `$color.palette.green-600` - `$color.bg.warning-solid-pressed` 추가 - 1982494: Badge 컴포넌트를 업데이트합니다. - `tone=warning` variant를 추가합니다. - `maxWidth` 스펙을 추가합니다. 신규 시맨틱 색상 토큰을 추가합니다. - `$color.fg.warning` - `$color.stroke.warning` - `$color.fg.brand-contrast` - `$color.bg.brand-weak` - `$color.bg.brand-weak-pressed` - Updated dependencies [f806356] - Updated dependencies [1982494] - @seed-design/css@0.1.14 ## 0.1.13 ### Patch Changes - Updated dependencies [0be9b00] - @seed-design/css@0.1.13 ## 0.1.12 ### Patch Changes - 62094b6: Help Bubble의 스타일 문제를 수정합니다. - `placement=left-*` / `placement=right-*`에서 arrow가 content와 떨어져 표시되는 문제를 수정합니다. - Updated dependencies [62094b6] - @seed-design/react-popover@0.0.8 - @seed-design/css@0.1.12 ## 0.1.11 ### Patch Changes - 9993e0e: 레이아웃 컴포넌트를 사용할 때 `flexGrow`, `flexShrink`, `flexWrap`에도 `true`를 사용할 수 있도록 수정합니다. Figma 레이어가 이미지 Fill을 가지고 있는 경우 `` 요소를 prepend합니다. ## 0.1.10 ### Patch Changes - Updated dependencies [ef91c21] - @seed-design/css@0.1.10 ## 0.1.9 ### Patch Changes - 5a025b7: Switch 컴포넌트를 업데이트합니다. - size: medium → 32, small → 16으로 rename합니다. - (React) `size="medium"`으로 `32`, `size="small"`로 `16`을 사용할 수 있습니다. (deprecated) - size: 24를 추가합니다. - 모든 size에 대해 레이블 스타일을 추가합니다. (기존: small에만 존재) - f9041e9: `CheckSelectBox`, `RadioSelectBox`의 `label`, `description` 영역을 수정합니다. - `span` 대신 `div`를 렌더링합니다. - 기본적으로 grow하도록 만들어 Badge 등 추가 요소를 넣기 쉽게 만듭니다. - Updated dependencies [5a025b7] - Updated dependencies [ac35731] - Updated dependencies [f9041e9] - @seed-design/css@0.1.9 ## 0.1.8 ### Patch Changes - Updated dependencies [609b8f3] - @seed-design/css@0.1.8 ## 0.1.7 ### Patch Changes - Updated dependencies [4afe80b] - @seed-design/css@0.1.7 ## 0.1.6 ### Patch Changes - Updated dependencies [235147d] - Updated dependencies [3c13ad7] - @seed-design/css@0.1.6 ## 0.1.5 ### Patch Changes - 861ecb4: Menu Sheet 컴포넌트를 추가하는 동시에 Action Sheet과 Extended Action Sheet 컴포넌트를 deprecate합니다. - [Menu Sheet React 문서](https://seed-design.io/react/components/menu-sheet) - Menu Sheet는 기존 Extended Action Sheet의 모든 기능을 포함하는 동시에, `labelAlign` prop으로 `MenuSheetItem`를 `left` 또는 `center`로 정렬할 수 있습니다. - Updated dependencies [861ecb4] - Updated dependencies [3889eb6] - @seed-design/css@0.1.5 ## 0.1.4 ### Patch Changes - 0ffcd48: Chip 컴포넌트가 추가되고, ActionChip, ControlChip 컴포넌트가 Deprecated 되었습니다. - [Chip 컴포넌트](https://seed-design.io/react/components/chip) - Chip 컴포넌트는 버튼과 토글 컴포넌트를 모두 포함하고 있습니다. - 56e03ca: Layout 컴포넌트 `pb`, `pt`, `paddingBottom`, `paddingTop` 속성에 `safeArea` 값을 지정할 수 있도록 지원 ```tsx ``` - Updated dependencies [0ffcd48] - @seed-design/css@0.1.4 ## 0.1.3 ### Patch Changes - Updated dependencies [cdc0930] - Updated dependencies [946faf7] - Updated dependencies [71c58fd] - @seed-design/css@0.1.3 ## 0.1.2 ### Patch Changes - 00eafa3: package.json에 `types` 필드를 추가합니다. - Updated dependencies [7b2c0f3] - @seed-design/css@0.1.2 ## 0.1.1 ### Patch Changes - Updated dependencies [e3b782d] - @seed-design/css@0.1.1 ## 0.1.0 ### Minor Changes - b0f7a4e: AI Gradient 관련 토큰을 추가합니다. (0.0.41) - direction 관련 속성을 css property에 맞게 변경합니다. (0.1.0) - 7cc6087: HelpBubble의 arrow가 상위 요소의 font-size에 영향을 받는 것을 수정합니다 - bdca898: BottomSheet의 description font-size를 t5로 변경합니다 ### Patch Changes - Updated dependencies [7cc6087] - Updated dependencies [bdca898] - @seed-design/css@0.1.0 ## 0.0.41 ### Patch Changes - 561f74c: Text 컴포넌트에 `textDecorationLine` 옵션을 추가합니다. - b43de05: Gradient 컬러를 추가합니다 - Updated dependencies [561f74c] - Updated dependencies [b43de05] - @seed-design/css@0.0.41 ## 0.0.39 ### Patch Changes - Updated dependencies [f801300] - @seed-design/css@0.0.39 ## 0.0.38 ### Patch Changes - 145b718: Float 컴포넌트의 기본 display를 inline-flex로 변경합니다. - 70fbaaf: Action Button에 type="ghost"를 추가합니다. - Updated dependencies [70fbaaf] - @seed-design/css@0.0.38 ## 0.0.35 ### Patch Changes - Updated dependencies [0789dc8] - @seed-design/css@0.0.35 ## 0.0.34 ### Patch Changes - 2fc411d: Icon 컴포넌트의 size, color style prop 타입을 다른 컴포넌트와 동일하게 수정합니다. - Updated dependencies [92801a2] - @seed-design/css@0.0.34 ## 0.0.33 ### Patch Changes - fbdb091: Style prop에 `_active`를 추가합니다. background 속성만을 지원합니다. - Updated dependencies [fbdb091] - @seed-design/css@0.0.33 ## 0.0.32 ### Patch Changes - e9db89f: 레이아웃 컴포넌트에 `asChild` 속성을 추가합니다. - abfda51: Text 컴포넌트에 ref forwarding을 추가합니다. ## 0.0.31 ### Patch Changes - 408d7ef: Icon 컴포넌트의 size의 IDE 자동완성을 개선합니다. - fd7c569: - Tabs.Carousel을 사용하는 경우 Hydration 이후 스크롤 애니매이션이 발생하는 문제를 수정합니다. - Tabs.Carousel의 드래그 제스처를 방지하는 영역을 선언할 수 있는 `Tabs.carouselPreventDrag` api를 추가합니다. - layout=hug일 때 Indicator에서 발생하는 Layout Shift를 수정합니다. - lazyMount 옵션이 의도와 다르게 모든 탭이 한꺼번에 마운트되는 문제를 수정합니다. - Updated dependencies [fd7c569] - @seed-design/react-tabs@0.0.8 - @seed-design/css@0.0.31 ## 0.0.30 ### Patch Changes - 4610b5b: PullToRefresh에 disabled prop을 추가합니다. - 739b6bf: Tabs.Indicator의 width가 첫 렌더링 시 0으로 설정되는 문제를 수정합니다. Tabs의 불필요한 리렌더링을 줄입니다. - 285cb9b: - `ContextualFloatingButton`과 `FloatingActionButton` 컴포넌트를 제공합니다. - 기존의 `Fab` 및 `ExtendedFab`를 deprecate합니다. - Floating 요소들의 위치를 편리하게 제어하도록 `Float` 유틸리티 컴포넌트를 제공합니다. - Updated dependencies [4610b5b] - Updated dependencies [739b6bf] - Updated dependencies [285cb9b] - @seed-design/react-pull-to-refresh@0.0.6 - @seed-design/react-tabs@0.0.7 - @seed-design/css@0.0.30 ## 0.0.29 ### Patch Changes - 29ec9f0: `reactSlot.createSlot is not a function` 오류가 발생하지 않도록, radix-ui/react-slot 버전을 1.2.3으로 수정합니다. - Updated dependencies [116ee2c] - Updated dependencies [29ec9f0] - @seed-design/css@0.0.29 - @seed-design/react-primitive@0.0.3 - @seed-design/react-avatar@0.0.4 - @seed-design/react-checkbox@0.0.4 - @seed-design/react-dialog@0.0.5 - @seed-design/react-popover@0.0.7 - @seed-design/react-progress@0.0.4 - @seed-design/react-pull-to-refresh@0.0.5 - @seed-design/react-radio-group@0.0.4 - @seed-design/react-segmented-control@0.0.5 - @seed-design/react-snackbar@0.0.5 - @seed-design/react-switch@0.0.4 - @seed-design/react-tabs@0.0.6 - @seed-design/react-text-field@0.0.4 - @seed-design/react-toggle@0.0.4 ## 0.0.28 ### Patch Changes - Updated dependencies [5337e14] - @seed-design/css@0.0.28 ## 0.0.27 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. - Updated dependencies [9d85c16] - Updated dependencies [d951317] - Updated dependencies [7851a31] - Updated dependencies [b3f964d] - @seed-design/css@0.0.27 - @seed-design/react-segmented-control@0.0.4 - @seed-design/react-pull-to-refresh@0.0.4 - @seed-design/react-radio-group@0.0.3 - @seed-design/react-text-field@0.0.3 - @seed-design/react-checkbox@0.0.3 - @seed-design/react-progress@0.0.3 - @seed-design/react-snackbar@0.0.4 - @seed-design/react-popover@0.0.6 - @seed-design/react-avatar@0.0.3 - @seed-design/react-dialog@0.0.4 - @seed-design/react-portal@0.0.2 - @seed-design/react-switch@0.0.3 - @seed-design/react-toggle@0.0.3 - @seed-design/react-tabs@0.0.5 ## 0.0.25 ### Patch Changes - c87ede9: Avatar Stack의 디자인을 업데이트합니다. - Updated dependencies [c87ede9] - @seed-design/css@0.0.25 ## 0.0.24 ### Patch Changes - 4da536f: ActionSheet의 header가 렌더링되지 않을 때 상단 radius가 누락되는 버그를 수정합니다. - 3efe201: `` 컴포넌트를 제공합니다. - Updated dependencies [4da536f] - Updated dependencies [3efe201] - @seed-design/css@0.0.24 - @seed-design/react-portal@0.0.1 ## 0.0.23 ### Patch Changes - Updated dependencies [63e1541] - @seed-design/css@0.0.23 ## 0.0.21 ### Patch Changes - 7ae87f8: 2개의 컨텐츠를 동일한 비율로 나누어 배치하되, 너무 긴 경우 세로로 접는 `` 컴포넌트를 추가합니다. - f144d28: BottomSheet, Dialog의 배경 색상을 layer-floating으로 변경합니다. - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [5d69d1d] - Updated dependencies [4d34760] - Updated dependencies [7ae87f8] - Updated dependencies [f144d28] - Updated dependencies [e368c69] - @seed-design/css@0.0.21 - @seed-design/react-segmented-control@0.0.3 - @seed-design/react-pull-to-refresh@0.0.3 - @seed-design/react-radio-group@0.0.2 - @seed-design/react-text-field@0.0.2 - @seed-design/react-primitive@0.0.2 - @seed-design/react-checkbox@0.0.2 - @seed-design/react-progress@0.0.2 - @seed-design/react-snackbar@0.0.3 - @seed-design/react-popover@0.0.5 - @seed-design/react-avatar@0.0.2 - @seed-design/react-dialog@0.0.3 - @seed-design/react-switch@0.0.2 - @seed-design/react-toggle@0.0.2 - @seed-design/react-tabs@0.0.4 - @seed-design/dom-utils@0.0.2 ## 0.0.19 ### Patch Changes - Updated dependencies [3c9ec66] - Updated dependencies [b3bb6e7] - @seed-design/css@0.0.19 ## 0.0.17 ### Patch Changes - c042f90: recipe에서 직접 스타일시트 의존성을 표현하도록 변경합니다. - Updated dependencies [c042f90] - @seed-design/css@0.0.17 ## 0.0.15 ### Patch Changes - 1bb9f7b: - vite dev에서 컴포넌트 스타일시트가 로드되지 않는 버그를 수정합니다. - 플러그인이 컴포넌트 스타일시트를 로드하는 방식을 변경합니다. - 4511814: - 레이아웃 및 flex 관련 shorthand prop을 추가합니다. (px, py, wrap, align, justify, direction) - ActionButton에 flexGrow prop을 추가합니다. - VStack, HStack 컴포넌트를 추가합니다. - Stack, Inline, Columns 컴포넌트를 deprecated 처리합니다. - 디자인 토큰이 아닌 css prop의 value가 유효한 css value가 되도록 변경합니다. - flexStart, spaceBetween 등 camelCase로 제공되는 값을 deprecated 처리합니다. - d49e697: - Divider의 굵기가 의도보다 굵게 렌더링되는 버그 수정 - borderColor, borderWidth 대신 color, thickness로 인터페이스 변경 - f4b0723: HelpBubble 디자인 스펙 업데이트 (shadow) - f4b0723: HelpBubble의 enter, exit 모션을 추가합니다. - Updated dependencies [1bb9f7b] - Updated dependencies [4511814] - Updated dependencies [f4b0723] - Updated dependencies [f4b0723] - @seed-design/css@0.0.15 - @seed-design/react-popover@0.0.4 ## 0.0.14 ### Patch Changes - 87599b0: Divider 컴포넌트를 추가합니다. - 92c0b80: HelpBubble 디자인 스펙 업데이트 (shadow) - c1d94d0: HelpBubble의 enter, exit 모션을 추가합니다. - Updated dependencies [92c0b80] - Updated dependencies [c1d94d0] - @seed-design/css@0.0.14 - @seed-design/react-popover@0.0.3 ## 0.0.13 ### Patch Changes - 7fca755: Avatar의 Badge 스펙을 최신화합니다. - Updated dependencies [7fca755] - Updated dependencies [c0c0b7e] - @seed-design/css@0.0.13 - @seed-design/react-pull-to-refresh@0.0.2 ## 0.0.12 ### Patch Changes - 6426379: 유틸리티 컴포넌트에 사용되는 ScopedColorFg, ScopedColorBg, ScopedColorPalette, ScopedColorStroke 타입을 제공합니다. - f5858ad: feat: icon scope를 `@daangn`에서 `@karrotmarket` 으로 변경해요 - Updated dependencies [fee050d] - Updated dependencies [6426379] - Updated dependencies [ee41f37] - @seed-design/react-tabs@0.0.3 - @seed-design/css@0.0.12 ## 0.0.11 ### Patch Changes - Updated dependencies [e70f340] - Updated dependencies [72f344f] - @seed-design/css@0.0.11 ## 0.0.10 ### Patch Changes - e4b704c: Avatar size=42를 추가합니다. - de5901d: Icon 컴포넌트에 color, size prop을 추가합니다. - Updated dependencies [e4b704c] - Updated dependencies [09fecb9] - @seed-design/css@0.0.10 - @seed-design/react-segmented-control@0.0.2 - @seed-design/react-snackbar@0.0.2 - @seed-design/react-popover@0.0.2 - @seed-design/react-dialog@0.0.2 - @seed-design/react-tabs@0.0.2 ## 0.0.9 ### Patch Changes - 63f8651: MannerTemp 컴포넌트를 추가합니다. - Updated dependencies [63f8651] - Updated dependencies [d9b01a9] - @seed-design/css@0.0.9 ## 0.0.8 ### Patch Changes - 1424700: Notification Badge를 추가합니다. - Tabs의 Notification 슬롯을 Notification Badge로 변경합니다. - Updated dependencies [1424700] - Updated dependencies [0efeea1] - @seed-design/css@0.0.8 ## 0.0.7 ### Patch Changes - Updated dependencies [8aca3de] - @seed-design/css@0.0.7 ## 0.0.6 ### Patch Changes - 3d66c5b: visuallyHidden을 recipe에서 제거합니다. - Updated dependencies [bf198e8] - Updated dependencies [3d66c5b] - Updated dependencies [a8d5242] - Updated dependencies [ccf3989] - @seed-design/css@0.0.6 ## 0.0.5 ### Patch Changes - e3234e7: single-slot recipe를 위한 간소화된 인터페이스를 추가합니다. - Updated dependencies [e3234e7] - Updated dependencies [5502bed] - @seed-design/css@0.0.5 ## 0.0.4 ### Patch Changes - 6df5d19: Badge 디자인 업데이트 - neutral tone 색상 변경 - pill shape 삭제 - Updated dependencies [6df5d19] - Updated dependencies [5cb50e7] - @seed-design/css@0.0.4 ## 0.0.3 ### Patch Changes - Updated dependencies [a33af94] - Updated dependencies [b180822] - @seed-design/css@0.0.3 ## 0.0.2 ### Patch Changes - Updated dependencies [d04e344] - @seed-design/css@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/css@0.0.1 - @seed-design/react-avatar@0.0.1 - @seed-design/react-checkbox@0.0.1 - @seed-design/react-dialog@0.0.1 - @seed-design/react-popover@0.0.1 - @seed-design/react-primitive@0.0.1 - @seed-design/react-progress@0.0.1 - @seed-design/react-pull-to-refresh@0.0.1 - @seed-design/react-radio-group@0.0.1 - @seed-design/react-segmented-control@0.0.1 - @seed-design/react-snackbar@0.0.1 - @seed-design/react-switch@0.0.1 - @seed-design/react-tabs@0.0.1 - @seed-design/react-text-field@0.0.1 - @seed-design/react-toggle@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.4 ### Patch Changes - Updated dependencies [93cfc30] - @seed-design/css@0.0.1-rc.4 ## 0.0.1-rc.3 ### Patch Changes - cc4b2c5: fix: externalize subpath imports from `@seed-design/css` refactor: streamline package configurations refactor(qvism): generate recipe-shared module from cli - Updated dependencies [cc4b2c5] - @seed-design/css@0.0.1-rc.3 ## 0.0.1-rc.2 ### Patch Changes - Updated dependencies [14c9983] - @seed-design/css@0.0.1-rc.1 ## 0.0.1-rc.1 ### Patch Changes - 6ee6544: re-export stylesheet from @seed-design/css package. ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/css@0.0.1-rc.0 - @seed-design/react-avatar@0.0.1-rc.0 - @seed-design/react-checkbox@0.0.1-rc.0 - @seed-design/react-dialog@0.0.1-rc.0 - @seed-design/react-popover@0.0.1-rc.0 - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/react-progress@0.0.1-rc.0 - @seed-design/react-pull-to-refresh@0.0.1-rc.0 - @seed-design/react-radio-group@0.0.1-rc.0 - @seed-design/react-segmented-control@0.0.1-rc.0 - @seed-design/react-snackbar@0.0.1-rc.0 - @seed-design/react-switch@0.0.1-rc.0 - @seed-design/react-tabs@0.0.1-rc.0 - @seed-design/react-text-field@0.0.1-rc.0 - @seed-design/react-toggle@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 --- ## @seed-design/react-accordion ## 1.0.0 ### Major Changes - 31bf164: Accordion 컴포넌트를 추가합니다. - 여러 개의 관련된 콘텐츠 섹션을 수직으로 나열하고, 각 섹션을 펼치거나 접어 정보를 탐색할 수 있는 컴포넌트입니다. ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 - @seed-design/react-collapsible@0.1.1 --- ## @seed-design/react-attachment-display ## 1.0.0 ### Major Changes - 555525a: Attachment Field 관련 컴포넌트를 추가합니다. - Attachment Field: 파일을 선택하거나 드래그 앤 드롭으로 직접 업로드할 때 사용합니다. - Attachment Display Field: 외부 소스에서 URL로 제공된 미디어를 표시·관리할 때 사용합니다. ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 --- ## @seed-design/react-avatar ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.4 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.3 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.2 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/react-primitive@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 ## 0.0.0-alpha-20241030023710 ### Patch Changes - alpha - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241030023710 ## 0.0.0-alpha-20241004093556 ### Patch Changes - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241004093556 --- ## @seed-design/react-checkbox ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.1 ### Patch Changes - ae1b768: :focus-visible selector를 사용하기 전 브라우저에서 selector를 지원하는지 확인합니다. - Updated dependencies [ae1b768] - @seed-design/react-supports@0.0.1 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.4 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.3 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.2 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/react-primitive@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 ## 0.0.0-alpha-20241030023710 ### Patch Changes - alpha - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241030023710 ## 0.0.0-alpha-20241004093556 ### Patch Changes - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241004093556 --- ## @seed-design/react-collapsible ## 1.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 0.1.0 ### Minor Changes - cfd2df4: fieldset, collapsible headless 릴리즈 --- ## @seed-design/react-dialog ## 2.0.0 ### Major Changes - 69e3b97: Menu 컴포넌트를 추가합니다. Drawer를 연 뒤 Drawer 뒤 요소에 포커스가 남아 있는 문제를 수정합니다. Drawer가 열리는 경우 `Drawer.Content`에 자동으로 포커스가 이동합니다. - `Drawer.RootProps`의 `autoFocus` 기본값을 `false`에서 `true`로 변경합니다. - 스크린 리더가 `modal=true` (기본값)인 Dialog 및 Drawer 뒤 요소를 읽을 수 있는 문제를 수정합니다. Dialog(AlertDialog, MenuSheet)와 Drawer(BottomSheet)를 `@radix-ui/react-dismissable-layer`에서 자체 `useDismissibleLayer` 훅 기반으로 리팩터링하고 불필요하게 외부로 노출되던 내부 옵션들을 제거합니다. - `DialogRoot` 및 `DrawerRoot`의 `onOpenChange` 두 번째 인자 `details.reason`이 `interactOutside`인 경우 `details.event`의 타입을 `PointerEvent | FocusEvent`에서 `PointerEvent | TouchEvent | FocusEvent`로 변경합니다. - `DialogRoot` 및 `DrawerRoot` 두 번째 인자 `details`의 `reason`으로 `cascadeDismiss`를 추가합니다. 두 개 이상의 오버레이 컴포넌트를 표시한 상황에서 하위 컴포넌트가 dismiss되는 경우 상위 컴포넌트는 `cascadeDismiss`와 함께 `onOpenChange`가 호출됩니다. - `@seed-design/react` 패키지에서 `@radix-ui/react-dialog` 의존성을 제거합니다. - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. ### Patch Changes - Updated dependencies [69e3b97] - Updated dependencies [60d1a82] - Updated dependencies [ec33023] - @seed-design/react-dismissible-layer@1.0.0 - @seed-design/react-presence@1.0.0 - @seed-design/react-prevent-scroll@1.0.0 - @seed-design/react-primitive@2.0.0 ## 1.0.3 ### Patch Changes - 26b1de5: `AlertDialog`, `MenuSheet` 등 Dialog 기반 컴포넌트가 열릴 때 첫 항목 대신 컨테이너에 포커스가 가도록 수정합니다. - URL로 직접 진입해 열었을 때 첫 항목에 포커스 링이 노출되던 문제를 수정합니다. - 스크린리더가 다이얼로그의 제목과 설명을 먼저 읽도록 초기 포커스를 컨테이너로 옮깁니다. ## 1.0.2 ### Patch Changes - 576c2e6: `AlertDialogRoot`, `MenuSheetRoot` 및 `BottomSheetRoot`의 `onOpenChange` 두 번째 인자로 `details`를 제공합니다. `details.reason`과 `details.event`를 사용할 수 있습니다. `DialogAction`을 `DialogPrimitive.CloseButton`으로 교체합니다. `AlertDialogAction` `onClick` 핸들러에서 `event.preventDefault()`를 호출하여 닫기 동작을 방지할 수 있습니다. [(예제)](https://seed-design.io/react/components/alert-dialog#prevent-close) - Updated dependencies [576c2e6] - @seed-design/react-use-controllable-state@1.0.0 ## 1.0.1 ### Patch Changes - 69ccc6e: Overlay 컴포넌트에 skipAnimation 옵션을 추가합니다 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.5 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.4 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.3 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.2 ### Patch Changes - 09fecb9: 누락된 seed-design/react-primitive 의존성 추가 및 불필요한 의존성 제거 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/dom-utils@0.0.1-rc.0 --- ## @seed-design/react-dismissible-layer ## 1.0.0 ### Major Changes - 69e3b97: Menu 컴포넌트를 추가합니다. Drawer를 연 뒤 Drawer 뒤 요소에 포커스가 남아 있는 문제를 수정합니다. Drawer가 열리는 경우 `Drawer.Content`에 자동으로 포커스가 이동합니다. - `Drawer.RootProps`의 `autoFocus` 기본값을 `false`에서 `true`로 변경합니다. - 스크린 리더가 `modal=true` (기본값)인 Dialog 및 Drawer 뒤 요소를 읽을 수 있는 문제를 수정합니다. Dialog(AlertDialog, MenuSheet)와 Drawer(BottomSheet)를 `@radix-ui/react-dismissable-layer`에서 자체 `useDismissibleLayer` 훅 기반으로 리팩터링하고 불필요하게 외부로 노출되던 내부 옵션들을 제거합니다. - `DialogRoot` 및 `DrawerRoot`의 `onOpenChange` 두 번째 인자 `details.reason`이 `interactOutside`인 경우 `details.event`의 타입을 `PointerEvent | FocusEvent`에서 `PointerEvent | TouchEvent | FocusEvent`로 변경합니다. - `DialogRoot` 및 `DrawerRoot` 두 번째 인자 `details`의 `reason`으로 `cascadeDismiss`를 추가합니다. 두 개 이상의 오버레이 컴포넌트를 표시한 상황에서 하위 컴포넌트가 dismiss되는 경우 상위 컴포넌트는 `cascadeDismiss`와 함께 `onOpenChange`가 호출됩니다. - `@seed-design/react` 패키지에서 `@radix-ui/react-dialog` 의존성을 제거합니다. - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. --- ## @seed-design/react-drawer ## 2.0.0 ### Major Changes - 69e3b97: Menu 컴포넌트를 추가합니다. Drawer를 연 뒤 Drawer 뒤 요소에 포커스가 남아 있는 문제를 수정합니다. Drawer가 열리는 경우 `Drawer.Content`에 자동으로 포커스가 이동합니다. - `Drawer.RootProps`의 `autoFocus` 기본값을 `false`에서 `true`로 변경합니다. - 스크린 리더가 `modal=true` (기본값)인 Dialog 및 Drawer 뒤 요소를 읽을 수 있는 문제를 수정합니다. Dialog(AlertDialog, MenuSheet)와 Drawer(BottomSheet)를 `@radix-ui/react-dismissable-layer`에서 자체 `useDismissibleLayer` 훅 기반으로 리팩터링하고 불필요하게 외부로 노출되던 내부 옵션들을 제거합니다. - `DialogRoot` 및 `DrawerRoot`의 `onOpenChange` 두 번째 인자 `details.reason`이 `interactOutside`인 경우 `details.event`의 타입을 `PointerEvent | FocusEvent`에서 `PointerEvent | TouchEvent | FocusEvent`로 변경합니다. - `DialogRoot` 및 `DrawerRoot` 두 번째 인자 `details`의 `reason`으로 `cascadeDismiss`를 추가합니다. 두 개 이상의 오버레이 컴포넌트를 표시한 상황에서 하위 컴포넌트가 dismiss되는 경우 상위 컴포넌트는 `cascadeDismiss`와 함께 `onOpenChange`가 호출됩니다. - `@seed-design/react` 패키지에서 `@radix-ui/react-dialog` 의존성을 제거합니다. - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. ### Patch Changes - Updated dependencies [69e3b97] - Updated dependencies [60d1a82] - Updated dependencies [ec33023] - @seed-design/react-dismissible-layer@1.0.0 - @seed-design/react-presence@1.0.0 - @seed-design/react-prevent-scroll@1.0.0 - @seed-design/react-primitive@2.0.0 ## 1.0.11 ### Patch Changes - 306673a: iOS 환경에서 Bottom Sheet(Drawer) 내부 버튼에서 시트 스와이프 동작을 수행하는 경우 의도하지 않은 버튼 클릭을 발생시키던 문제를 수정합니다. ## 1.0.10 ### Patch Changes - 59887b7: SEED React 2.0.0에서 제거될 예정인 Drawer/BottomSheet의 `noBodyStyles`, `preventScrollRestoration` 옵션을 deprecated 처리합니다. 2.0.0부터는 두 옵션 모두 기본값(`noBodyStyles=true`, `preventScrollRestoration=false`)처럼 동작합니다. ## 1.0.9 ### Patch Changes - 0420c89: Android 환경에서 온스크린 키보드가 닫힐 때 Drawer(Bottom Sheet) 높이가 정상적으로 복원되지 않는 문제를 수정합니다. ## 1.0.8 ### Patch Changes - 576c2e6: `AlertDialogRoot`, `MenuSheetRoot` 및 `BottomSheetRoot`의 `onOpenChange` 두 번째 인자로 `details`를 제공합니다. `details.reason`과 `details.event`를 사용할 수 있습니다. `DialogAction`을 `DialogPrimitive.CloseButton`으로 교체합니다. `AlertDialogAction` `onClick` 핸들러에서 `event.preventDefault()`를 호출하여 닫기 동작을 방지할 수 있습니다. [(예제)](https://seed-design.io/react/components/alert-dialog#prevent-close) - Updated dependencies [576c2e6] - @seed-design/react-use-controllable-state@1.0.0 ## 1.0.6 ### Patch Changes - 8188130: AlertDialog 닫힐 때 focus 복원으로 인해 BottomSheet가 즉시 닫히는 문제 수정 - onFocusOutside에서 항상 preventDefault 호출 - onInteractOutside에서 defaultPrevented 체크 추가 - 9cbeba0: BottomSheet `showCloseButton` variant 변경 및 animation 버그 수정 - `showCloseButton` variant 변경: CloseButton 미사용 시 header padding 조정 가능 - `hasEntered` 상태 추가: modal prop 변경 시 enter animation 재실행 방지 ## 1.0.5 ### Patch Changes - 69ccc6e: Overlay 컴포넌트에 skipAnimation 옵션을 추가합니다 ## 1.0.4 ### Patch Changes - e3806c1: BottomSheet에 handleOnly 옵션이 정상적으로 동작하지 않는 이슈를 수정합니다 ## 1.0.3 ### Patch Changes - cc8864d: use-prevent-scroll 로직을 삭제합니다 ## 1.0.2 ### Patch Changes - 4102a4b: BottomSheetContent에 style 객체가 전달되지 않는 버그를 수정합니다. - e272ef8: Export 수정: `DialogProps` → `UseDrawerProps` - fbc9cb0: uncontrolled 상태에서 onOpenChange가 두번 호출되는 버그를 수정합니다 - 4971dcc: body에 강제로 스타일을 주입하는 noBodyStyles 옵션의 기본값을 true로 변경합니다 ## 1.0.1 ### Patch Changes - 68b5eab: @seed-design/react-drawer 패키지 배포 ## 1.0.0 ### Major Changes - 33def2d: `@seed-design/react-drawer` 1.0.0 --- ## @seed-design/react-field ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.1 ### Patch Changes - ae1b768: :focus-visible selector를 사용하기 전 브라우저에서 selector를 지원하는지 확인합니다. - Updated dependencies [ae1b768] - @seed-design/react-supports@0.0.1 ## 1.0.0 ### Major Changes - a55f584: Slider 컴포넌트를 추가합니다. --- ## @seed-design/react-field-button ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.2 ### Patch Changes - 751e952: FieldButton 내부 clear button이 FieldButton 상호작용에 따른 data prop을 받지 않도록 수정합니다. ## 1.0.1 ### Patch Changes - 53290ab: FieldButton에 Read Only 상태를 추가합니다. Disabled 상태인 FieldButton은 내부 ``도 `disabled` 속성을 갖도록 수정합니다. - ae1b768: :focus-visible selector를 사용하기 전 브라우저에서 selector를 지원하는지 확인합니다. - Updated dependencies [ae1b768] - @seed-design/react-supports@0.0.1 ## 1.0.0 ### Major Changes - d6bb84d: (BREAKING CHANGE: TextField snippet을 다시 설치해야 합니다.) Text Field 관련 컴포넌트를 업데이트합니다. - 스타일 업데이트 - size 통일 및 variant (underline) 추가 - 내부적으로 Field 컴포넌트를 사용하도록 변경하여 스타일 일관성 향상 Field Button 컴포넌트를 추가합니다. --- ## @seed-design/react-fieldset ## 1.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 0.1.0 ### Minor Changes - cfd2df4: fieldset, collapsible headless 릴리즈 --- ## @seed-design/react-file-upload ## 1.0.0 ### Major Changes - 555525a: Attachment Field 관련 컴포넌트를 추가합니다. - Attachment Field: 파일을 선택하거나 드래그 앤 드롭으로 직접 업로드할 때 사용합니다. - Attachment Display Field: 외부 소스에서 URL로 제공된 미디어를 표시·관리할 때 사용합니다. ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 --- ## @seed-design/react-floating ## 1.0.0 ### Major Changes - 029d052: Help Bubble Tooltip 컴포넌트를 추가합니다. --- ## @seed-design/react-image ## 1.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 0.1.1 ### Patch Changes - c46d593: `Image.Content` (`ImageFrame` 및 `AvatarImage`)에 `loading="lazy"` 사용 시 이미지 로드가 시작되지 않는 문제를 수정합니다. ## 0.1.0 ### Minor Changes - cfd2df4: react-image headless 릴리즈 --- ## @seed-design/react-menu ## 1.0.0 ### Major Changes - 69e3b97: Menu 컴포넌트를 추가합니다. Drawer를 연 뒤 Drawer 뒤 요소에 포커스가 남아 있는 문제를 수정합니다. Drawer가 열리는 경우 `Drawer.Content`에 자동으로 포커스가 이동합니다. - `Drawer.RootProps`의 `autoFocus` 기본값을 `false`에서 `true`로 변경합니다. - 스크린 리더가 `modal=true` (기본값)인 Dialog 및 Drawer 뒤 요소를 읽을 수 있는 문제를 수정합니다. Dialog(AlertDialog, MenuSheet)와 Drawer(BottomSheet)를 `@radix-ui/react-dismissable-layer`에서 자체 `useDismissibleLayer` 훅 기반으로 리팩터링하고 불필요하게 외부로 노출되던 내부 옵션들을 제거합니다. - `DialogRoot` 및 `DrawerRoot`의 `onOpenChange` 두 번째 인자 `details.reason`이 `interactOutside`인 경우 `details.event`의 타입을 `PointerEvent | FocusEvent`에서 `PointerEvent | TouchEvent | FocusEvent`로 변경합니다. - `DialogRoot` 및 `DrawerRoot` 두 번째 인자 `details`의 `reason`으로 `cascadeDismiss`를 추가합니다. 두 개 이상의 오버레이 컴포넌트를 표시한 상황에서 하위 컴포넌트가 dismiss되는 경우 상위 컴포넌트는 `cascadeDismiss`와 함께 `onOpenChange`가 호출됩니다. - `@seed-design/react` 패키지에서 `@radix-ui/react-dialog` 의존성을 제거합니다. ### Patch Changes - Updated dependencies [69e3b97] - Updated dependencies [60d1a82] - Updated dependencies [ec33023] - @seed-design/react-dismissible-layer@1.0.0 - @seed-design/react-primitive@2.0.0 --- ## @seed-design/react-middle-truncate ## 1.0.0 ### Major Changes - 555525a: Attachment Field 관련 컴포넌트를 추가합니다. - Attachment Field: 파일을 선택하거나 드래그 앤 드롭으로 직접 업로드할 때 사용합니다. - Attachment Display Field: 외부 소스에서 URL로 제공된 미디어를 표시·관리할 때 사용합니다. ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 --- ## @seed-design/react-navigation-menu ## 1.0.0 ### Major Changes - 2abd3ed: Side Navigation에서 내부적으로 사용되는 `NavigationMenu` 컴포넌트를 추가합니다. ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 --- ## @seed-design/react-popover ## 2.0.0 ### Major Changes - 029d052: Help Bubble Tooltip 컴포넌트를 추가합니다. ### Patch Changes - Updated dependencies [029d052] - Updated dependencies [ec33023] - @seed-design/react-floating@1.0.0 - @seed-design/react-primitive@2.0.0 ## 1.0.4 ### Patch Changes - e48f021: HelpBubble이 좁은 화면에서 화면 밖으로 잘리던 문제를 수정합니다. (Popover의 floating 요소 너비를 viewport에 맞게 동적으로 제한하고, 긴 텍스트가 Help Bubble 밖으로 넘치지 않도록 긴 단어 중간 줄바꿈을 허용합니다.) ## 1.0.3 ### Patch Changes - 2c302a5: PopoverPositionerPortal과 HelpBubblePositionerPortal을 추가합니다. ## 1.0.2 ### Patch Changes - 0c1ab6a: 닫힌 HelpBubbleAnchor/HelpBubbleTrigger가 불필요하게 리렌더링되지 않도록 수정합니다. ## 1.0.1 ### Patch Changes - b10ff0b: closeOnInteractOutside를 false로 설정하여 Help Bubble 외부와 상호작용 시에도 닫히지 않도록 설정할 수 있습니다. (기본값: true) ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.8 ### Patch Changes - 62094b6: Help Bubble의 스타일 문제를 수정합니다. - `placement=left-*` / `placement=right-*`에서 arrow가 content와 떨어져 표시되는 문제를 수정합니다. ## 0.0.7 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.6 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.5 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.4 ### Patch Changes - f4b0723: HelpBubble의 enter, exit 모션을 추가합니다. ## 0.0.3 ### Patch Changes - c1d94d0: HelpBubble의 enter, exit 모션을 추가합니다. ## 0.0.2 ### Patch Changes - 09fecb9: 누락된 seed-design/react-primitive 의존성 추가 및 불필요한 의존성 제거 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/dom-utils@0.0.1-rc.0 ## 0.0.0-alpha-20241030023710 ### Patch Changes - alpha - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241030023710 ## 0.0.0-alpha-20241004093556 ### Patch Changes - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241004093556 --- ## @seed-design/react-portal ## 2.0.0 이 패키지의 코드 변경은 없으며, SEED React 2 릴리즈에 맞춰 앞으로의 semver 정합성을 위해 버전을 올립니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ## 0.0.2 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.1 ### Patch Changes - 3efe201: `` 컴포넌트를 제공합니다. --- ## @seed-design/react-presence ## 1.0.0 ### Major Changes - 69e3b97: Menu 컴포넌트를 추가합니다. Drawer를 연 뒤 Drawer 뒤 요소에 포커스가 남아 있는 문제를 수정합니다. Drawer가 열리는 경우 `Drawer.Content`에 자동으로 포커스가 이동합니다. - `Drawer.RootProps`의 `autoFocus` 기본값을 `false`에서 `true`로 변경합니다. - 스크린 리더가 `modal=true` (기본값)인 Dialog 및 Drawer 뒤 요소를 읽을 수 있는 문제를 수정합니다. Dialog(AlertDialog, MenuSheet)와 Drawer(BottomSheet)를 `@radix-ui/react-dismissable-layer`에서 자체 `useDismissibleLayer` 훅 기반으로 리팩터링하고 불필요하게 외부로 노출되던 내부 옵션들을 제거합니다. - `DialogRoot` 및 `DrawerRoot`의 `onOpenChange` 두 번째 인자 `details.reason`이 `interactOutside`인 경우 `details.event`의 타입을 `PointerEvent | FocusEvent`에서 `PointerEvent | TouchEvent | FocusEvent`로 변경합니다. - `DialogRoot` 및 `DrawerRoot` 두 번째 인자 `details`의 `reason`으로 `cascadeDismiss`를 추가합니다. 두 개 이상의 오버레이 컴포넌트를 표시한 상황에서 하위 컴포넌트가 dismiss되는 경우 상위 컴포넌트는 `cascadeDismiss`와 함께 `onOpenChange`가 호출됩니다. - `@seed-design/react` 패키지에서 `@radix-ui/react-dialog` 의존성을 제거합니다. ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 --- ## @seed-design/react-prevent-scroll ## 1.0.0 ### Major Changes - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. --- ## @seed-design/react-primitive ## 2.0.0 ### Major Changes - ec33023: Side Navigation 컴포넌트를 추가합니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ## 0.0.3 ### Patch Changes - 29ec9f0: `reactSlot.createSlot is not a function` 오류가 발생하지 않도록, radix-ui/react-slot 버전을 1.2.3으로 수정합니다. ## 0.0.2 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate --- ## @seed-design/react-progress ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.4 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.3 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.2 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/react-primitive@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 --- ## @seed-design/react-pull-to-refresh ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.1 ### Patch Changes - 687b261: `PullToRefresh.preventPull`을 활용하여 `PullToRefreshContent` 내부에서 당겨서 새로고침(PTR) 동작을 비활성화할 수 있습니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.6 ### Patch Changes - 4610b5b: PullToRefresh에 disabled prop을 추가합니다. ## 0.0.5 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.4 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.3 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.2 ### Patch Changes - c0c0b7e: 여러 PTR 인스턴스가 동일한 상태를 공유하는 문제를 수정합니다. ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/react-primitive@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 --- ## @seed-design/react-radio-group ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 - @seed-design/react-fieldset@0.1.1 ## 1.1.0 ### Minor Changes - 98dbac4: [Checkbox](/react/components/checkbox) 관련 컴포넌트를 추가합니다. - `CheckboxGroup` snippet 컴포넌트가 추가되었습니다. 사용하려면 snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:checkbox` - `CheckboxGroup`은 자체적으로 gap과 100% width를 갖습니다. `VStack`을 사용하여 `Checkbox`를 묶지 않아도 됩니다. - 기존 `Checkbox`를 `CheckboxGroup`으로 감쌀 필요는 없습니다. `CheckboxGroup`은 선택적으로 사용할 수 있습니다. - `label`, `description`, `errorMessage`, `indicator`, `showRequiredIndicator`, `labelWeight` prop을 사용할 수 있습니다. [Radio Group](/react/components/radio-group) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet 업데이트 필요**: `RadioGroup` snippet의 내부 구조가 변경되었습니다. snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:radio-group` - `RadioGroup`이 자체적으로 gap과 100% width를 갖습니다. `VStack`을 사용하여 `RadioGroupItem`을 묶는 코드를 제거합니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `RadioGroupItem`을 묶어서 사용하던 `VStack`을 제거하여 `RadioGroupItem`이 `RadioGroup`의 direct child가 되도록 변경하세요. - `label`, `description`, `errorMessage`, `indicator`, `showRequiredIndicator`, `labelWeight` prop을 사용할 수 있습니다. - `@seed-design/react`의 `RadioGroup.Root`를 레이아웃 컴포넌트로 변경합니다. - `@seed-design/react`에서 직접 import해서 사용하는 코드가 있다면 `RadioGroup.Root`를 `@seed-design/react/primitive`의 `RadioGroup.Root`로 변경해주세요. ```tsx // 전 import { VStack } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; ; ``` ```tsx // 후 import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; { /* aria-label 대신 label을 사용하여 시각적으로 레이블을 표시할 수도 있습니다. */ } ; ``` ```tsx // 전 import { RadioGroup } from "@seed-design/react"; import { ListRadioItem } from "seed-design/ui/list"; } /> ; ``` ```tsx // 후 import { RadioGroup } from "@seed-design/react/primitive"; import { ListRadioItem } from "seed-design/ui/list"; } /> ; ``` RadioGroupItem, RadioChipItem, RadioSelectBoxItem, ListRadioItem에서 `invalid` prop이 제거되었습니다. - **1.1 → 1.2 업그레이드 시 확인 필요**: `invalid` 상태는 group/field 레벨에서 설정해주세요. 각 항목을 `data-invalid` 속성으로 스타일링하는 경우 확인이 필요합니다. - 2643d17: [Select Box](/react/components/select-box) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet을 다시 내려받아 주세요.** - `npx @seed-design/cli@latest add ui:select-box` - `CheckSelectBoxGroup`, `RadioSelectBoxRoot`의 children이 기본적으로 gap이 포함된 그리드 레이아웃으로 정렬됩니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `CheckSelectBox`, `RadioSelectBoxItem`을 묶어서 사용하던 `VStack`을 제거하여 `CheckSelectBox`와 `RadioSelectBoxItem`이 `CheckSelectBoxGroup` 또는 `RadioSelectBoxRoot`의 direct child가 되도록 변경하세요. `VStack`에 `gap` 이외의 스타일이 적용된 경우 `...`와 같이 `VStack`을 외부에 남겨두세요. - **기능 추가**: `CheckSelectBoxGroup`와 `RadioSelectBoxRoot`에 `columns`를 지정할 수 있습니다. `columns`가 `2` 이상인 경우 하위 항목에 기본적으로 `layout="vertical"`이 적용됩니다. 기본 `layout`은 하위 항목에서 오버라이드할 수 있습니다. - **기능 추가**: `CheckSelectBoxGroup`과 `RadioSelectBoxRoot`에 `label`, `description`, `errorMessage`, `indicator` 등 Fieldset 관련 prop을 사용할 수 있습니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `CheckSelectBox`, `RadioSelectBoxItem`에 기본적으로 표시되던 `Checkmark`와 `RadioMark`가 이제 표시되지 않습니다. `suffix` prop을 통해 선택적으로 추가할 수 있습니다. - 단순 마이그레이션 시 `suffix={}`와 `suffix={}`를 추가하세요. - **기능 추가**: `prefixIcon`, `footer`, `footerVisibility` prop 추가 - `footer`에 넣는 요소는 기본적으로 해당 `CheckSelectBox` 또는 `RadioSelectBoxItem`가 선택된 상태일 때 표시됩니다. `footerVisibility="always"`를 설정하여 footer 요소를 항상 표시할 수 있습니다. - `label`이 기본적으로 가로 나열되며 `$dimension.x2` gap을 갖는 flex container로 변경되었습니다. - **1.1 → 1.2 업그레이드 시 확인 권장**: `label={{/* ... */}}`와 같은 코드는 `HStack`을 `Fragment` 등으로 대체할 수 있습니다. - **문제 수정**: `CheckSelectBox`와 `RadioSelectBoxItem`에서 사용되지 않는 `children`을 타입 정의에서 제거합니다. - `CheckSelectBoxGroup`에 `label`, `aria-label`, `aria-labelledby` 중 아무것도 설정하지 않은 경우 경고를 표시합니다. (`RadioSelectBoxRoot`는 기존에도 표시) ### Patch Changes - Updated dependencies [cfd2df4] - @seed-design/react-fieldset@0.1.0 ## 1.0.1 ### Patch Changes - ae1b768: :focus-visible selector를 사용하기 전 브라우저에서 selector를 지원하는지 확인합니다. - Updated dependencies [ae1b768] - @seed-design/react-supports@0.0.1 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.4 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.3 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.2 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/react-primitive@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 ## 0.0.0-alpha-20241030023710 ### Patch Changes - alpha - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241030023710 ## 0.0.0-alpha-20241004093556 ### Patch Changes - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241004093556 --- ## @seed-design/react-scrollable ## 1.0.1 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.0 ### Major Changes - bc3cd6f: Release `@seed-design/react-scrollable` --- ## @seed-design/react-segmented-control ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.1 ### Patch Changes - ae1b768: :focus-visible selector를 사용하기 전 브라우저에서 selector를 지원하는지 확인합니다. - Updated dependencies [ae1b768] - @seed-design/react-supports@0.0.1 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.5 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.4 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.3 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.2 ### Patch Changes - 09fecb9: 누락된 seed-design/react-primitive 의존성 추가 및 불필요한 의존성 제거 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/dom-utils@0.0.1-rc.0 --- ## @seed-design/react-side-navigation ## 1.0.0 ### Major Changes - ec33023: Side Navigation 컴포넌트를 추가합니다. ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 - @seed-design/react-collapsible@0.1.1 --- ## @seed-design/react-slider ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.2 ### Patch Changes - fe1cdb3: 마우스/키보드 사용 환경에서 Slider의 사용성을 개선합니다. - Slider `valueIndicatorTrigger`에 `"auto"` 옵션을 추가하여 마우스를 사용하는 환경에서 thumb에 hover할 때, 터치 환경에서 thumb을 누를 때 value indicator가 표시되도록 하고 `valueIndicatorTrigger` 기본값을 `"active"`에서 `"auto"`로 변경합니다. - Slider thumb에 focus할 때 value indicator가 표시되도록 수정합니다. ## 1.0.1 ### Patch Changes - 1340675: Slider Value Indicator가 표시되는 조건을 설정하는 `valueIndicatorTrigger` prop을 추가합니다. ("active"|"hover", 기본값: "active") - 1340675: Slider Value Indicator가 Track 양 끝에 있을 때 Track 바깥 영역을 차지하지 않도록 수정합니다. ## 1.0.0 ### Major Changes - a55f584: Slider 컴포넌트를 추가합니다. --- ## @seed-design/react-snackbar ## 2.0.0 ### Major Changes - afb77c5: Snackbar 내부 액션 버튼 클릭 시 `onAction` 핸들러 호출 이후 스낵바가 닫히는 동작을 기본값으로 제공합니다. - snippet에 존재하던 deprecate된 `shouldCloseOnAction` 옵션(default: `true`)을 제거합니다. - 해당 동작을 React 컴포넌트로 이전하여 항상 `true`처럼 동작하도록 변경합니다. ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.2 ### Patch Changes - 840f5d5: Snackbar가 표시된 상태에서 새 Snackbar를 create할 때의 동작을 정의하는 `strategy` 옵션을 추가합니다. 기본값은 `immediate`로, 새 Snackbar가 기존 Snackbar를 즉시 교체합니다. - 기존 스낵바에 할당된 시간이 모두 지난 뒤 새 Snackbar가 표시되는 이전 버전의 기존 동작을 선호하는 경우 `SnackbarProvider` 또는 `useSnackbarAdapter` 옵션으로 `queued`를 사용할 수 있습니다. - `immediate` 옵션을 모방하기 위해 `dismiss()` 후 `setTimeout(() => create(...), 0)`하던 workaround와 함께 사용해도 정상 동작하지만, 동작이 동일하므로 workaround는 제거하는 것을 권장합니다. - 934a0ba: Snackbar의 `timeout` 기본값을 5초에서 4초로 변경합니다. `timeout`을 명시적으로 지정한 경우에는 기존 동작이 유지됩니다. ## 1.0.1 ### Patch Changes - acae645: Snackbar 타이머가 멈추는 기준을 `focus`에서 `focus-visible`로 수정하여 `pauseOnInteraction={true}`인 경우 Snackbar가 닫히지 않는 문제를 수정합니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.7 ### Patch Changes - 11f5e76: Snackbar 헤드리스에서 닫기 버튼에 하드코딩된 `aria-label` 속성을 제거합니다. ## 0.0.6 ### Patch Changes - 8299ba9: Snackbar 컴포넌트를 업데이트합니다. - root 영역에 maxWidth 스펙을 추가합니다. - `pauseOnInteraction`의 기본값을 `false`에서 `true`로 변경합니다. ## 0.0.5 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.4 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.3 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.2 ### Patch Changes - 09fecb9: 누락된 seed-design/react-primitive 의존성 추가 및 불필요한 의존성 제거 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/react-primitive@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 --- ## @seed-design/react-supports ## 1.0.0 이 패키지의 코드 변경은 없으며, SEED React 2 릴리즈에 맞춰 앞으로의 semver 정합성을 위해 버전을 올립니다. ## 0.0.1 ### Patch Changes - ae1b768: :focus-visible selector를 사용하기 전 브라우저에서 selector를 지원하는지 확인합니다. --- ## @seed-design/react-switch ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.1 ### Patch Changes - ae1b768: :focus-visible selector를 사용하기 전 브라우저에서 selector를 지원하는지 확인합니다. - Updated dependencies [ae1b768] - @seed-design/react-supports@0.0.1 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.4 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.3 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.2 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/react-primitive@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 ## 0.0.0-alpha-20241030023710 ### Patch Changes - alpha - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241030023710 ## 0.0.0-alpha-20241004093556 ### Patch Changes - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241004093556 --- ## @seed-design/react-tabs ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.5 ### Patch Changes - 81ec9f5: lazyMount를 사용하는 Tabs · ChipTabs에서 멀리 떨어진 탭을 선택하면 다른 탭이 열리던 문제를 해결합니다. lazyMount 및 TabsCarousel 사용 시 TabsContent 자체를 항상 렌더하고, TabsContent의 children만 lazy mount하도록 변경합니다. - 136a150: iOS 환경에서 ChipTabs + ScrollFog 사용 시 가로 스크롤이 동작하지 않는 문제를 해결합니다. ## 1.0.4 ### Patch Changes - db6ea04: `TabsCarousel`의 `autoHeight`와 `lazyMount`를 함께 사용할 때 lazy content 마운트 후 높이가 갱신되지 않는 문제를 수정합니다. - Embla v8 AutoHeight 플러그인이 slide 내부 content의 높이 변화를 감지하지 못하는 알려진 이슈에 대해 `watchResize` workaround를 적용합니다. ## 1.0.3 ### Patch Changes - 2552b1d: Tabs trigger에 포커스 후 키보드 조작으로 탭 변경 시 탭은 변경되지만 포커스가 이동하지 않는 문제를 수정합니다. - a465d74: Tabs trigger에 포커스된 상태에서 키보드 조작 시 스크롤 등 브라우저 기본 동작이 발생하지 않도록 수정합니다. ## 1.0.2 ### Patch Changes - 938bf0b: TabsCarousel에 onSwipeStart, onSwipeEnd 이벤트 콜백을 추가합니다. ## 1.0.1 ### Patch Changes - ae1b768: :focus-visible selector를 사용하기 전 브라우저에서 selector를 지원하는지 확인합니다. - Updated dependencies [ae1b768] - @seed-design/react-supports@0.0.1 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.9 ### Patch Changes - ce047f5: Tabs.Carousel의 reInit 이벤트 시 탭 인덱스가 변경되지 않는 문제를 수정합니다. ## 0.0.8 ### Patch Changes - fd7c569: - Tabs.Carousel을 사용하는 경우 Hydration 이후 스크롤 애니매이션이 발생하는 문제를 수정합니다. - Tabs.Carousel의 드래그 제스처를 방지하는 영역을 선언할 수 있는 `Tabs.carouselPreventDrag` api를 추가합니다. - layout=hug일 때 Indicator에서 발생하는 Layout Shift를 수정합니다. - lazyMount 옵션이 의도와 다르게 모든 탭이 한꺼번에 마운트되는 문제를 수정합니다. ## 0.0.7 ### Patch Changes - 739b6bf: Tabs.Indicator의 width가 첫 렌더링 시 0으로 설정되는 문제를 수정합니다. Tabs의 불필요한 리렌더링을 줄입니다. ## 0.0.6 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.5 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.4 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.3 ### Patch Changes - fee050d: overflow된 Tab 선택 시 스크롤 동작을 추가합니다. ## 0.0.2 ### Patch Changes - 09fecb9: 누락된 seed-design/react-primitive 의존성 추가 및 불필요한 의존성 제거 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/dom-utils@0.0.1-rc.0 ## 0.0.0-alpha-20241209060641 ### Patch Changes - tabs 스와이프 관련 이벤트 추가 ## 0.0.0-alpha-20241204061301 ### Patch Changes - useSwipeable dragProps ## 0.0.0-alpha-20241203133940 ### Patch Changes - tabs 자잘한 스타일 수정 및 fixTriggerList prop 추가 ## 0.0.0-alpha-20241202094027 ### Patch Changes - Tabs에 content-list에 스타일을 변경해요 ## 0.0.0-alpha-20241202031729 ### Patch Changes - add --seed-design-tabs-current-tab-enabled-index css variable in root ## 0.0.0-alpha-20241202030714 ### Patch Changes - SSR 문제를 해결해요 ## 0.0.0-alpha-20241030023710 ### Patch Changes - alpha - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241030023710 ## 0.0.0-alpha-20241014145845 ### Patch Changes - sync style chipTabs, Tabs to figma ## 0.0.0-alpha-20241004093556 ### Patch Changes - prerelease - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241004093556 --- ## @seed-design/react-text-field ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.1.1 ### Patch Changes - ae1b768: :focus-visible selector를 사용하기 전 브라우저에서 selector를 지원하는지 확인합니다. - Updated dependencies [ae1b768] - @seed-design/react-supports@0.0.1 ## 1.1.0 ### Minor Changes - d6bb84d: (BREAKING CHANGE: TextField snippet을 다시 설치해야 합니다.) Text Field 관련 컴포넌트를 업데이트합니다. - 스타일 업데이트 - size 통일 및 variant (underline) 추가 - 내부적으로 Field 컴포넌트를 사용하도록 변경하여 스타일 일관성 향상 Field Button 컴포넌트를 추가합니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.4 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.3 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.2 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/react-primitive@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 ## 0.0.0-alpha-20241030023710 ### Patch Changes - alpha - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241030023710 ## 0.0.0-alpha-20241004093556 ### Patch Changes - Updated dependencies - @seed-design/dom-utils@0.0.0-alpha-20241004093556 --- ## @seed-design/react-theming ## 2.1.5 ### Patch Changes - 93cfc30: fix: inconsistent platform data attribute name - `seedScaleLetterSpacing` -> `seedPlatform` ## 2.1.4 ### Patch Changes - ee35c53: change `@seed-design/design-token` version ## 2.1.3 ### Patch Changes - cd0f24e: add types field in exports map - Updated dependencies [cd0f24e] - @seed-design/design-token@1.0.2 ## 2.1.2 ### Patch Changes - fcdb8d5: fix dataset location body to documentElement ## 2.1.1 ### Patch Changes - 6d298fa: mode variant 처리 - 6d298fa: processing colorVariant localstorage and mode plugin option. ## 2.1.0 ### Minor Changes - 6453963: Change attribute location body -> html - 725fa03: Create ColorModeValues, DefaultColorModeValue variables ## 2.0.4 ### Patch Changes - 20c37c5: add prefers-color-scheme logic in noFlashScript ## 2.0.3 ### Patch Changes - 4fe0e8e: Modified so that the scale-color attribute is also changed according to the mode ## 2.0.2 ### Patch Changes - 8da317b: mode variant 처리 - 8da317b: processing colorVariant localstorage and mode plugin option. - 8da317b: change color variable name and some logics ## 2.0.1 ### Patch Changes - 51a44af: export d.ts ## 2.0.0 ### Major Changes - ae764ec: Initial implementation of @seed-design/react-theming --- ## @seed-design/react-toggle ## 2.0.0 ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.0.1 ### Patch Changes - 7e0728a: ToggleButton이 비활성화 상태에서 `aria-disabled="true"`를 갖도록 수정합니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [34f92f2] - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.0.4 ### Patch Changes - Updated dependencies [29ec9f0] - @seed-design/react-primitive@0.0.3 ## 0.0.3 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. ## 0.0.2 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [e368c69] - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/react-primitive@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 --- ## @seed-design/react-tooltip ## 1.0.0 ### Major Changes - 029d052: Help Bubble Tooltip 컴포넌트를 추가합니다. ### Patch Changes - Updated dependencies [029d052] - Updated dependencies [ec33023] - @seed-design/react-floating@1.0.0 - @seed-design/react-primitive@2.0.0 --- ## @seed-design/react-use-controllable-state ## 2.0.0 이 패키지의 코드 변경은 없으며, SEED React 2 릴리즈에 맞춰 앞으로의 semver 정합성을 위해 버전을 올립니다. ## 1.0.0 ### Major Changes - 576c2e6: `AlertDialogRoot`, `MenuSheetRoot` 및 `BottomSheetRoot`의 `onOpenChange` 두 번째 인자로 `details`를 제공합니다. `details.reason`과 `details.event`를 사용할 수 있습니다. `DialogAction`을 `DialogPrimitive.CloseButton`으로 교체합니다. `AlertDialogAction` `onClick` 핸들러에서 `event.preventDefault()`를 호출하여 닫기 동작을 방지할 수 있습니다. [(예제)](https://seed-design.io/react/components/alert-dialog#prevent-close) --- ## @seed-design/rootage-artifacts ## 2.1.1 ### Patch Changes - bfaa48a: Floating Action Button의 확장(extended) 형태 아이콘 크기를 `$dimension.x5`에서 `$dimension.x4`로 수정합니다. - e97a89c: `@seed-design/react`에서 `@seed-design/css/vars/component` 의존을 제거하여 ImageFrame `offsetX` `offsetY` 및 ScrollFog `size`의 기본값을 기존과 동일한 값으로 하드코딩합니다. - 7224b94: ImageFrameReactionButton root에 반투명 배경색과 그림자를 추가하여 밝은 이미지 위에서의 가시성을 개선합니다. - c4ce431: Side Panel Close Button 및 Side Panel Header의 스타일을 디자인 스펙에 맞게 수정하고, Side Panel body에 `box-sizing: border-box`를 적용하여, body에 지정한 `height`/`minHeight`/`maxHeight`가 padding을 포함해 계산되도록 수정합니다. ## 2.1.0 ### Minor Changes - f2b198d: `$color.bg.layer-fill` 토큰을 다시 추가합니다. `2.0.0`에서 제거했으나 마땅한 대체 토큰이 없어 deprecated 상태로 되살립니다. 값은 제거 이전과 동일합니다. 추후 동일한 값의 새 이름 토큰으로 대체된 뒤 `3.0.0`에서 제거될 예정입니다. `@seed-design/tailwind3-plugin`과 `@seed-design/tailwind4-theme`의 `@seed-design/css` peer dependency 버전 범위를 `^2.1.0`으로 올립니다. ## 2.0.0 ### Major Changes - 555525a: Attachment Field 관련 컴포넌트를 추가합니다. - Attachment Field: 파일을 선택하거나 드래그 앤 드롭으로 직접 업로드할 때 사용합니다. - Attachment Display Field: 외부 소스에서 URL로 제공된 미디어를 표시·관리할 때 사용합니다. - d1a8d7c: 신규 타이포그래피 토큰을 추가합니다. - **폰트 크기 토큰 (`$font-size`)** - `t11`, `t11-static` - `t12`, `t12-static` - `t13`, `t13-static` - `t14`, `t14-static` - **줄 간격 토큰 (`$line-height`)** - `t11`, `t11-static` - `t12`, `t12-static` - `t13`, `t13-static` - `t14`, `t14-static` - **텍스트 스타일 (`textStyle`)** - `t8Regular`, `t8Medium`, `t8StaticRegular`, `t8StaticMedium` - `t9Regular`, `t9Medium`, `t9StaticRegular`, `t9StaticMedium` - `t10Regular`, `t10Medium`, `t10StaticRegular`, `t10StaticMedium` - `t11Regular`, `t11Medium`, `t11Bold`, `t11StaticRegular`, `t11StaticMedium`, `t11StaticBold` - `t12Regular`, `t12Medium`, `t12Bold`, `t12StaticRegular`, `t12StaticMedium`, `t12StaticBold` - `t13Regular`, `t13Medium`, `t13Bold`, `t13StaticRegular`, `t13StaticMedium`, `t13StaticBold` - `t14Regular`, `t14Medium`, `t14Bold`, `t14StaticRegular`, `t14StaticMedium`, `t14StaticBold` - 69e3b97: Menu 컴포넌트를 추가합니다. Drawer를 연 뒤 Drawer 뒤 요소에 포커스가 남아 있는 문제를 수정합니다. Drawer가 열리는 경우 `Drawer.Content`에 자동으로 포커스가 이동합니다. - `Drawer.RootProps`의 `autoFocus` 기본값을 `false`에서 `true`로 변경합니다. - 스크린 리더가 `modal=true` (기본값)인 Dialog 및 Drawer 뒤 요소를 읽을 수 있는 문제를 수정합니다. Dialog(AlertDialog, MenuSheet)와 Drawer(BottomSheet)를 `@radix-ui/react-dismissable-layer`에서 자체 `useDismissibleLayer` 훅 기반으로 리팩터링하고 불필요하게 외부로 노출되던 내부 옵션들을 제거합니다. - `DialogRoot` 및 `DrawerRoot`의 `onOpenChange` 두 번째 인자 `details.reason`이 `interactOutside`인 경우 `details.event`의 타입을 `PointerEvent | FocusEvent`에서 `PointerEvent | TouchEvent | FocusEvent`로 변경합니다. - `DialogRoot` 및 `DrawerRoot` 두 번째 인자 `details`의 `reason`으로 `cascadeDismiss`를 추가합니다. 두 개 이상의 오버레이 컴포넌트를 표시한 상황에서 하위 컴포넌트가 dismiss되는 경우 상위 컴포넌트는 `cascadeDismiss`와 함께 `onOpenChange`가 호출됩니다. - `@seed-design/react` 패키지에서 `@radix-ui/react-dialog` 의존성을 제거합니다. - 57e4f1a: Chip Tabs Trigger가 disabled 상태일 때 opacity를 사용하여 스타일을 조절하도록 수정합니다. - 270b277: 컴포넌트 및 파운데이션 전반에 JSDoc 주석을 강화합니다. - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. - 98b52a7: Side Panel 컴포넌트를 추가합니다. - 화면 좌/우 가장자리에서 슬라이드해 보조 콘텐츠, 상세 정보, 설정 흐름을 표시할 수 있습니다. - c0fd999: Text Input(Text Field)과 Input Button(Field Button)에 `size="medium"` variant를 추가합니다. - 31bf164: Accordion 컴포넌트를 추가합니다. - 여러 개의 관련된 콘텐츠 섹션을 수직으로 나열하고, 각 섹션을 펼치거나 접어 정보를 탐색할 수 있는 컴포넌트입니다. ## 1.2.12 ### Patch Changes - 8dc8f4f: Menu Sheet의 디자인 스펙에서 핸들 여백 확보를 위해 `content` 상단 패딩을 `x4` → `x6`으로 늘리고 `header.paddingTop`을 제거합니다 (핸들 여백 확보). React/CSS `MenuSheet`의 경우 핸들이 존재하지 않으므로 변경사항이 없습니다. ## 1.2.11 ### Patch Changes - 9cacf29: AppBar 하단에 구분선을 표시하는 `divider` 옵션을 deprecated 처리합니다. SEED React 1.3에서 제거될 예정이며, 제거 이후에는 AppBar 하단 구분선이 표시되지 않습니다. ## 1.2.10 ### Patch Changes - d8860f4: Chip `layout=withText` variant에 size 별 최소 너비를 추가합니다. - 7588e59: 신규 pressed 상태 스타일링을 위한 `$duration.pressed-scale` 및 `$timing-function.pressed-scale` 토큰을 추가합니다. (컴포넌트 변경사항 없음) ## 1.2.9 ### Patch Changes - 46bdf17: AppBar 내부 title/subtitle 영역이 폰트 스케일링이 대응된 iOS 환경에서 최대 120%까지 폰트 스케일링을 하도록 변경합니다. (기존 동작: 폰트 스케일링이 대응된 환경에서도 폰트 스케일링 설정의 영향을 받지 않음) - 79c7aa3: NotificationBadge의 size=large variant가 최소 너비를 갖도록 하고, size=large에서 표시되는 레이블이 폰트 스케일링의 영향을 받지 않도록 수정합니다. ## 1.2.8 ### Patch Changes - 5bf2e8a: `ContentPlaceholder` asset의 최대 너비를 64px에서 160px로 변경합니다. ## 1.2.7 ### Patch Changes - 9b36676: (React 컴포넌트 변경사항 없음) menu-sheet 컴포넌트 스펙에 핸들 여백을 위한 header.paddingTop을 추가하고 menu-sheet-close-button을 deprecated 처리합니다. ## 1.2.6 ### Patch Changes - e78d460: Avatar 및 Avatar Stack의 `size=56` variant를 추가합니다. ## 1.2.5 ### Patch Changes - 4f56e76: (사용자 변경사항 없음) Rootage `text-input`에 `type=singleLine` variant 정의를 추가합니다. - 67a7780: Content Placeholder 컴포넌트를 추가합니다. ## 1.2.4 ### Patch Changes - 103830e: List Item의 title fontWeight를 `$font-weight.medium`에서 `$font-weight.regular`로 변경합니다. - 751e952: `$color.stroke.focus-ring` 색상 토큰을 추가합니다. - 77cdc0e: IdentityPlaceholder의 스타일과 글리프를 업데이트하고, `identity="business"` variant를 추가합니다. ## 1.2.3 ### Patch Changes - eb390cf: BottomSheet의 title padding을 수정합니다. - left 헤더 정렬 시 오른쪽 padding을 `50px` -> `56px`로 수정합니다. - center 헤더 정렬 시 양쪽 padding을 `60px` -> `56px`로 수정합니다. - 7c3bbe7: Slider의 Value Indicator 가시성 및 트랜지션을 개선합니다. ## 1.2.2 ### Patch Changes - a3e6859: ImageFrame `rounded` 옵션을 제거하고, borderRadius를 받을 수 있게 변경합니다 - 4287600: BottomSheet title 영역에서 닫기 버튼 간격에 따라 조정된 padding 기준으로 정리하고, word-break 추가 및 description 영역 너비를 정리했습니다. ## 1.2.1 ### Patch Changes - 9446f2c: ImageFrameReactionButton에 fillIcon, lineIcon spec을 추가합니다 - 8ad9484: ImageFrameReactionButton의 SVG 사이즈와 Rootage 정의를 수정합니다. ## 1.2.0 ### Minor Changes - 0ecb893: [Help Bubble](/react/components/help-bubble) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet 업데이트 필요**: `HelpBubbleTrigger` 및 `HelpBubbleAnchor`의 내부 구조가 변경되었습니다. snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:help-bubble` - **인터페이스 변경사항이 없으므로 `HelpBubbleAnchor`와 `HelpBubbleTrigger`를 사용하는 기존 코드를 변경할 필요가 없습니다.** - `HelpBubble.Body`를 사용하여 `HelpBubble.Title`과 `HelpBubble.Description`을 감싸도록 변경되었습니다. - `zIndexOffset`을 활용하여 `HelpBubble.Positioner`의 z-index를 조정할 수 있습니다. ([예시](/react/components/help-bubble#z-index-offset)) - a58022d: `SwitchMark`를 `Switchmark`로, `RadioMark`를 `Radiomark`로 Snippet 컴포넌트 이름을 변경합니다. - **1.1 → 1.2 업그레이드 시 변경 권장**: snippet을 다시 내려받고, `SwitchMark`, `RadioMark`를 사용하는 코드를 아래와 같이 변경하세요. - `npx @seed-design/cli@latest add ui:switch ui:radio-group` - snippet에 `SwitchMark`, `RadioMark` 정의가 존재하지만, 1.3 릴리즈 시 snippet에서 해당 맵핑이 제거될 예정이므로 미리 변경해두시길 권장드립니다. ```tsx // 전 import { ListSwitchItem, ListRadioItem } from "seed-design/ui/list"; import { SwitchMark } from "seed-design/ui/switch"; import { RadioMark } from "seed-design/ui/radio-group"; } />; } value="option" title="옵션" />; ``` ```tsx // 후 import { ListSwitchItem, ListRadioItem } from "seed-design/ui/list"; import { Switchmark } from "seed-design/ui/switch"; import { Radiomark } from "seed-design/ui/radio-group"; } />; } value="option" title="옵션" />; ``` ### Patch Changes - 98dbac4: [Checkbox](/react/components/checkbox) 관련 컴포넌트를 추가합니다. - `CheckboxGroup` snippet 컴포넌트가 추가되었습니다. 사용하려면 snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:checkbox` - `CheckboxGroup`은 자체적으로 gap과 100% width를 갖습니다. `VStack`을 사용하여 `Checkbox`를 묶지 않아도 됩니다. - 기존 `Checkbox`를 `CheckboxGroup`으로 감쌀 필요는 없습니다. `CheckboxGroup`은 선택적으로 사용할 수 있습니다. - `label`, `description`, `errorMessage`, `indicator`, `showRequiredIndicator`, `labelWeight` prop을 사용할 수 있습니다. [Radio Group](/react/components/radio-group) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet 업데이트 필요**: `RadioGroup` snippet의 내부 구조가 변경되었습니다. snippet을 다시 내려받아 주세요. - `npx @seed-design/cli@latest add ui:radio-group` - `RadioGroup`이 자체적으로 gap과 100% width를 갖습니다. `VStack`을 사용하여 `RadioGroupItem`을 묶는 코드를 제거합니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `RadioGroupItem`을 묶어서 사용하던 `VStack`을 제거하여 `RadioGroupItem`이 `RadioGroup`의 direct child가 되도록 변경하세요. - `label`, `description`, `errorMessage`, `indicator`, `showRequiredIndicator`, `labelWeight` prop을 사용할 수 있습니다. - `@seed-design/react`의 `RadioGroup.Root`를 레이아웃 컴포넌트로 변경합니다. - `@seed-design/react`에서 직접 import해서 사용하는 코드가 있다면 `RadioGroup.Root`를 `@seed-design/react/primitive`의 `RadioGroup.Root`로 변경해주세요. ```tsx // 전 import { VStack } from "@seed-design/react"; import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; ; ``` ```tsx // 후 import { RadioGroup, RadioGroupItem } from "seed-design/ui/radio-group"; { /* aria-label 대신 label을 사용하여 시각적으로 레이블을 표시할 수도 있습니다. */ } ; ``` ```tsx // 전 import { RadioGroup } from "@seed-design/react"; import { ListRadioItem } from "seed-design/ui/list"; } /> ; ``` ```tsx // 후 import { RadioGroup } from "@seed-design/react/primitive"; import { ListRadioItem } from "seed-design/ui/list"; } /> ; ``` RadioGroupItem, RadioChipItem, RadioSelectBoxItem, ListRadioItem에서 `invalid` prop이 제거되었습니다. - **1.1 → 1.2 업그레이드 시 확인 필요**: `invalid` 상태는 group/field 레벨에서 설정해주세요. 각 항목을 `data-invalid` 속성으로 스타일링하는 경우 확인이 필요합니다. - 2643d17: [Select Box](/react/components/select-box) 관련 컴포넌트를 업데이트합니다. - **1.1 → 1.2 업그레이드 시 snippet을 다시 내려받아 주세요.** - `npx @seed-design/cli@latest add ui:select-box` - `CheckSelectBoxGroup`, `RadioSelectBoxRoot`의 children이 기본적으로 gap이 포함된 그리드 레이아웃으로 정렬됩니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `CheckSelectBox`, `RadioSelectBoxItem`을 묶어서 사용하던 `VStack`을 제거하여 `CheckSelectBox`와 `RadioSelectBoxItem`이 `CheckSelectBoxGroup` 또는 `RadioSelectBoxRoot`의 direct child가 되도록 변경하세요. `VStack`에 `gap` 이외의 스타일이 적용된 경우 `...`와 같이 `VStack`을 외부에 남겨두세요. - **기능 추가**: `CheckSelectBoxGroup`와 `RadioSelectBoxRoot`에 `columns`를 지정할 수 있습니다. `columns`가 `2` 이상인 경우 하위 항목에 기본적으로 `layout="vertical"`이 적용됩니다. 기본 `layout`은 하위 항목에서 오버라이드할 수 있습니다. - **기능 추가**: `CheckSelectBoxGroup`과 `RadioSelectBoxRoot`에 `label`, `description`, `errorMessage`, `indicator` 등 Fieldset 관련 prop을 사용할 수 있습니다. - **1.1 → 1.2 업그레이드 시 변경 필요**: `CheckSelectBox`, `RadioSelectBoxItem`에 기본적으로 표시되던 `Checkmark`와 `RadioMark`가 이제 표시되지 않습니다. `suffix` prop을 통해 선택적으로 추가할 수 있습니다. - 단순 마이그레이션 시 `suffix={}`와 `suffix={}`를 추가하세요. - **기능 추가**: `prefixIcon`, `footer`, `footerVisibility` prop 추가 - `footer`에 넣는 요소는 기본적으로 해당 `CheckSelectBox` 또는 `RadioSelectBoxItem`가 선택된 상태일 때 표시됩니다. `footerVisibility="always"`를 설정하여 footer 요소를 항상 표시할 수 있습니다. - `label`이 기본적으로 가로 나열되며 `$dimension.x2` gap을 갖는 flex container로 변경되었습니다. - **1.1 → 1.2 업그레이드 시 확인 권장**: `label={{/* ... */}}`와 같은 코드는 `HStack`을 `Fragment` 등으로 대체할 수 있습니다. - **문제 수정**: `CheckSelectBox`와 `RadioSelectBoxItem`에서 사용되지 않는 `children`을 타입 정의에서 제거합니다. - `CheckSelectBoxGroup`에 `label`, `aria-label`, `aria-labelledby` 중 아무것도 설정하지 않은 경우 경고를 표시합니다. (`RadioSelectBoxRoot`는 기존에도 표시) - 17c0ebd: Text Field (text-input)과 Field Button (input-button)의 포커스 및 에러 상태 border 트랜지션을 수정합니다. ## 1.1.8 ### Patch Changes - db49a84: Chip 컴포넌트 스펙에 `layout=withText` variant를 명시합니다. (스타일 변경사항 없음) - 6fab0e7: Skeleton 가시성 향상을 위해 `$gradient.shimmer-magic` 및 `$gradient.shimmer-neutral` 토큰의 색상을 업데이트합니다. - 5faef3a: 주석, 참고 사항 및 상세 리스트 등 부가 정보에 사용할 수 있는 시맨틱 텍스트 스타일 `articleNote`를 추가합니다. - 50ee0a6: `@seed-design/css@1.3` 및 `@seed-design/react@1.3`에서 제거되는 토큰 및 옵션에 관한 경고를 추가합니다. - 1.3에서 제거 예정인 색상 토큰 - [`$color.bg.layer-fill`](/docs/foundation/design-token/%24color.bg.layer-fill) - [`$gradient.fade-layer-floating`](/docs/foundation/design-token/%24gradient.fade-layer-floating) - [`$gradient.fade-layer-default`](/docs/foundation/design-token/%24gradient.fade-layer-default) - 1.3에서 제거 예정인 컴포넌트 variant - [ChipTabs](/react/components/chip-tabs) - `variant="brandSolid"` - 1.2까지 사용 가능, 1.3부터 디자인 변경 필요 - [Checkbox](/react/components/checkbox) - `weight="default"` - 0.2.4부터 `weight="regular"` 사용 가능 - `weight="default"`는 1.2까지 사용 가능, 1.3부터 `weight="regular"`만 허용 - `weight="stronger"` - 0.2.4부터 `weight="bold"` 사용 가능 - `weight="stronger"`는 1.2까지 사용 가능, 1.3부터 `weight="bold"`만 허용 - [Switch](/react/components/switch) - `size="small"` - 0.1.9부터 `size="16"` 사용 가능 - `size="small"`은 1.2까지 사용 가능, 1.3부터 `size="16"`만 허용 - `size="medium"` - 0.1.9부터 `size="32"` 사용 가능 - `size="medium"`은 1.2까지 사용 가능, 1.3부터 `size="32"`만 허용 - `StyleProps`를 상속하는 컴포넌트 - `display`, `justifyContent/justify`, `alignItems/align`, `alignContent`, `alignSelf`, `flexDirection/direction` 프로퍼티에서의 `camelCase` 값 제거 예정 - 0.0.15부터 `kebab-case` 값 사용 가능 - `camelCase` 값은 1.2까지 사용 가능, 1.3부터 `kebab-case` 값만 허용 - 예: `justifyContent="spaceBetween"` → `justifyContent="space-between"` - 영향 범위인 컴포넌트: [Box](/react/components/layout/box), [Flex](/react/components/layout/flex), [HStack](/react/components/layout/h-stack), [VStack](/react/components/layout/v-stack), [Article](/react/components/article), [List (List.Root) 및 ListItem, ListButtonItem, ListLinkItem, ListSwitchItem, ListCheckItem, ListRadioItem (List.Item)](/react/components/list), [BottomSheetBody (BottomSheet.Body)](/react/components/bottom-sheet), ResponsivePair, [Inline (deprecated)](/react/components/inline), [Columns, Column (deprecated)](/react/components/columns), [Stack (deprecated)](/react/components/stack) - 94bebf8: `$color.bg.layer-basement` 위에서 컴포넌트의 가시성을 보장하기 위해 `$color.bg.neutral-weak-alpha` 토큰을 추가합니다. - Chip `variant=solid`에 적용 - ChipTab `variant=neutralSolid`에 적용 - SegmentedControl root에 적용 ## 1.1.7 ### Patch Changes - 2f29fe8: 정적 텍스트 스타일 추가 (t1Static* ~ t10Static*) - 9119723: Checkmark `variant=ghost` `tone=neutral`에서 icon의 색상을 $color.fg.neutral로 변경하고 색상 트랜지션을 추가합니다. - 10c0765: 배너 템플릿에 사용되는 `$color.banner.*` 색상 토큰을 추가합니다. - b46264a: Rootage에 누락된 Slot Schema 정의 추가 (스타일 변경사항 없음) ## 1.1.6 ### Patch Changes - 9be0581: `radiomark` Rootage 정의를 명확화합니다. (스타일 변경사항 없음) - cc4a45a: 신규 [Elevation 가이드](https://seed-design.io/docs/foundation/elevation)에 맞는 shadow 토큰을 추가합니다. - React: Box, Flex, HStack 등 StyleProps를 사용하는 컴포넌트에서 `boxShadow` prop을 사용하여 shadow 토큰을 쉽게 사용할 수 있습니다. - 739937f: Button들의 xsmall variant의 텍스트 사이즈를 t4에서 t3로 변경해요. ## 1.1.5 ### Patch Changes - db5de74: PageBanner에 tone="magic" 스타일을 추가합니다. - 70d11b8: Segmented Control의 스타일을 업데이트합니다: Indicator에서 shadow 정의를 제거합니다. ## 1.1.4 ### Patch Changes - 8752805: List Item에 신규 active(pressed) 스타일을 적용하고, disabled 상태에서 detail 영역의 색상을 수정합니다. ## 1.1.3 ### Patch Changes - dfe6c1e: transparent 상태 컬러 추가, 컴포넌트 상태 컬러 변경, transition 추가 - `$color.bg.transparent-pressed` 컬러와 `$color.bg.transparent` 컬러가 추가되었습니다. - 다음 컴포넌트들의 색상이 transparent 관련 토큰으로 변경되었습니다. - `Chip` (outlineStrong, outlineWeak) - `Action Button` (neutralOutline, brandOutline, ghost) - `Checkmark` - `Tabs` (outline) - `List Item` - `Radiomark` - `Reaction Button` - `Select Box` - 다음 컴포넌트들의 color transition이 추가되었습니다. (duration: $duration.d3, timing-function: $timing-function.easing) - `Checkmark` - `Radiomark` - `Reaction Button` - `Select Box` - a09e6b4: 다음 컴포넌트들의 color transition을 `$duration.color-transition` (d3) 토큰으로 변경합니다 - `Action Button` - `Bottom Sheet Handle` - `Checkmark` - `Tabs` (outline) - `Chip` - `Contextual Floating Button` - `Floating Action Button` - `Input Button` - `List Item` - `Radiomark` - `Reaction Button` - `Segmented Control Item` - `Select Box` - `Text Input` - `Toggle Button` ## 1.1.2 ### Patch Changes - 53290ab: FieldButton에 Read Only 상태를 추가합니다. Disabled 상태인 FieldButton은 내부 ``도 `disabled` 속성을 갖도록 수정합니다. ## 1.1.1 ### Patch Changes - f4e07bb: 1.1 이전 버전과 호환 가능하도록 임시적으로 사용할 text-input size=medium variant를 추가합니다. - 영향 받는 React 컴포넌트: TextFieldInput, TextFieldTextarea - 114dafd: text-input의 readonly 스타일을 업데이트합니다. - 영향 받는 React 컴포넌트: TextFieldInput, TextFieldTextarea - bc3cd6f: ScrollFog 컴포넌트를 추가합니다 ## 1.1.0 ### Minor Changes - a55f584: Slider 컴포넌트를 추가합니다. - 33def2d: (BREAKING CHANGE: BottomSheet snippet을 다시 설치해야 합니다.) BottomSheet에 드래그를 통해 닫는 기능을 추가합니다. - vaul headless 코드 기반으로 seed에 맞게 커스텀하여 구현했습니다. - vaul과 동일한 인터페이스를 가지고 있습니다. (snap-points, fade-from-index, etc.) - `npx @seed-design/cli@latest add ui:bottom-sheet`로 snippet을 최신화하세요. ### Patch Changes - d6bb84d: (BREAKING CHANGE: TextField snippet을 다시 설치해야 합니다.) Text Field 관련 컴포넌트를 업데이트합니다. - 스타일 업데이트 - size 통일 및 variant (underline) 추가 - 내부적으로 Field 컴포넌트를 사용하도록 변경하여 스타일 일관성 향상 Field Button 컴포넌트를 추가합니다. - 6af6501: (BREAKING CHANGE: PageBanner snippet을 다시 설치해야 합니다.) Page Banner 스니펫을 업데이트합니다. - Box를 사용하여 스타일링하던 부분을 `PageBanner.Body`로 교체합니다. - `PageBanner.TextContent`를 `PageBanner.Content`로 이름 변경합니다. ## 1.0.6 ### Patch Changes - 15ab93a: List Item 컴포넌트의 상하 여백을 `$dimension.x2_5`에서 `$dimension.x3`로 늘립니다. ## 1.0.5 ### Patch Changes - 6aafce0: Tag Group 컴포넌트를 추가합니다. Tag Group은 아이콘 및 텍스트로 이루어진 태그를 구분 기호와 함께 수평 레이아웃으로 표시하는 컴포넌트입니다. - 1902dfa: AppBar의 스타일을 업데이트합니다. - Top Navigation의 title 및 description에 `lineHeight` 값을 정의합니다. (React AppBar 컴포넌트에 반영됩니다.) - `` 컴포넌트를 활용하여 `` 내부 아이콘을 커스터마이징할 수 있도록 수정합니다. - 4c33f07: Switch가 checked 상태가 아닐 때 thumb 크기를 줄여 상태를 인지하기 쉽도록 합니다. enabled-disabled 상태 간 트랜지션을 추가합니다. - 3df657f: Switch와 Switch Mark의 disabled 상태를 더 잘 구별할 수 있도록 스타일을 수정합니다. ## 1.0.4 ### Patch Changes - f1cf4cd: Text Field와 Multiline Text Field가 기본적으로 배경 색을 갖지 않도록 수정합니다. - 3898183: 매너온도 L9 전경 및 배경 색상의 채도를 낮춥니다. ## 1.0.3 ### Patch Changes - 6c6099d: Callout에 tone=positive variant를 추가합니다. ## 1.0.2 ### Patch Changes - 6d2e13d: MannerTemp 컴포넌트가 레이아웃에서 너비를 덜 차지하도록 업데이트합니다. ## 1.0.1 ### Patch Changes - 1420b68: MannerTemp 컴포넌트가 레이아웃에서 높이를 덜 차지하도록 업데이트합니다. ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Minor Changes - 39a96f1: (**BREAKING CHANGE**: Snackbar Snippet을 다시 설치해야합니다) Snackbar 컴포넌트 변경 - Snackbar의 배경색이 다크모드에서 흰색으로 변경됩니다. - Prefix 요소유무에 따라 여백이 변경됩니다. - `npx @seed-design/cli@latest add ui:snackbar` 명령어로 설치하세요. ### Patch Changes - e038490: (**BREAKING CHANGE**: Snippet을 다시 설치해야 합니다.) Manner Temp, Manner Temp Badge 컴포넌트를 업데이트합니다. - snippet 내 오타 수정 - 신규 10단계 반영 - 업데이트 가이드 1. `@seed-design/css@latest @seed-design/react@latest` 설치 2. `npx @seed-design/cli@latest add ui:manner-temp ui:manner-temp-badge`로 snippet 최신화 3. 온도 범위가 변경되었으므로, `` 혹은 ``과 같이 `level`을 직접 지정하여 사용하고 있는 경우가 있는지 확인 - 4153ca5: HelpBubble 컴포넌트의 배경색이 다크모드에서 흰색으로 변경됩니다. - a7d07f0: (**BREAKING CHANGE**: `SwitchMark` 사용을 위해서는 Snippet을 다시 설치해야 합니다.) Switch의 토글 영역만을 정의한 Switch Mark 컴포넌트를 추가합니다. - `npx @seed-design/cli@latest add ui:switch` 명령어로 설치하세요. (**BREAKING CHANGE**: `ListHeader` 사용을 위해서는 Snippet을 다시 설치해야 합니다.) List Header 컴포넌트를 추가합니다. - `npx @seed-design/cli@latest add ui:list` 명령어로 설치하세요. ## 0.1.4 ### Patch Changes - 0ca19c0: Segmented Control 컴포넌트를 업데이트합니다. - Notification Badge를 표시하는 notification prop을 추가합니다. - `SegmentedControlItem`의 `children`을 `string`에서 `ReactNode`로 확대합니다. - 스타일을 업데이트합니다. ## 0.1.3 ### Patch Changes - 8ebe8a5: Switch, Checkmark, Radio Mark의 스타일을 업데이트합니다. - tone=neutral variant를 추가합니다. - Switch의 thumb 크기를 조정합니다. Checkbox와 Radio의 weight variant를 default, stronger에서 regular, bold로 수정합니다. - f61b80d: 다크 모드에서의 색상 대비 보장을 위해 시맨틱 색상을 수정하고 컴포넌트에서의 색상을 변경합니다. - **$color.bg.warning-solid**: theme-dark에서 $color.palette.yellow-600 → $color.palette.yellow-800 - **$color.bg.warning-solid-pressed**: theme-dark에서 $color.palette.yellow-700 → $color.palette.yellow-900 - Badge, Page Banner의 tone=warning, variant=solid variant에서 전경 항목 색상 변경: $color.fg.neutral → $color.palette.static-black-alpha-900 ## 0.1.2 ### Patch Changes - a22b8b9: ChipTabs 컴포넌트 Variant, Size 변경 및 디자인 수정 - variant `neutralOutline` 추가 - variant `brandSolid` deprecated - size(`medium(default)` | `large`) 추가 - 12faf5a: List 컴포넌트를 추가하고, Checkbox 및 Radio 컴포넌트를 개선합니다. - List 컴포넌트를 제공하여, 정보를 구조화된 목록 형태로 표시할 수 있도록 합니다. - Checkbox와 Radio의 컨트롤 영역만을 표시하는 Checkmark와 RadioMark를 제공합니다. - Select Box에서 컨트롤 영역을 Checkmark와 RadioMark로 교체합니다. - RadioGroup 컴포넌트를 제공합니다. ## 0.1.1 ### Patch Changes - 35984d0: Chip 컴포넌트를 업데이트합니다. - 아이콘에 트랜지션 효과가 적용되지 않던 현상을 수정합니다. - Button, Toggle 등 사용되는 방식에 따라 적절한 data prop을 받도록 수정합니다. ## 0.1.0 ### Minor Changes - 8448880: 시맨틱 stroke 컬러 토큰을 업데이트합니다. **이름이 변경되는 stroke 토큰** - [Color Role 규칙](https://seed-design.io/docs/foundation/color/color-role)에 맞춰 일관적인 토큰 이름을 유지할 수 있도록 업데이트합니다. - 이름이 변경되는 stroke 토큰을 사용하고 있는 경우, 간단한 Find & Replace 마이그레이션이 필요합니다. | 기존 | 신규 | 비고 | | ------------------------------- | ------------------------------- | ---------------------------------- | | **$color.stroke.neutral-muted** | $color.stroke.neutral-subtle | 가장 먼저 마이그레이션해야 합니다. | | $color.stroke.on-image | $color.stroke.neutral-subtle | | $color.stroke.neutral | **$color.stroke.neutral-muted** | | $color.stroke.field-focused | $color.stroke.neutral-contrast | | $color.stroke.control | $color.stroke.neutral-weak | | $color.stroke.field | $color.stroke.neutral-weak | | $color.stroke.brand | $color.stroke.brand-weak | | $color.stroke.positive | $color.stroke.positive-weak | | $color.stroke.informative | $color.stroke.informative-weak | | $color.stroke.warning | $color.stroke.warning-weak | | $color.stroke.critical | $color.stroke.critical-weak | **색상이 변경되는 stroke 토큰 (마이그레이션 불필요)** `$color.stroke.neutral-contrast` (이름 변경 전 `$color.stroke.field-focused`) 모든 theme mode에서 `$color.palette.gray-800` → `$color.palette.gray-1000`로 변경되었습니다. **신규 stroke 토큰 (마이그레이션 불필요)** | 신규 | | ------------------------------- | | $color.stroke.neutral-solid | | $color.stroke.brand-solid | | $color.stroke.positive-solid | | $color.stroke.informative-solid | | $color.stroke.warning-solid | | $color.stroke.critical-solid | ## 0.0.6 ### Patch Changes - 8299ba9: Snackbar 컴포넌트를 업데이트합니다. - root 영역에 maxWidth 스펙을 추가합니다. - `pauseOnInteraction`의 기본값을 `false`에서 `true`로 변경합니다. ## 0.0.5 ### Patch Changes - f806356: Page Banner 컴포넌트를 추가합니다. Inline Banner 컴포넌트를 deprecate합니다. - Inline Banner 컴포넌트 대비 모든 `tone`에서 모든 `variant`를 지원하며, 내부 Button의 충분한 터치 영역을 보장합니다. ```tsx 새 탭에서 열기 } /> ``` 시맨틱 색상 토큰을 추가하고 수정합니다. - `$color.bg.positive-solid-pressed`: theme-dark에서 `$color.palette.green-500` → `$color.palette.green-600` - `$color.bg.warning-solid-pressed` 추가 - 1982494: Badge 컴포넌트를 업데이트합니다. - `tone=warning` variant를 추가합니다. - `maxWidth` 스펙을 추가합니다. 신규 시맨틱 색상 토큰을 추가합니다. - `$color.fg.warning` - `$color.stroke.warning` - `$color.fg.brand-contrast` - `$color.bg.brand-weak` - `$color.bg.brand-weak-pressed` ## 0.0.4 ### Patch Changes - 0be9b00: Avatar, Avatar Stack 컴포넌트에 `size=108` variant를 추가합니다. ## 0.0.3 ### Patch Changes - 5a025b7: Switch 컴포넌트를 업데이트합니다. - size: medium → 32, small → 16으로 rename합니다. - (React) `size="medium"`으로 `32`, `size="small"`로 `16`을 사용할 수 있습니다. (deprecated) - size: 24를 추가합니다. - 모든 size에 대해 레이블 스타일을 추가합니다. (기존: small에만 존재) ## 0.0.2 ### Patch Changes - 235147d: action-button: `size=medium, layout=withText` variant에서 gap을 1 → 1.5로 수정합니다. - 3c13ad7: `highlight-magic-pressed` 그라디언트 토큰을 추가합니다. ## 0.0.1 ### Patch Changes - 861ecb4: Menu Sheet 컴포넌트를 추가하는 동시에 Action Sheet과 Extended Action Sheet 컴포넌트를 deprecate합니다. - [Menu Sheet React 문서](https://seed-design.io/react/components/menu-sheet) - Menu Sheet는 기존 Extended Action Sheet의 모든 기능을 포함하는 동시에, `labelAlign` prop으로 `MenuSheetItem`를 `left` 또는 `center`로 정렬할 수 있습니다. --- ## @seed-design/rsbuild-plugin ## 2.0.0 ### Patch Changes - Updated dependencies [4ad24cc] - Updated dependencies [555525a] - Updated dependencies [7a6847e] - Updated dependencies [029d052] - Updated dependencies [54a2ff8] - Updated dependencies [d1a8d7c] - Updated dependencies [69e3b97] - Updated dependencies [57e4f1a] - Updated dependencies [2abd3ed] - Updated dependencies [270b277] - Updated dependencies [9369ff9] - Updated dependencies [4839a64] - Updated dependencies [60d1a82] - Updated dependencies [ec33023] - Updated dependencies [98b52a7] - Updated dependencies [f982d61] - Updated dependencies [7f1dbe3] - Updated dependencies [c0fd999] - Updated dependencies [31bf164] - @seed-design/css@2.0.0 ## 1.2.1 ### Patch Changes - c1bf147: `@rsbuild/core` peerDependency 범위를 확장하여 rsbuild v1.1과 v2를 모두 지원합니다. ## 1.2.0 ### Minor Changes - 47e9e6c: (BREAKING CHANGE: `@rsbuild/core`를 v2로 함께 업그레이드해야 합니다.) `@rsbuild/core` peer dependency를 v2로 갱신합니다. - `@seed-design/rsbuild-plugin`이 사용하는 `modifyHTMLTags` API와 타입(`RsbuildPlugin`, `HtmlBasicTag`)은 Rsbuild v2에서도 동일하므로 SEED 측 동작은 변화 없습니다. - `@rsbuild/core` v1을 계속 사용하려는 프로젝트는 `@seed-design/rsbuild-plugin` 이전 버전을 유지해 주세요. ## 1.1.0 ### Patch Changes - Updated dependencies [d6bb84d] - Updated dependencies [a55f584] - Updated dependencies [191005f] - Updated dependencies [b131282] - Updated dependencies [6af6501] - Updated dependencies [33def2d] - @seed-design/css@1.1.0 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [39a96f1] - Updated dependencies [34f92f2] - Updated dependencies [e038490] - Updated dependencies [4153ca5] - Updated dependencies [a7d07f0] - @seed-design/css@1.0.0 ## 0.2.0 ### Patch Changes - Updated dependencies [8448880] - @seed-design/css@0.2.0 ## 0.1.3 ### Patch Changes - 71c58fd: iOS Font Scaling - iOS 기기에서 시스템 폰트 크기 설정에 따라 동적으로 폰트 크기와 줄 높이를 조정하는 폰트 스케일링 옵션이 추가되었습니다. - 플러그인(webpack, vite, rsbuild)에서 `fontScaling` 옵션을 통해 폰트 스케일링 기능을 활성화할 수 있습니다. - `data-seed-font-scaling='enabled'` 일 때, 폰트 크기를 조정합니다. - Updated dependencies [cdc0930] - Updated dependencies [946faf7] - Updated dependencies [71c58fd] - @seed-design/css@0.1.3 ## 0.1.2 ### Patch Changes - 7b2c0f3: Updated dependencies - @seed-design/react@0.1.1 - Updated dependencies [7b2c0f3] - @seed-design/css@0.1.2 ## 0.1.0 ### Patch Changes - Updated dependencies [7cc6087] - Updated dependencies [bdca898] - @seed-design/css@0.1.0 ## 0.0.41 ### Patch Changes - Updated dependencies [561f74c] - Updated dependencies [b43de05] - @seed-design/css@0.0.41 ## 0.0.39 ### Patch Changes - Updated dependencies [f801300] - @seed-design/css@0.0.39 ## 0.0.38 ### Patch Changes - Updated dependencies [70fbaaf] - @seed-design/css@0.0.38 ## 0.0.35 ### Patch Changes - Updated dependencies [0789dc8] - @seed-design/css@0.0.35 ## 0.0.34 ### Patch Changes - Updated dependencies [92801a2] - @seed-design/css@0.0.34 ## 0.0.33 ### Patch Changes - Updated dependencies [fbdb091] - @seed-design/css@0.0.33 ## 0.0.31 ### Patch Changes - Updated dependencies [fd7c569] - @seed-design/css@0.0.31 ## 0.0.30 ### Patch Changes - Updated dependencies [285cb9b] - @seed-design/css@0.0.30 ## 0.0.29 ### Patch Changes - Updated dependencies [116ee2c] - @seed-design/css@0.0.29 ## 0.0.28 ### Patch Changes - Updated dependencies [5337e14] - @seed-design/css@0.0.28 ## 0.0.27 ### Patch Changes - Updated dependencies [9d85c16] - Updated dependencies [d951317] - Updated dependencies [b3f964d] - @seed-design/css@0.0.27 ## 0.0.26 ### Patch Changes - c8b2921: types 필드와 exports 필드를 수정해요 ## 0.0.25 ### Patch Changes - Updated dependencies [c87ede9] - @seed-design/css@0.0.25 ## 0.0.24 ### Patch Changes - Updated dependencies [4da536f] - @seed-design/css@0.0.24 ## 0.0.23 ### Patch Changes - Updated dependencies [63e1541] - @seed-design/css@0.0.23 ## 0.0.21 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [5d69d1d] - Updated dependencies [4d34760] - Updated dependencies [7ae87f8] - Updated dependencies [f144d28] - Updated dependencies [e368c69] - @seed-design/css@0.0.21 ## 0.0.19 ### Patch Changes - Updated dependencies [3c9ec66] - Updated dependencies [b3bb6e7] - @seed-design/css@0.0.19 ## 0.0.17 ### Patch Changes - Updated dependencies [c042f90] - @seed-design/css@0.0.17 ## 0.0.15 ### Patch Changes - 1bb9f7b: - vite dev에서 컴포넌트 스타일시트가 로드되지 않는 버그를 수정합니다. - 플러그인이 컴포넌트 스타일시트를 로드하는 방식을 변경합니다. - Updated dependencies [1bb9f7b] - Updated dependencies [4511814] - Updated dependencies [f4b0723] - Updated dependencies [f4b0723] - @seed-design/css@0.0.15 ## 0.0.14 ### Patch Changes - Updated dependencies [92c0b80] - Updated dependencies [c1d94d0] - @seed-design/css@0.0.14 ## 0.0.13 ### Patch Changes - Updated dependencies [7fca755] - @seed-design/css@0.0.13 ## 0.0.12 ### Patch Changes - Updated dependencies [6426379] - Updated dependencies [ee41f37] - @seed-design/css@0.0.12 ## 0.0.11 ### Patch Changes - Updated dependencies [e70f340] - Updated dependencies [72f344f] - @seed-design/css@0.0.11 ## 0.0.10 ### Patch Changes - Updated dependencies [e4b704c] - @seed-design/css@0.0.10 ## 0.0.9 ### Patch Changes - Updated dependencies [63f8651] - Updated dependencies [d9b01a9] - @seed-design/css@0.0.9 ## 0.0.8 ### Patch Changes - Updated dependencies [1424700] - Updated dependencies [0efeea1] - @seed-design/css@0.0.8 ## 0.0.7 ### Patch Changes - Updated dependencies [8aca3de] - @seed-design/css@0.0.7 ## 0.0.6 ### Patch Changes - Updated dependencies [bf198e8] - Updated dependencies [3d66c5b] - Updated dependencies [a8d5242] - Updated dependencies [ccf3989] - @seed-design/css@0.0.6 ## 0.0.5 ### Patch Changes - Updated dependencies [e3234e7] - Updated dependencies [5502bed] - @seed-design/css@0.0.5 ## 0.0.4 ### Patch Changes - Updated dependencies [6df5d19] - Updated dependencies [5cb50e7] - @seed-design/css@0.0.4 ## 0.0.3 ### Patch Changes - 23156f2: Implement rsbuild plugin - Updated dependencies [a33af94] - Updated dependencies [b180822] - @seed-design/css@0.0.3 --- ## @seed-design/rsbuild-plugin-lynx-icon ## 0.1.0 ### Minor Changes - 05c799e: Lynx 아이콘 SVG를 WebP data URL로 변환하는 Rsbuild 플러그인 추가 - Lynx 3.5에서 SVG를 지원하지 않아 빌드 시 `sharp`를 사용해 WebP로 변환하는 플러그인을 추가합니다. - 기존 수동 로더 설정 대신 `pluginLynxIcon()` 플러그인으로 간단하게 설정할 수 있습니다. --- ## @seed-design/stackflow ## 2.0.0 ### Major Changes - 7a6847e: `AppScreen` 관련 키보드 동작을 개선합니다. - `AppScreen` 액티비티 push 시, 해당 `AppScreen`으로 키보드 포커스가 이동하도록 수정합니다. - `AlertDialog` 등 모달 컴포넌트 위에 `AppScreen`을 push하는 경우에도 `AlertDialog`에 포커스가 남아 있는 문제를 수정합니다. - 가장 마지막에 등장한 `AppScreen` 안에서만 포커스가 유지되도록 수정합니다. - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. ### Patch Changes - Updated dependencies [ec33023] - @seed-design/react-primitive@2.0.0 ## 1.1.24 ### Patch Changes - 9eb5f67: 전환 애니메이션이 겹칠 때 발생하던 렌더링 깨짐을 수정합니다. - 동시 `pop()`, 빠른 백버튼, swipe-back이 겹치면 착지 화면이 약 1/3 왼쪽으로 밀리거나 AppBar가 보이지 않던 문제를 수정합니다. - `pop(2)`, `pop(); pop();` 등 한 번에 여러 화면을 닫는 동작은 기존과 동일하게 동작합니다. ## 1.1.23 ### Patch Changes - 9cacf29: AppBar 하단에 구분선을 표시하는 `divider` 옵션을 deprecated 처리합니다. SEED React 1.3에서 제거될 예정이며, 제거 이후에는 AppBar 하단 구분선이 표시되지 않습니다. ## 1.1.22 ### Patch Changes - 09882f6: `@seed-design/css`에 대한 `peerDependencies` 호환 범위를 정확하게 명시합니다. WAAPI 기반 AppScreen 전환(1.1.21에 도입)은 css 패키지의 transition selector 제거 + AppBar background slot 추가 변경과 짝을 이루어야 정상 동작합니다. 이전 표현(`>=1.1.19`)은 호환되지 않는 구버전 css도 허용하여 잘못된 조합 시 화면 전환 깨짐/AppBar 배경 사라짐 등의 문제가 발생할 수 있었습니다. 새 표현 `>=1.1.25 <1.2.0 || >=1.2.11`은 다음을 보장합니다: - 1.1.x: css 1.1.25 이상 (백포팅된 WAAPI-호환 css) - 1.2.x: css 1.2.11 이상 (PR #1444가 머지된 css) ## 1.1.21 ### Patch Changes - be7022a: AppScreen 전환 애니메이션의 성능을 개선하고 스와이프 백 관련 버그를 수정합니다. - 스와이프 중 CSS variable cascade로 인한 스타일 재계산 병목을 제거하고, 개별 요소에 직접 inline style을 적용하는 방식으로 변경합니다. - 스와이프 종료 시 발생하던 플리커(CSS 핸드오프 gap)를 WAAPI(Web Animations API)로 대체하여 해결합니다. - 스와이프 complete 후 CSS `[pop]` animation이 재트리거되는 이중 애니메이션 문제를 해결합니다. - 제스처 중 `useState` 기반 상태 관리를 `useRef`로 변경하여 불필요한 React 리렌더를 제거합니다. - `slideFromRightIOS`, `fadeFromBottomAndroid`, `fadeIn` 세 가지 transition style 모두 지원합니다. - 프로덕션에서 무거운 activity를 push/pop 할 때 애니메이션이 경로 중간부터 시작되던 jank를 수정합니다. (`requestAnimationFrame` 대기 구간 제거) - `Animation.finished`를 지원하지 않는 구형 WebView(Chrome < 84)에서 push → pop 시 화면이 비어버리던 문제를 `onfinish`/`oncancel` 기반 폴백과 setTimeout race 안전망으로 해결합니다. - AppBar 배경을 pseudo-element에서 일반 DOM slot으로 옮겨, `pseudoElement` WAAPI 옵션을 지원하지 않는 브라우저(Chrome < 82)에서도 swipe back 중 AppBar 전환이 정상 동작하도록 합니다. ## 1.1.20 ### Patch Changes - 846036b: `AppBarSlot` 컴포넌트를 추가합니다. `AppBar` 내에 `AppBarLeft`, `AppBarMain`, `AppBarRight`에 대응되지 않는 커스텀 요소를 배치하는 경우, 다른 요소와 동일한 화면 전환 트랜지션을 적용하기 위해 사용할 수 있습니다. ## 1.1.19 ### Patch Changes - da4c6d4: AppScreen 스와이프 백 제스처가 좌→우 단방향으로만 동작하도록 수정합니다. - 스와이프 백 중 초기 위치보다 왼쪽(역방향)으로 드래그할 수 없도록 수정합니다. - `onSwipeBackMove` 콜백의 `displacement` 및 `displacementRatio` 값이 항상 0 이상으로 전달되도록 수정합니다. - 9eb86f4: Stackflow 플러그인 `seedPlugin`이 콜백 형태의 옵션을 지원하도록 수정합니다. Stack에 제공하는 `initialContext`를 통해 theme을 동적으로 결정할 수 있습니다. ## 1.1.18 ### Patch Changes - af9256e: AppScreen에 `layerOffsetTop` 또는 `layerOffsetBottom` 사용 시 해당 속성이 AppBar DOM에 attribute로 설정되는 문제를 수정합니다. ## 1.1.17 ### Patch Changes - 6db2515: AppBar의 `bg` 또는 `background` 프로퍼티로 배경 색상을 조절할 수 있는 옵션을 추가합니다. (`tone="layer"`인 경우 적용) ## 1.1.16 ### Patch Changes - 6d30b72: Stackflow와 함께 AppScreen 사용 시 최상위 AppScreen이 push/pop될 때, 이외의 AppScreen이 고유한 `transitionStyle`을 재생하는 문제를 수정합니다. 같은 스택 내에 여러 `transitionStyle`이 공존할 때 자연스러운 트랜지션을 제공하기 위해 최상위 AppScreen의 `transitionStyle`을 재생합니다. ([데모](https://seed-design.io/react/stackflow/app-screen#transition-styles)) - 예를 들면, `transitionStyle="fadeFromBottomAndroid"`인 0번 AppScreen 위에 `transitionStyle="slideFromLeftIOS"`인 1번 AppScreen이 push되는 경우, 0번 AppScreen이 `slideFromLeftIOS` 트랜지션을 재생하도록 수정합니다. - 0번 AppScreen이 자연스럽게 좌측으로 조금 밀려나며 어두워지고(`slideFromLeftIOS`) 1번 AppScreen이 우측에서 슬라이드 인(`slideFromLeftIOS`) - Updated dependencies [2f29fe8] - Updated dependencies [9119723] - Updated dependencies [6d30b72] - Updated dependencies [10c0765] - Updated dependencies [5e462db] - @seed-design/css@1.1.16 ## 1.1.15 ### Patch Changes - e4b0ce8: AppScreen 스와이프 중 취소 시 트랜지션이 끝난 뒤에도 `GlobalInteraction`의 `data-swipe-back-state`가 `idle`로 되돌아가지 않고 `canceling`으로 남아 있는 문제를 수정합니다. - Updated dependencies [76acd7e] - Updated dependencies [7a428ec] - Updated dependencies [498a9e7] - @seed-design/css@1.1.15 ## 1.1.11 ### Patch Changes - ea488c5: @seed-design/stackflow: @stackflow/react-ui-core를 peerDependencies에서 dependencies로 이동합니다. ## 1.1.9 ### Patch Changes - 4e7e15b: Stackflow 플러그인이 최신이 아닌 data attribute에 의해 잘못된 트랜지션을 재생하지 않도록 수정합니다. - Updated dependencies [37d332d] - Updated dependencies [77517f1] - @seed-design/css@1.1.9 ## 1.1.5 ### Patch Changes - 7529e31: @seed-design/stackflow 패키지에서 `useActivityZIndexBase` 훅을 제공합니다. - Updated dependencies [53290ab] - @seed-design/css@1.1.5 ## 1.1.0 ### Patch Changes - b131282: AppScreen에 `tone` 속성을 조절해 그라데이션이 들어간 투명한 배경을 사용할 수 있습니다. - AppBar에 있던 `tone` 속성을 AppScreen에서도 사용할 수 있도록 이동합니다. - AppScreen, AppBar 둘 다 `tone` 속성을 사용할 수 있도록 합니다. - Updated dependencies [d6bb84d] - Updated dependencies [a55f584] - Updated dependencies [191005f] - Updated dependencies [b131282] - Updated dependencies [6af6501] - Updated dependencies [33def2d] - @seed-design/css@1.1.0 ## 1.0.5 ### Patch Changes - 9b91751: AppScreen에서 스와이프로 pop할 때 AppScreen이 한번 깜빡거리고 닫히는 버그를 수정합니다. - Updated dependencies [f1cf4cd] - Updated dependencies [9b91751] - Updated dependencies [3898183] - @seed-design/css@1.0.5 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [39a96f1] - Updated dependencies [34f92f2] - Updated dependencies [e038490] - Updated dependencies [4153ca5] - Updated dependencies [a7d07f0] - @seed-design/css@1.0.0 - @seed-design/react-primitive@1.0.0 - @seed-design/dom-utils@1.0.0 ## 0.2.0 ### Patch Changes - Updated dependencies [8448880] - @seed-design/css@0.2.0 ## 0.1.3 ### Patch Changes - cdc0930: `@seed-design/stackflow` 백스와이프 애니메이션 개선 - iOS 스타일 화면 전환 애니메이션의 지속 시간과 타이밍 함수가 `300ms`에서 `350ms`로 조정되어 더 부드러운 전환 효과를 제공합니다. - 스와이프 백 제스처 시 애니메이션이 보다 자연스럽고 일관되게 표현됩니다. - 스와이프 백 종료 시 CSS 변수를 활용해 전환 상태를 명확히 하여 사용자 경험이 개선되었습니다. - Updated dependencies [cdc0930] - Updated dependencies [946faf7] - Updated dependencies [71c58fd] - @seed-design/css@0.1.3 ## 0.1.2 ### Patch Changes - 7b2c0f3: Updated dependencies - @seed-design/react@0.1.1 - Updated dependencies [7b2c0f3] - @seed-design/css@0.1.2 ## 0.1.0 ### Patch Changes - Updated dependencies [7cc6087] - Updated dependencies [bdca898] - @seed-design/css@0.1.0 ## 0.0.41 ### Patch Changes - Updated dependencies [561f74c] - Updated dependencies [b43de05] - @seed-design/css@0.0.41 ## 0.0.39 ### Patch Changes - Updated dependencies [f801300] - @seed-design/css@0.0.39 ## 0.0.38 ### Patch Changes - Updated dependencies [70fbaaf] - @seed-design/css@0.0.38 ## 0.0.37 ### Patch Changes - 97a1237: `@stackflow/react-ui-core`의 usePreventTouchDuringTransition prop `ref`로 이름을 변경했습니다 ## 0.0.36 ### Patch Changes - 479e38b: 화면 전환 중 터치 입력이 차단합니다. ## 0.0.35 ### Patch Changes - Updated dependencies [0789dc8] - @seed-design/css@0.0.35 ## 0.0.34 ### Patch Changes - Updated dependencies [92801a2] - @seed-design/css@0.0.34 ## 0.0.33 ### Patch Changes - Updated dependencies [fbdb091] - @seed-design/css@0.0.33 ## 0.0.31 ### Patch Changes - Updated dependencies [fd7c569] - @seed-design/css@0.0.31 ## 0.0.30 ### Patch Changes - Updated dependencies [285cb9b] - @seed-design/css@0.0.30 ## 0.0.29 ### Patch Changes - 29ec9f0: `reactSlot.createSlot is not a function` 오류가 발생하지 않도록, radix-ui/react-slot 버전을 1.2.3으로 수정합니다. - Updated dependencies [116ee2c] - Updated dependencies [29ec9f0] - @seed-design/css@0.0.29 - @seed-design/react-primitive@0.0.3 ## 0.0.28 ### Patch Changes - Updated dependencies [5337e14] - @seed-design/css@0.0.28 ## 0.0.27 ### Patch Changes - 7851a31: RSC 지원을 위한 "use client" directive를 추가합니다. - Updated dependencies [9d85c16] - Updated dependencies [d951317] - Updated dependencies [b3f964d] - @seed-design/css@0.0.27 ## 0.0.25 ### Patch Changes - Updated dependencies [c87ede9] - @seed-design/css@0.0.25 ## 0.0.24 ### Patch Changes - Updated dependencies [4da536f] - @seed-design/css@0.0.24 ## 0.0.23 ### Patch Changes - 63e1541: AppBar의 배경이 상단 safe-area를 덮도록 수정합니다. - Updated dependencies [63e1541] - @seed-design/css@0.0.23 ## 0.0.21 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [5d69d1d] - Updated dependencies [4d34760] - Updated dependencies [7ae87f8] - Updated dependencies [f144d28] - Updated dependencies [e368c69] - @seed-design/css@0.0.21 - @seed-design/react-primitive@0.0.2 - @seed-design/dom-utils@0.0.2 ## 0.0.19 ### Patch Changes - Updated dependencies [3c9ec66] - Updated dependencies [b3bb6e7] - @seed-design/css@0.0.19 ## 0.0.17 ### Patch Changes - c042f90: recipe에서 직접 스타일시트 의존성을 표현하도록 변경합니다. - Updated dependencies [c042f90] - @seed-design/css@0.0.17 ## 0.0.15 ### Patch Changes - 1bb9f7b: - vite dev에서 컴포넌트 스타일시트가 로드되지 않는 버그를 수정합니다. - 플러그인이 컴포넌트 스타일시트를 로드하는 방식을 변경합니다. - Updated dependencies [1bb9f7b] - Updated dependencies [4511814] - Updated dependencies [f4b0723] - Updated dependencies [f4b0723] - @seed-design/css@0.0.15 ## 0.0.14 ### Patch Changes - Updated dependencies [92c0b80] - Updated dependencies [c1d94d0] - @seed-design/css@0.0.14 ## 0.0.13 ### Patch Changes - Updated dependencies [7fca755] - @seed-design/css@0.0.13 ## 0.0.12 ### Patch Changes - Updated dependencies [6426379] - Updated dependencies [ee41f37] - @seed-design/css@0.0.12 ## 0.0.11 ### Patch Changes - Updated dependencies [e70f340] - Updated dependencies [72f344f] - @seed-design/css@0.0.11 ## 0.0.10 ### Patch Changes - Updated dependencies [e4b704c] - @seed-design/css@0.0.10 ## 0.0.9 ### Patch Changes - Updated dependencies [63f8651] - Updated dependencies [d9b01a9] - @seed-design/css@0.0.9 ## 0.0.8 ### Patch Changes - Updated dependencies [1424700] - Updated dependencies [0efeea1] - @seed-design/css@0.0.8 ## 0.0.7 ### Patch Changes - Updated dependencies [8aca3de] - @seed-design/css@0.0.7 ## 0.0.6 ### Patch Changes - Updated dependencies [bf198e8] - Updated dependencies [3d66c5b] - Updated dependencies [a8d5242] - Updated dependencies [ccf3989] - @seed-design/css@0.0.6 ## 0.0.5 ### Patch Changes - Updated dependencies [e3234e7] - Updated dependencies [5502bed] - @seed-design/css@0.0.5 ## 0.0.4 ### Patch Changes - Updated dependencies [6df5d19] - Updated dependencies [5cb50e7] - @seed-design/css@0.0.4 ## 0.0.3 ### Patch Changes - Updated dependencies [a33af94] - Updated dependencies [b180822] - @seed-design/css@0.0.3 ## 0.0.2 ### Patch Changes - Updated dependencies [d04e344] - @seed-design/css@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/css@0.0.1 - @seed-design/react-primitive@0.0.1 - @seed-design/dom-utils@0.0.1 ## 0.0.1-rc.4 ### Patch Changes - Updated dependencies [93cfc30] - @seed-design/css@0.0.1-rc.4 ## 0.0.1-rc.3 ### Patch Changes - Updated dependencies [cc4b2c5] - @seed-design/css@0.0.1-rc.3 ## 0.0.1-rc.1 ### Patch Changes - Updated dependencies [14c9983] - @seed-design/css@0.0.1-rc.1 ## 0.0.1-rc.0 ### Patch Changes - Seed Design V3 release candidate - Updated dependencies - @seed-design/css@0.0.1-rc.0 - @seed-design/react-primitive@0.0.1-rc.0 - @seed-design/dom-utils@0.0.1-rc.0 --- ## @seed-design/stylesheet ## 1.1.1 ### Patch Changes - 1b49be1: V3 compat을 강제로 적용하지 않고, 선택적으로 적용할 수 있도록 변경합니다. ## 1.1.0 ### Patch Changes - 93cfc30: feat: change theming data attribute names - Seed Design V3의 theming과 호환되도록 data attribute를 추가합니다. - 9c06ce7: V3 다크모드 배경색과 일치하도록 주요 배경 색상을 변경합니다. ## 1.0.4 ### Patch Changes - cf271ad: sync design token ## 1.0.3 ### Patch Changes - 19be06c: recover lost changes ## 1.0.2 ### Patch Changes - fc69b3e: chore(stylesheet): change css selector logic ## 1.0.1 ### Patch Changes - 21773c2: style(stylesheet): add css selector ## 1.0.0 ### Major Changes - 1.0.0 Release ## Karrot UI → Seed Design 주요 변경 사항 - 프로젝트/디자인 시스템 명이 **Seed Design**으로 리브랜딩 됩니다. - 패키지 명이 `@seed-design/*` 으로 변경됩니다. - `@karrot-ui/*` packages are deprecated. - `color-scheme: light dark` 지원이 기본값이 됩니다. - 디자인 토큰이 [KDT](https://github.com/daangn/kdt/tree/main/language) 의미론을 따릅니다. --- ## @seed-design/tailwind3-plugin ## 2.1.0 ### Minor Changes - f2b198d: `$color.bg.layer-fill` 토큰을 다시 추가합니다. `2.0.0`에서 제거했으나 마땅한 대체 토큰이 없어 deprecated 상태로 되살립니다. 값은 제거 이전과 동일합니다. 추후 동일한 값의 새 이름 토큰으로 대체된 뒤 `3.0.0`에서 제거될 예정입니다. `@seed-design/tailwind3-plugin`과 `@seed-design/tailwind4-theme`의 `@seed-design/css` peer dependency 버전 범위를 `^2.1.0`으로 올립니다. ## 2.0.0 ### Major Changes - d1a8d7c: 신규 타이포그래피 토큰을 추가합니다. - **폰트 크기 토큰 (`$font-size`)** - `t11`, `t11-static` - `t12`, `t12-static` - `t13`, `t13-static` - `t14`, `t14-static` - **줄 간격 토큰 (`$line-height`)** - `t11`, `t11-static` - `t12`, `t12-static` - `t13`, `t13-static` - `t14`, `t14-static` - **텍스트 스타일 (`textStyle`)** - `t8Regular`, `t8Medium`, `t8StaticRegular`, `t8StaticMedium` - `t9Regular`, `t9Medium`, `t9StaticRegular`, `t9StaticMedium` - `t10Regular`, `t10Medium`, `t10StaticRegular`, `t10StaticMedium` - `t11Regular`, `t11Medium`, `t11Bold`, `t11StaticRegular`, `t11StaticMedium`, `t11StaticBold` - `t12Regular`, `t12Medium`, `t12Bold`, `t12StaticRegular`, `t12StaticMedium`, `t12StaticBold` - `t13Regular`, `t13Medium`, `t13Bold`, `t13StaticRegular`, `t13StaticMedium`, `t13StaticBold` - `t14Regular`, `t14Medium`, `t14Bold`, `t14StaticRegular`, `t14StaticMedium`, `t14StaticBold` - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. ## 1.1.19 ### Patch Changes - 7588e59: 신규 pressed 상태 스타일링을 위한 `$duration.pressed-scale` 및 `$timing-function.pressed-scale` 토큰을 추가합니다. (컴포넌트 변경사항 없음) ## 1.1.18 ### Patch Changes - 751e952: `$color.stroke.focus-ring` 색상 토큰을 추가합니다. ## 1.1.17 ### Patch Changes - 6fab0e7: Skeleton 가시성 향상을 위해 `$gradient.shimmer-magic` 및 `$gradient.shimmer-neutral` 토큰의 색상을 업데이트합니다. - 5faef3a: 주석, 참고 사항 및 상세 리스트 등 부가 정보에 사용할 수 있는 시맨틱 텍스트 스타일 `articleNote`를 추가합니다. - 94bebf8: `$color.bg.layer-basement` 위에서 컴포넌트의 가시성을 보장하기 위해 `$color.bg.neutral-weak-alpha` 토큰을 추가합니다. - Chip `variant=solid`에 적용 - ChipTab `variant=neutralSolid`에 적용 - SegmentedControl root에 적용 ## 1.1.16 ### Patch Changes - 2f29fe8: 정적 텍스트 스타일 추가 (t1Static* ~ t10Static*) - 10c0765: 배너 템플릿에 사용되는 `$color.banner.*` 색상 토큰을 추가합니다. ## 1.1.13 ### Patch Changes - cc4a45a: 신규 [Elevation 가이드](https://seed-design.io/docs/foundation/elevation)에 맞는 shadow 토큰을 추가합니다. - React: Box, Flex, HStack 등 StyleProps를 사용하는 컴포넌트에서 `boxShadow` prop을 사용하여 shadow 토큰을 쉽게 사용할 수 있습니다. ## 1.1.6 ### Patch Changes - dfe6c1e: transparent 상태 컬러 추가, 컴포넌트 상태 컬러 변경, transition 추가 - `$color.bg.transparent-pressed` 컬러와 `$color.bg.transparent` 컬러가 추가되었습니다. - 다음 컴포넌트들의 색상이 transparent 관련 토큰으로 변경되었습니다. - `Chip` (outlineStrong, outlineWeak) - `Action Button` (neutralOutline, brandOutline, ghost) - `Checkmark` - `Tabs` (outline) - `List Item` - `Radiomark` - `Reaction Button` - `Select Box` - 다음 컴포넌트들의 color transition이 추가되었습니다. (duration: $duration.d3, timing-function: $timing-function.easing) - `Checkmark` - `Radiomark` - `Reaction Button` - `Select Box` - a09e6b4: 다음 컴포넌트들의 color transition을 `$duration.color-transition` (d3) 토큰으로 변경합니다 - `Action Button` - `Bottom Sheet Handle` - `Checkmark` - `Tabs` (outline) - `Chip` - `Contextual Floating Button` - `Floating Action Button` - `Input Button` - `List Item` - `Radiomark` - `Reaction Button` - `Segmented Control Item` - `Select Box` - `Text Input` - `Toggle Button` ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - e038490: (**BREAKING CHANGE**: Snippet을 다시 설치해야 합니다.) Manner Temp, Manner Temp Badge 컴포넌트를 업데이트합니다. - snippet 내 오타 수정 - 신규 10단계 반영 - 업데이트 가이드 1. `@seed-design/css@latest @seed-design/react@latest` 설치 2. `npx @seed-design/cli@latest add ui:manner-temp ui:manner-temp-badge`로 snippet 최신화 3. 온도 범위가 변경되었으므로, `` 혹은 ``과 같이 `level`을 직접 지정하여 사용하고 있는 경우가 있는지 확인 ## 0.2.0 ### Minor Changes - 8448880: 시맨틱 stroke 컬러 토큰을 업데이트합니다. **이름이 변경되는 stroke 토큰** - [Color Role 규칙](https://seed-design.io/docs/foundation/color/color-role)에 맞춰 일관적인 토큰 이름을 유지할 수 있도록 업데이트합니다. - 이름이 변경되는 stroke 토큰을 사용하고 있는 경우, 간단한 Find & Replace 마이그레이션이 필요합니다. | 기존 | 신규 | 비고 | | ------------------------------- | ------------------------------- | ---------------------------------- | | **$color.stroke.neutral-muted** | $color.stroke.neutral-subtle | 가장 먼저 마이그레이션해야 합니다. | | $color.stroke.on-image | $color.stroke.neutral-subtle | | $color.stroke.neutral | **$color.stroke.neutral-muted** | | $color.stroke.field-focused | $color.stroke.neutral-contrast | | $color.stroke.control | $color.stroke.neutral-weak | | $color.stroke.field | $color.stroke.neutral-weak | | $color.stroke.brand | $color.stroke.brand-weak | | $color.stroke.positive | $color.stroke.positive-weak | | $color.stroke.informative | $color.stroke.informative-weak | | $color.stroke.warning | $color.stroke.warning-weak | | $color.stroke.critical | $color.stroke.critical-weak | **색상이 변경되는 stroke 토큰 (마이그레이션 불필요)** `$color.stroke.neutral-contrast` (이름 변경 전 `$color.stroke.field-focused`) 모든 theme mode에서 `$color.palette.gray-800` → `$color.palette.gray-1000`로 변경되었습니다. **신규 stroke 토큰 (마이그레이션 불필요)** | 신규 | | ------------------------------- | | $color.stroke.neutral-solid | | $color.stroke.brand-solid | | $color.stroke.positive-solid | | $color.stroke.informative-solid | | $color.stroke.warning-solid | | $color.stroke.critical-solid | ## 0.1.15 ### Patch Changes - c51a261: font-size, line-height 토큰에 static variant를 추가합니다. - `--seed-font-size-t1-static` ~ `--seed-font-size-t10-static` - `--seed-line-height-t1-static` ~ `--seed-line-height-t10-static` - 3de4cec: 플랫폼별 조건부 폰트 스케일링 제한 (iOS: 135%, Android: 150%) 적용 - CSS 변수 `--seed-{font-size|line-height}-limit-{min|max}` 도입 - 빌드 타임 basePx 계산을 런타임 static 토큰 참조로 대체 - global.ts에 폰트 스케일링 변수 통합 ## 0.1.14 ### Patch Changes - f806356: Page Banner 컴포넌트를 추가합니다. Inline Banner 컴포넌트를 deprecate합니다. - Inline Banner 컴포넌트 대비 모든 `tone`에서 모든 `variant`를 지원하며, 내부 Button의 충분한 터치 영역을 보장합니다. ```tsx 새 탭에서 열기 } /> ``` 시맨틱 색상 토큰을 추가하고 수정합니다. - `$color.bg.positive-solid-pressed`: theme-dark에서 `$color.palette.green-500` → `$color.palette.green-600` - `$color.bg.warning-solid-pressed` 추가 - 1982494: Badge 컴포넌트를 업데이트합니다. - `tone=warning` variant를 추가합니다. - `maxWidth` 스펙을 추가합니다. 신규 시맨틱 색상 토큰을 추가합니다. - `$color.fg.warning` - `$color.stroke.warning` - `$color.fg.brand-contrast` - `$color.bg.brand-weak` - `$color.bg.brand-weak-pressed` ## 0.1.6 ### Patch Changes - 3c13ad7: `highlight-magic-pressed` 그라디언트 토큰을 추가합니다. ## 0.1.3 ### Patch Changes - 946faf7: 그라디언트 토큰 추가 및 변경 - `fade-layer-floating`, `fade-layer-default` 토큰이 추가되었습니다. - `$gradient.shimmer-magic` 토큰 stop color가 변경되었습니다. ## 0.1.2 ### Patch Changes - 7b2c0f3: Updated dependencies - @seed-design/react@0.1.1 ## 0.1.1 ### Patch Changes - e3b782d: `stroke.neutral`, `stroke.neutral-muted`, `stroke.on-image`의 컬러를 alpha 값으로 변경합니다. ## 0.0.41 ### Patch Changes - b43de05: Gradient 컬러를 추가합니다 ## 0.0.39 ### Patch Changes - f801300: 새로운 black, white alpha 값을 추가합니다 `$color.palette.static-black-alpha-50` (예전 값) - 예전 값: #0000000d (투명도 약 5.1%) - 변경 값: `$color.palette.static-black-alpha-200` (투명도 4.7%) `$color.palette.static-black-alpha-200` (예전 값) - 예전 값: #00000033 (투명도 20%) - 변경 값: `$color.palette.static-black-alpha-500` (투명도 17.3%) `$color.palette.static-black-alpha-500` (예전 값) - 예전 값: #00000080 (투명도 약 50.2%) - 변경 값: `$color.palette.static-black-alpha-700` (투명도 45.5%) `$color.palette.static-white-alpha-200` (예전 값) - 예전 값: #ffffff33 (투명도 20%) - 변경 값: `$color.palette.static-white-alpha-300` (투명도 18%) `$color.palette.static-white-alpha-800` (예전 값) - 예전 값: #ffffffcc (투명도 약 80%) - 변경 값: `$color.palette.static-white-alpha-800` (투명도 87.1%) - (참고: 이 값은 이름은 같지만 실제 투명도 값은 80%에서 87.1%로 변경되었습니다.) ## 0.0.21 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. ## 0.0.12 ### Patch Changes - ac83753: tailwind plugin (v3), tailwind theme (v4)를 제공해요. --- ## @seed-design/tailwind4-theme ## 2.1.0 ### Minor Changes - f2b198d: `$color.bg.layer-fill` 토큰을 다시 추가합니다. `2.0.0`에서 제거했으나 마땅한 대체 토큰이 없어 deprecated 상태로 되살립니다. 값은 제거 이전과 동일합니다. 추후 동일한 값의 새 이름 토큰으로 대체된 뒤 `3.0.0`에서 제거될 예정입니다. `@seed-design/tailwind3-plugin`과 `@seed-design/tailwind4-theme`의 `@seed-design/css` peer dependency 버전 범위를 `^2.1.0`으로 올립니다. ## 2.0.0 ### Major Changes - d1a8d7c: 신규 타이포그래피 토큰을 추가합니다. - **폰트 크기 토큰 (`$font-size`)** - `t11`, `t11-static` - `t12`, `t12-static` - `t13`, `t13-static` - `t14`, `t14-static` - **줄 간격 토큰 (`$line-height`)** - `t11`, `t11-static` - `t12`, `t12-static` - `t13`, `t13-static` - `t14`, `t14-static` - **텍스트 스타일 (`textStyle`)** - `t8Regular`, `t8Medium`, `t8StaticRegular`, `t8StaticMedium` - `t9Regular`, `t9Medium`, `t9StaticRegular`, `t9StaticMedium` - `t10Regular`, `t10Medium`, `t10StaticRegular`, `t10StaticMedium` - `t11Regular`, `t11Medium`, `t11Bold`, `t11StaticRegular`, `t11StaticMedium`, `t11StaticBold` - `t12Regular`, `t12Medium`, `t12Bold`, `t12StaticRegular`, `t12StaticMedium`, `t12StaticBold` - `t13Regular`, `t13Medium`, `t13Bold`, `t13StaticRegular`, `t13StaticMedium`, `t13StaticBold` - `t14Regular`, `t14Medium`, `t14Bold`, `t14StaticRegular`, `t14StaticMedium`, `t14StaticBold` - 60d1a82: 1.2에서 deprecate된 옵션을 제거합니다. - 색상 토큰 - `$color.bg.layer-fill`: 라이트 및 다크 모드에서 모두 테스트 후 `$color.bg.neutral-weak`으로 대체할 수 있습니다. - 그라디언트 토큰 - `$gradient.fade-layer-floating` - `$gradient.fade-layer-default` - Chip Tabs의 `brandSolid` variant - AppBar의 `divider` 옵션 - Image Frame의 `rounded` variant: `borderRadius` 옵션을 사용해주세요. - Switch의 `small` 및 `medium` size: 각각 `16`과 `32`를 사용해주세요. - Checkbox의 `default` 및 `stronger` weight: 각각 `regular`와 `bold`를 사용해주세요. - `` 등 유틸리티 컴포넌트 레이아웃 프로퍼티의 camelCase 옵션: kebab-case 옵션을 사용해주세요. - `display`, `justifyContent`, `justify`, `alignItems`, `align`, `alignContent`, `alignSelf`, `flexDirection`, `direction` - `AppBar`의 `divider` 옵션 - 하단 구분선이 더 이상 표시되지 않습니다. - `BottomSheetRoot` (`DrawerRoot`)의 `noBodyStyles` 옵션 - 제거되어 기본값(true)처럼 동작합니다. - `BottomSheetRoot` (`DrawerRoot`)의 `preventScrollRestoration` 옵션 - 제거되어 기본값(false)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 옵션 - BottomSheet는 항상 아래에서 올라오므로 `direction`을 받지 않습니다. - `BottomSheetBackdrop` (`DrawerBackdrop`)의 `forceMount` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `lazyMount`/`unmountOnExit` 옵션으로 대체할 수 있습니다. - `BottomSheetContent` (`DrawerContent`)의 `onPointerDownOutside`, `onOpenAutoFocus`, `onCloseAutoFocus`, `onEscapeKeyDown`, `onInteractOutside`, `forceMount`, `onFocusOutside` 옵션 - 제거되어 `BottomSheetRoot` (`DrawerRoot`)의 `onOpenChange` 두 번째 인자 `details`를 통해 대체할 수 있습니다. ## 1.1.19 ### Patch Changes - 7588e59: 신규 pressed 상태 스타일링을 위한 `$duration.pressed-scale` 및 `$timing-function.pressed-scale` 토큰을 추가합니다. (컴포넌트 변경사항 없음) ## 1.1.18 ### Patch Changes - 751e952: `$color.stroke.focus-ring` 색상 토큰을 추가합니다. ## 1.1.17 ### Patch Changes - 6fab0e7: Skeleton 가시성 향상을 위해 `$gradient.shimmer-magic` 및 `$gradient.shimmer-neutral` 토큰의 색상을 업데이트합니다. - 5faef3a: 주석, 참고 사항 및 상세 리스트 등 부가 정보에 사용할 수 있는 시맨틱 텍스트 스타일 `articleNote`를 추가합니다. - 94bebf8: `$color.bg.layer-basement` 위에서 컴포넌트의 가시성을 보장하기 위해 `$color.bg.neutral-weak-alpha` 토큰을 추가합니다. - Chip `variant=solid`에 적용 - ChipTab `variant=neutralSolid`에 적용 - SegmentedControl root에 적용 ## 1.1.16 ### Patch Changes - 2f29fe8: 정적 텍스트 스타일 추가 (t1Static* ~ t10Static*) - 10c0765: 배너 템플릿에 사용되는 `$color.banner.*` 색상 토큰을 추가합니다. ## 1.1.13 ### Patch Changes - cc4a45a: 신규 [Elevation 가이드](https://seed-design.io/docs/foundation/elevation)에 맞는 shadow 토큰을 추가합니다. - React: Box, Flex, HStack 등 StyleProps를 사용하는 컴포넌트에서 `boxShadow` prop을 사용하여 shadow 토큰을 쉽게 사용할 수 있습니다. ## 1.1.6 ### Patch Changes - dfe6c1e: transparent 상태 컬러 추가, 컴포넌트 상태 컬러 변경, transition 추가 - `$color.bg.transparent-pressed` 컬러와 `$color.bg.transparent` 컬러가 추가되었습니다. - 다음 컴포넌트들의 색상이 transparent 관련 토큰으로 변경되었습니다. - `Chip` (outlineStrong, outlineWeak) - `Action Button` (neutralOutline, brandOutline, ghost) - `Checkmark` - `Tabs` (outline) - `List Item` - `Radiomark` - `Reaction Button` - `Select Box` - 다음 컴포넌트들의 color transition이 추가되었습니다. (duration: $duration.d3, timing-function: $timing-function.easing) - `Checkmark` - `Radiomark` - `Reaction Button` - `Select Box` - a09e6b4: 다음 컴포넌트들의 color transition을 `$duration.color-transition` (d3) 토큰으로 변경합니다 - `Action Button` - `Bottom Sheet Handle` - `Checkmark` - `Tabs` (outline) - `Chip` - `Contextual Floating Button` - `Floating Action Button` - `Input Button` - `List Item` - `Radiomark` - `Reaction Button` - `Segmented Control Item` - `Select Box` - `Text Input` - `Toggle Button` ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - e038490: (**BREAKING CHANGE**: Snippet을 다시 설치해야 합니다.) Manner Temp, Manner Temp Badge 컴포넌트를 업데이트합니다. - snippet 내 오타 수정 - 신규 10단계 반영 - 업데이트 가이드 1. `@seed-design/css@latest @seed-design/react@latest` 설치 2. `npx @seed-design/cli@latest add ui:manner-temp ui:manner-temp-badge`로 snippet 최신화 3. 온도 범위가 변경되었으므로, `` 혹은 ``과 같이 `level`을 직접 지정하여 사용하고 있는 경우가 있는지 확인 ## 0.2.0 ### Minor Changes - 8448880: 시맨틱 stroke 컬러 토큰을 업데이트합니다. **이름이 변경되는 stroke 토큰** - [Color Role 규칙](https://seed-design.io/docs/foundation/color/color-role)에 맞춰 일관적인 토큰 이름을 유지할 수 있도록 업데이트합니다. - 이름이 변경되는 stroke 토큰을 사용하고 있는 경우, 간단한 Find & Replace 마이그레이션이 필요합니다. | 기존 | 신규 | 비고 | | ------------------------------- | ------------------------------- | ---------------------------------- | | **$color.stroke.neutral-muted** | $color.stroke.neutral-subtle | 가장 먼저 마이그레이션해야 합니다. | | $color.stroke.on-image | $color.stroke.neutral-subtle | | $color.stroke.neutral | **$color.stroke.neutral-muted** | | $color.stroke.field-focused | $color.stroke.neutral-contrast | | $color.stroke.control | $color.stroke.neutral-weak | | $color.stroke.field | $color.stroke.neutral-weak | | $color.stroke.brand | $color.stroke.brand-weak | | $color.stroke.positive | $color.stroke.positive-weak | | $color.stroke.informative | $color.stroke.informative-weak | | $color.stroke.warning | $color.stroke.warning-weak | | $color.stroke.critical | $color.stroke.critical-weak | **색상이 변경되는 stroke 토큰 (마이그레이션 불필요)** `$color.stroke.neutral-contrast` (이름 변경 전 `$color.stroke.field-focused`) 모든 theme mode에서 `$color.palette.gray-800` → `$color.palette.gray-1000`로 변경되었습니다. **신규 stroke 토큰 (마이그레이션 불필요)** | 신규 | | ------------------------------- | | $color.stroke.neutral-solid | | $color.stroke.brand-solid | | $color.stroke.positive-solid | | $color.stroke.informative-solid | | $color.stroke.warning-solid | | $color.stroke.critical-solid | ## 0.1.15 ### Patch Changes - c51a261: font-size, line-height 토큰에 static variant를 추가합니다. - `--seed-font-size-t1-static` ~ `--seed-font-size-t10-static` - `--seed-line-height-t1-static` ~ `--seed-line-height-t10-static` - 3de4cec: 플랫폼별 조건부 폰트 스케일링 제한 (iOS: 135%, Android: 150%) 적용 - CSS 변수 `--seed-{font-size|line-height}-limit-{min|max}` 도입 - 빌드 타임 basePx 계산을 런타임 static 토큰 참조로 대체 - global.ts에 폰트 스케일링 변수 통합 ## 0.1.14 ### Patch Changes - f806356: Page Banner 컴포넌트를 추가합니다. Inline Banner 컴포넌트를 deprecate합니다. - Inline Banner 컴포넌트 대비 모든 `tone`에서 모든 `variant`를 지원하며, 내부 Button의 충분한 터치 영역을 보장합니다. ```tsx 새 탭에서 열기 } /> ``` 시맨틱 색상 토큰을 추가하고 수정합니다. - `$color.bg.positive-solid-pressed`: theme-dark에서 `$color.palette.green-500` → `$color.palette.green-600` - `$color.bg.warning-solid-pressed` 추가 - 1982494: Badge 컴포넌트를 업데이트합니다. - `tone=warning` variant를 추가합니다. - `maxWidth` 스펙을 추가합니다. 신규 시맨틱 색상 토큰을 추가합니다. - `$color.fg.warning` - `$color.stroke.warning` - `$color.fg.brand-contrast` - `$color.bg.brand-weak` - `$color.bg.brand-weak-pressed` ## 0.1.6 ### Patch Changes - 3c13ad7: `highlight-magic-pressed` 그라디언트 토큰을 추가합니다. ## 0.1.3 ### Patch Changes - 946faf7: 그라디언트 토큰 추가 및 변경 - `fade-layer-floating`, `fade-layer-default` 토큰이 추가되었습니다. - `$gradient.shimmer-magic` 토큰 stop color가 변경되었습니다. ## 0.1.2 ### Patch Changes - 0070c90: 문법 이상하던 부분을 수정해요 ## 0.1.1 ### Patch Changes - e3b782d: `stroke.neutral`, `stroke.neutral-muted`, `stroke.on-image`의 컬러를 alpha 값으로 변경합니다. ## 0.0.41 ### Patch Changes - b43de05: Gradient 컬러를 추가합니다 ## 0.0.39 ### Patch Changes - f801300: 새로운 black, white alpha 값을 추가합니다 `$color.palette.static-black-alpha-50` (예전 값) - 예전 값: #0000000d (투명도 약 5.1%) - 변경 값: `$color.palette.static-black-alpha-200` (투명도 4.7%) `$color.palette.static-black-alpha-200` (예전 값) - 예전 값: #00000033 (투명도 20%) - 변경 값: `$color.palette.static-black-alpha-500` (투명도 17.3%) `$color.palette.static-black-alpha-500` (예전 값) - 예전 값: #00000080 (투명도 약 50.2%) - 변경 값: `$color.palette.static-black-alpha-700` (투명도 45.5%) `$color.palette.static-white-alpha-200` (예전 값) - 예전 값: #ffffff33 (투명도 20%) - 변경 값: `$color.palette.static-white-alpha-300` (투명도 18%) `$color.palette.static-white-alpha-800` (예전 값) - 예전 값: #ffffffcc (투명도 약 80%) - 변경 값: `$color.palette.static-white-alpha-800` (투명도 87.1%) - (참고: 이 값은 이름은 같지만 실제 투명도 값은 80%에서 87.1%로 변경되었습니다.) ## 0.0.21 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. ## 0.0.12 ### Patch Changes - ac83753: tailwind plugin (v3), tailwind theme (v4)를 제공해요. --- ## @seed-design/vite-plugin ## 2.0.0 ### Patch Changes - Updated dependencies [4ad24cc] - Updated dependencies [555525a] - Updated dependencies [7a6847e] - Updated dependencies [029d052] - Updated dependencies [54a2ff8] - Updated dependencies [d1a8d7c] - Updated dependencies [69e3b97] - Updated dependencies [57e4f1a] - Updated dependencies [2abd3ed] - Updated dependencies [270b277] - Updated dependencies [9369ff9] - Updated dependencies [4839a64] - Updated dependencies [60d1a82] - Updated dependencies [ec33023] - Updated dependencies [98b52a7] - Updated dependencies [f982d61] - Updated dependencies [7f1dbe3] - Updated dependencies [c0fd999] - Updated dependencies [31bf164] - @seed-design/css@2.0.0 ## 1.1.1 ### Patch Changes - 251fd85: Vite 8과 함께 사용할 수 있도록 peerDependencies를 최신화합니다. ## 1.1.0 ### Patch Changes - Updated dependencies [d6bb84d] - Updated dependencies [a55f584] - Updated dependencies [191005f] - Updated dependencies [b131282] - Updated dependencies [6af6501] - Updated dependencies [33def2d] - @seed-design/css@1.1.0 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [39a96f1] - Updated dependencies [34f92f2] - Updated dependencies [e038490] - Updated dependencies [4153ca5] - Updated dependencies [a7d07f0] - @seed-design/css@1.0.0 ## 0.2.0 ### Patch Changes - Updated dependencies [8448880] - @seed-design/css@0.2.0 ## 0.1.3 ### Patch Changes - 71c58fd: iOS Font Scaling - iOS 기기에서 시스템 폰트 크기 설정에 따라 동적으로 폰트 크기와 줄 높이를 조정하는 폰트 스케일링 옵션이 추가되었습니다. - 플러그인(webpack, vite, rsbuild)에서 `fontScaling` 옵션을 통해 폰트 스케일링 기능을 활성화할 수 있습니다. - `data-seed-font-scaling='enabled'` 일 때, 폰트 크기를 조정합니다. - Updated dependencies [cdc0930] - Updated dependencies [946faf7] - Updated dependencies [71c58fd] - @seed-design/css@0.1.3 ## 0.1.2 ### Patch Changes - 7b2c0f3: Updated dependencies - @seed-design/react@0.1.1 - Updated dependencies [7b2c0f3] - @seed-design/css@0.1.2 ## 0.1.0 ### Patch Changes - Updated dependencies [7cc6087] - Updated dependencies [bdca898] - @seed-design/css@0.1.0 ## 0.0.41 ### Patch Changes - 7382c71: peer dependency에 vite 7을 추가합니다. - Updated dependencies [561f74c] - Updated dependencies [b43de05] - @seed-design/css@0.0.41 ## 0.0.39 ### Patch Changes - Updated dependencies [f801300] - @seed-design/css@0.0.39 ## 0.0.38 ### Patch Changes - Updated dependencies [70fbaaf] - @seed-design/css@0.0.38 ## 0.0.35 ### Patch Changes - Updated dependencies [0789dc8] - @seed-design/css@0.0.35 ## 0.0.34 ### Patch Changes - Updated dependencies [92801a2] - @seed-design/css@0.0.34 ## 0.0.33 ### Patch Changes - Updated dependencies [fbdb091] - @seed-design/css@0.0.33 ## 0.0.31 ### Patch Changes - Updated dependencies [fd7c569] - @seed-design/css@0.0.31 ## 0.0.30 ### Patch Changes - Updated dependencies [285cb9b] - @seed-design/css@0.0.30 ## 0.0.29 ### Patch Changes - Updated dependencies [116ee2c] - @seed-design/css@0.0.29 ## 0.0.28 ### Patch Changes - Updated dependencies [5337e14] - @seed-design/css@0.0.28 ## 0.0.27 ### Patch Changes - Updated dependencies [9d85c16] - Updated dependencies [d951317] - Updated dependencies [b3f964d] - @seed-design/css@0.0.27 ## 0.0.26 ### Patch Changes - c8b2921: types 필드와 exports 필드를 수정해요 ## 0.0.25 ### Patch Changes - Updated dependencies [c87ede9] - @seed-design/css@0.0.25 ## 0.0.24 ### Patch Changes - Updated dependencies [4da536f] - @seed-design/css@0.0.24 ## 0.0.23 ### Patch Changes - Updated dependencies [63e1541] - @seed-design/css@0.0.23 ## 0.0.21 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [5d69d1d] - Updated dependencies [4d34760] - Updated dependencies [7ae87f8] - Updated dependencies [f144d28] - Updated dependencies [e368c69] - @seed-design/css@0.0.21 ## 0.0.19 ### Patch Changes - Updated dependencies [3c9ec66] - Updated dependencies [b3bb6e7] - @seed-design/css@0.0.19 ## 0.0.17 ### Patch Changes - Updated dependencies [c042f90] - @seed-design/css@0.0.17 ## 0.0.15 ### Patch Changes - 1bb9f7b: - vite dev에서 컴포넌트 스타일시트가 로드되지 않는 버그를 수정합니다. - 플러그인이 컴포넌트 스타일시트를 로드하는 방식을 변경합니다. - Updated dependencies [1bb9f7b] - Updated dependencies [4511814] - Updated dependencies [f4b0723] - Updated dependencies [f4b0723] - @seed-design/css@0.0.15 ## 0.0.14 ### Patch Changes - Updated dependencies [92c0b80] - Updated dependencies [c1d94d0] - @seed-design/css@0.0.14 ## 0.0.13 ### Patch Changes - Updated dependencies [7fca755] - @seed-design/css@0.0.13 ## 0.0.12 ### Patch Changes - Updated dependencies [6426379] - Updated dependencies [ee41f37] - @seed-design/css@0.0.12 ## 0.0.11 ### Patch Changes - Updated dependencies [e70f340] - Updated dependencies [72f344f] - @seed-design/css@0.0.11 ## 0.0.10 ### Patch Changes - Updated dependencies [e4b704c] - @seed-design/css@0.0.10 ## 0.0.9 ### Patch Changes - Updated dependencies [63f8651] - Updated dependencies [d9b01a9] - @seed-design/css@0.0.9 ## 0.0.8 ### Patch Changes - Updated dependencies [1424700] - Updated dependencies [0efeea1] - @seed-design/css@0.0.8 ## 0.0.7 ### Patch Changes - Updated dependencies [8aca3de] - @seed-design/css@0.0.7 ## 0.0.6 ### Patch Changes - Updated dependencies [bf198e8] - Updated dependencies [3d66c5b] - Updated dependencies [a8d5242] - Updated dependencies [ccf3989] - @seed-design/css@0.0.6 ## 0.0.5 ### Patch Changes - Updated dependencies [e3234e7] - Updated dependencies [5502bed] - @seed-design/css@0.0.5 ## 0.0.4 ### Patch Changes - Updated dependencies [6df5d19] - Updated dependencies [5cb50e7] - @seed-design/css@0.0.4 ## 0.0.3 ### Patch Changes - 89c48af: change plugin named export to seedDesignPlugin - b180822: Inject data-seed in theming script instead of plugin - b20de60: Move the injection location of the theming script from pre-body to pre-head. - Updated dependencies [a33af94] - Updated dependencies [b180822] - @seed-design/css@0.0.3 ## 0.0.2 ### Patch Changes - Updated dependencies [d04e344] - @seed-design/css@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/css@0.0.1 --- ## @seed-design/webpack-plugin ## 2.0.0 ### Patch Changes - Updated dependencies [4ad24cc] - Updated dependencies [555525a] - Updated dependencies [7a6847e] - Updated dependencies [029d052] - Updated dependencies [54a2ff8] - Updated dependencies [d1a8d7c] - Updated dependencies [69e3b97] - Updated dependencies [57e4f1a] - Updated dependencies [2abd3ed] - Updated dependencies [270b277] - Updated dependencies [9369ff9] - Updated dependencies [4839a64] - Updated dependencies [60d1a82] - Updated dependencies [ec33023] - Updated dependencies [98b52a7] - Updated dependencies [f982d61] - Updated dependencies [7f1dbe3] - Updated dependencies [c0fd999] - Updated dependencies [31bf164] - @seed-design/css@2.0.0 ## 1.1.0 ### Patch Changes - Updated dependencies [d6bb84d] - Updated dependencies [a55f584] - Updated dependencies [191005f] - Updated dependencies [b131282] - Updated dependencies [6af6501] - Updated dependencies [33def2d] - @seed-design/css@1.1.0 ## 1.0.0 ### Major Changes - 34f92f2: 🌱 SEED Design 패키지의 첫 메이저 버전을 출시합니다. ### Patch Changes - Updated dependencies [39a96f1] - Updated dependencies [34f92f2] - Updated dependencies [e038490] - Updated dependencies [4153ca5] - Updated dependencies [a7d07f0] - @seed-design/css@1.0.0 ## 0.2.0 ### Patch Changes - Updated dependencies [8448880] - @seed-design/css@0.2.0 ## 0.1.3 ### Patch Changes - 71c58fd: iOS Font Scaling - iOS 기기에서 시스템 폰트 크기 설정에 따라 동적으로 폰트 크기와 줄 높이를 조정하는 폰트 스케일링 옵션이 추가되었습니다. - 플러그인(webpack, vite, rsbuild)에서 `fontScaling` 옵션을 통해 폰트 스케일링 기능을 활성화할 수 있습니다. - `data-seed-font-scaling='enabled'` 일 때, 폰트 크기를 조정합니다. - Updated dependencies [cdc0930] - Updated dependencies [946faf7] - Updated dependencies [71c58fd] - @seed-design/css@0.1.3 ## 0.1.2 ### Patch Changes - 7b2c0f3: Updated dependencies - @seed-design/react@0.1.1 - Updated dependencies [7b2c0f3] - @seed-design/css@0.1.2 ## 0.1.0 ### Patch Changes - Updated dependencies [7cc6087] - Updated dependencies [bdca898] - @seed-design/css@0.1.0 ## 0.0.41 ### Patch Changes - Updated dependencies [561f74c] - Updated dependencies [b43de05] - @seed-design/css@0.0.41 ## 0.0.39 ### Patch Changes - Updated dependencies [f801300] - @seed-design/css@0.0.39 ## 0.0.38 ### Patch Changes - Updated dependencies [70fbaaf] - @seed-design/css@0.0.38 ## 0.0.35 ### Patch Changes - Updated dependencies [0789dc8] - @seed-design/css@0.0.35 ## 0.0.34 ### Patch Changes - Updated dependencies [92801a2] - @seed-design/css@0.0.34 ## 0.0.33 ### Patch Changes - Updated dependencies [fbdb091] - @seed-design/css@0.0.33 ## 0.0.31 ### Patch Changes - Updated dependencies [fd7c569] - @seed-design/css@0.0.31 ## 0.0.30 ### Patch Changes - Updated dependencies [285cb9b] - @seed-design/css@0.0.30 ## 0.0.29 ### Patch Changes - Updated dependencies [116ee2c] - @seed-design/css@0.0.29 ## 0.0.28 ### Patch Changes - Updated dependencies [5337e14] - @seed-design/css@0.0.28 ## 0.0.27 ### Patch Changes - Updated dependencies [9d85c16] - Updated dependencies [d951317] - Updated dependencies [b3f964d] - @seed-design/css@0.0.27 ## 0.0.26 ### Patch Changes - c8b2921: types 필드와 exports 필드를 수정해요 ## 0.0.25 ### Patch Changes - Updated dependencies [c87ede9] - @seed-design/css@0.0.25 ## 0.0.24 ### Patch Changes - Updated dependencies [4da536f] - @seed-design/css@0.0.24 ## 0.0.23 ### Patch Changes - Updated dependencies [63e1541] - @seed-design/css@0.0.23 ## 0.0.21 ### Patch Changes - e368c69: 패키지 의존성을 최신화합니다. - Updated dependencies [5d69d1d] - Updated dependencies [4d34760] - Updated dependencies [7ae87f8] - Updated dependencies [f144d28] - Updated dependencies [e368c69] - @seed-design/css@0.0.21 ## 0.0.19 ### Patch Changes - Updated dependencies [3c9ec66] - Updated dependencies [b3bb6e7] - @seed-design/css@0.0.19 ## 0.0.17 ### Patch Changes - Updated dependencies [c042f90] - @seed-design/css@0.0.17 ## 0.0.15 ### Patch Changes - 1bb9f7b: - vite dev에서 컴포넌트 스타일시트가 로드되지 않는 버그를 수정합니다. - 플러그인이 컴포넌트 스타일시트를 로드하는 방식을 변경합니다. - Updated dependencies [1bb9f7b] - Updated dependencies [4511814] - Updated dependencies [f4b0723] - Updated dependencies [f4b0723] - @seed-design/css@0.0.15 ## 0.0.14 ### Patch Changes - Updated dependencies [92c0b80] - Updated dependencies [c1d94d0] - @seed-design/css@0.0.14 ## 0.0.13 ### Patch Changes - Updated dependencies [7fca755] - @seed-design/css@0.0.13 ## 0.0.12 ### Patch Changes - Updated dependencies [6426379] - Updated dependencies [ee41f37] - @seed-design/css@0.0.12 ## 0.0.11 ### Patch Changes - Updated dependencies [e70f340] - Updated dependencies [72f344f] - @seed-design/css@0.0.11 ## 0.0.10 ### Patch Changes - Updated dependencies [e4b704c] - @seed-design/css@0.0.10 ## 0.0.9 ### Patch Changes - Updated dependencies [63f8651] - Updated dependencies [d9b01a9] - @seed-design/css@0.0.9 ## 0.0.8 ### Patch Changes - Updated dependencies [1424700] - Updated dependencies [0efeea1] - @seed-design/css@0.0.8 ## 0.0.7 ### Patch Changes - Updated dependencies [8aca3de] - @seed-design/css@0.0.7 ## 0.0.6 ### Patch Changes - Updated dependencies [bf198e8] - Updated dependencies [3d66c5b] - Updated dependencies [a8d5242] - Updated dependencies [ccf3989] - @seed-design/css@0.0.6 ## 0.0.5 ### Patch Changes - Updated dependencies [e3234e7] - Updated dependencies [5502bed] - @seed-design/css@0.0.5 ## 0.0.4 ### Patch Changes - Updated dependencies [6df5d19] - Updated dependencies [5cb50e7] - @seed-design/css@0.0.4 ## 0.0.3 ### Patch Changes - bad86f9: loosen up peer dependencies - b180822: Inject data-seed in theming script instead of plugin - b20de60: Move the injection location of the theming script from pre-body to pre-head. - Updated dependencies [a33af94] - Updated dependencies [b180822] - @seed-design/css@0.0.3 ## 0.0.2 ### Patch Changes - Updated dependencies [d04e344] - @seed-design/css@0.0.2 ## 0.0.1 ### Patch Changes - b64023c: Initial release of the next version of Seed Design. - Updated dependencies [b64023c] - @seed-design/css@0.0.1 --- file: updates/upgrade/v1.mdx # SEED React 1 and older SEED React 2 이전 버전 간 업그레이드에 필요한 작업을 버전 구간별로 안내합니다. SEED React 2 이전 버전 간 업그레이드 방법을 안내합니다. SEED React 1.2에서 2로 업그레이드하려면 [SEED React 2 업그레이드](/react/updates/upgrade/v2) 문서를 참고할 수 있으며, 신규 기능 등 전체 변경사항은 [Changelog](/react/updates/changelog)에서 확인할 수 있습니다. SEED React 1은 마이너 버전에 breaking change를 포함합니다. *** ## 0.2.0 Stroke 시맨틱 토큰 이름 개편 \[#020-stroke-시맨틱-토큰-이름-개편] \[직접 판단 및 코드 수정 필요] 0.2 이전 버전에서 업그레이드하는 경우 아래 항목을 참고하여 사용하고 있는 stroke 색상 시맨틱 토큰을 사용하는 코드를 업데이트합니다. codemod로 일괄 변경하거나 표를 참고하여 직접 치환할 수 있습니다. - npm: npx @seed-design/codemod@latest replace-semantic-stroke-color \ - pnpm: pnpm dlx @seed-design/codemod@latest replace-semantic-stroke-color \ - yarn: yarn dlx @seed-design/codemod@latest replace-semantic-stroke-color \ - bun: bun x @seed-design/codemod@latest replace-semantic-stroke-color \ | 0.2.0 이전 토큰 | 0.2.0 이후 토큰 | 비고 | | ----------------------------- | -------------------------------- | ----------------------------- | | `$color.stroke.neutral-muted` | `$color.stroke.neutral-subtle` | 순서상 이 항목을 가장 먼저 마이그레이션해야 합니다. | | `$color.stroke.on-image` | `$color.stroke.neutral-subtle` | | | `$color.stroke.neutral` | `$color.stroke.neutral-muted` | | | `$color.stroke.field-focused` | `$color.stroke.neutral-contrast` | | | `$color.stroke.control` | `$color.stroke.neutral-weak` | | | `$color.stroke.field` | `$color.stroke.neutral-weak` | | | `$color.stroke.brand` | `$color.stroke.brand-weak` | | | `$color.stroke.positive` | `$color.stroke.positive-weak` | | | `$color.stroke.informative` | `$color.stroke.informative-weak` | | | `$color.stroke.warning` | `$color.stroke.warning-weak` | | | `$color.stroke.critical` | `$color.stroke.critical-weak` | | 자세한 내용은 [Available Transforms](/react/developer-tools/codemods/available-transforms)를 참고해주세요. *** ## 1.0으로 업그레이드하기 \[#10으로-업그레이드하기] 1.0 버전 기준의 SEED React 문서입니다. 아래 컴포넌트의 snippet(권장 조합) 변경사항이 있으며, 이외 필요한 코드 변경은 없습니다. \[snippet 업데이트 필요] - npm: npx @seed-design/cli@latest add-all --seed-react-version 1.0 --on-diff backup ui - pnpm: pnpm dlx @seed-design/cli@latest add-all --seed-react-version 1.0 --on-diff backup ui - yarn: yarn dlx @seed-design/cli@latest add-all --seed-react-version 1.0 --on-diff backup ui - bun: bun x @seed-design/cli@latest add-all --seed-react-version 1.0 --on-diff backup ui * **Snackbar**: 레이아웃 변경 - `add ui:snackbar` * **MannerTemp / MannerTempBadge**: 신규 10단계 온도 범위를 반영합니다. - `add ui:manner-temp ui:manner-temp-badge` - `level`을 직접 지정하여 사용하는 코드가 있다면 온도별 범위가 의도에 맞게 표시되는지 확인이 필요합니다. * **SwitchMark / List Header** 추가 - `add ui:switch`, `add ui:list` Snackbar/MannerTemp/MannerTempBadge를 `@seed-design/react`에서 직접 import해서 사용하는 코드가 있다면 신규 snippet 구성에 맞게 수정합니다. *** ## 1.0에서 1.1로 업그레이드하기 \[#10에서-11로-업그레이드하기] 1.1 버전 기준의 SEED React 문서입니다. ### 1. Page Banner 구성 변경 \[#1-page-banner-구성-변경] \[snippet 업데이트 필요] `PageBanner.TextContent`가 `PageBanner.Content`로 이름이 변경되었으며, Box로 스타일링하던 레이아웃을 `PageBanner.Body`로 교체합니다. snippet 변경사항이 있어 다시 추가가 필요합니다. snippet 컴포넌트를 사용하는 코드의 변경은 불필요합니다. `@seed-design/react`에서 `PageBanner`를 직접 import해서 사용하는 코드가 있다면 신규 snippet 구성에 맞게 수정합니다. - npm: npx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:page-banner - pnpm: pnpm dlx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:page-banner - yarn: yarn dlx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:page-banner - bun: bun x @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:page-banner ### 2. Text Field `size="xlarge"` 제거 및 Field 기반 snippet 제공 \[#2-text-field-sizexlarge-제거-및-field-기반-snippet-제공] \[snippet 업데이트 및 코드 수정 필요] Text Field snippet 컴포넌트가 내부적으로 Field 컴포넌트를 사용하도록 정리합니다. `onValueChange` 시그니처를 `((value: string) => void) | undefined`에서 `((values: { value: string; graphemes: string[]; slicedValue: string; slicedGraphemes: string[]; }) => void) | undefined`로 변경합니다. snippet 변경사항이 있어 다시 추가가 필요합니다. `@seed-design/react`에서 `TextField`를 직접 import해서 사용하는 코드가 있다면 신규 snippet 구성에 맞게 수정합니다. - npm: npx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:text-field - pnpm: pnpm dlx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:text-field - yarn: yarn dlx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:text-field - bun: bun x @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:text-field `size="xlarge"`가 제거되었습니다. `size="large"`(기본값)를 사용해주세요. ### 3. Bottom Sheet를 Drawer 기반으로 전환 \[#3-bottom-sheet를-drawer-기반으로-전환] \[snippet 업데이트 및 코드 수정 필요] Dialog 기반의 Bottom Sheet를 Drawer 기반으로 전환하여 swipe to dismiss 및 snap point 등의 기능을 추가합니다. snippet 변경사항이 있어 다시 추가가 필요합니다. `@seed-design/react`에서 `BottomSheet`를 직접 import해서 사용하는 코드가 있다면 신규 snippet 구성에 맞게 수정합니다. - npm: npx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:bottom-sheet - pnpm: pnpm dlx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:bottom-sheet - yarn: yarn dlx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:bottom-sheet - bun: bun x @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:bottom-sheet snippet 컴포넌트 `BottomSheetContent`의 `hideCloseButton`(기본값: `false`)은 `showCloseButton`(기본값: `true`)으로 인터페이스가 변경되었습니다. ```tsx // [!code --] // [!code ++] ``` ### 4. Error State snippet 변경 \[#4-error-state-snippet-변경] \[snippet 업데이트 및 코드 수정 필요] Error State snippet이 내부적으로 Action Button(`variant="ghost"`)을 사용하도록 변경되어 다시 추가가 필요합니다. - npm: npx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:error-state - pnpm: pnpm dlx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:error-state - yarn: yarn dlx @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:error-state - bun: bun x @seed-design/cli@latest add --seed-react-version 1.1 --on-diff backup ui:error-state snippet 컴포넌트 `ErrorState`의 `secondaryActionProps` 타입이 `React.ButtonHTMLAttributes`에서 `ActionButtonProps`로 변경되었습니다. `secondaryActionProps`를 사용하는 코드가 있다면 신규 타입에 맞게 수정합니다. *** ## 1.1에서 1.2로 업그레이드하기 \[#11에서-12로-업그레이드하기] 1.2 버전 기준의 SEED React 문서입니다. 아래 6개 h3 항목들은 서로 독립적이므로 병렬로 진행할 수 있습니다. ### 1. Help Bubble 레이아웃 변경 \[#1-help-bubble-레이아웃-변경] \[snippet 업데이트 필요] 신규 `HelpBubble.Body`가 `HelpBubble.Title`과 `HelpBubble.Description`을 감싸도록 snippet이 업데이트되었습니다. `HelpBubbleTrigger`, `HelpBubbleAnchor` 등 snippet에서 export되는 컴포넌트를 사용하는 코드는 snippet을 다시 받는 것 외에 변경이 불필요합니다. `@seed-design/react`에서 `HelpBubble`을 직접 import해서 사용하는 코드가 있다면 신규 snippet 구성에 맞게 수정합니다. - npm: npx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:help-bubble - pnpm: pnpm dlx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:help-bubble - yarn: yarn dlx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:help-bubble - bun: bun x @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:help-bubble ### 2. MenuSheetItem 레이아웃 변경 \[#2-menusheetitem-레이아웃-변경] \[snippet 업데이트 및 코드 수정 필요] `MenuSheetItem` snippet 컴포넌트에 `description`을 추가할 수 있도록 개선하며 레이아웃이 변경되었습니다. snippet 변경사항이 있어 다시 추가가 필요합니다. `@seed-design/react`에서 `MenuSheetItem`을 직접 import해서 사용하는 코드가 있다면 신규 snippet 구성에 맞게 수정합니다. - npm: npx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:menu-sheet - pnpm: pnpm dlx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:menu-sheet - yarn: yarn dlx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:menu-sheet - bun: bun x @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:menu-sheet snippet 컴포넌트 `MenuSheetItem`을 사용하는 코드에서 `children` 대신 `label`, `` 대신 `prefixIcon` prop을 사용해주세요. ```diff - - } /> - 메뉴 항목 -; +} label="메뉴 항목" />; ``` ### 3. Bottom Sheet 및 Menu Sheet가 기본적으로 safe area를 갖도록 수정 \[#3-bottom-sheet-및-menu-sheet가-기본적으로-safe-area를-갖도록-수정] \[직접 판단 필요] `BottomSheet.Content`와 `MenuSheet.Content`가 기본적으로 하단 safe area만큼 padding을 갖도록 수정합니다. 시트에 직접 safe area padding을 넣고 있었던 경우 제거합니다. ```tsx {/* [!code --] */} ... {/* [!code ++] */} ... ``` ### 4. Tag Group 레이아웃 변경 \[#4-tag-group-레이아웃-변경] \[snippet 업데이트 및 코드 수정 필요] Tag Group Item의 레이아웃을 위해 snippet 컴포넌트를 신규로 제공합니다. - npm: npx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:tag-group - pnpm: pnpm dlx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:tag-group - yarn: yarn dlx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:tag-group - bun: bun x @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:tag-group `@seed-design/react`에서 import하는 Tag Group을 snippet(`seed-design/ui/tag-group`)에서 import하도록 교체하고, `children` 대신 `label`, `PrefixIcon`/`SuffixIcon` 대신 `prefixIcon`/`suffixIcon` prop을 사용해주세요. ```tsx import { TagGroupRoot, TagGroupItem } from "@seed-design/react"; // [!code --] import { TagGroupRoot, TagGroupItem } from "seed-design/ui/tag-group"; // [!code ++] {/* [!code --] */} {/* [!code --] */} } /> {/* [!code --] */} 서초4동 {/* [!code --] */} {/* [!code ++] */} } /> ; ``` 아이콘에만 다른 색상을 지정하는 등 커스터마이징이 필요한 경우 `@seed-design/react`의 `TagGroup.Item`과 `TagGroup.ItemLabel`을 병행해서 사용할 수 있습니다. ```tsx import { TagGroupRoot } from "seed-design/ui/tag-group"; import { TagGroup as SeedTagGroup } from "@seed-design/react"; {/* ... */} } color="fg.brand" /> 광고 ; ``` ### 5. Select Box 레이아웃 변경 \[#5-select-box-레이아웃-변경] \[snippet 업데이트 및 코드 수정 필요] snippet 변경사항이 있어 다시 추가가 필요합니다. `@seed-design/react`에서 Select Box 관련 컴포넌트를 직접 import해서 사용하는 코드가 있다면 신규 snippet 구성에 맞게 수정합니다. - npm: npx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:select-box - pnpm: pnpm dlx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:select-box - yarn: yarn dlx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:select-box - bun: bun x @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:select-box * `CheckSelectBoxGroup` / `RadioSelectBoxRoot` 컴포넌트를 기본적으로 gap이 포함된 그리드 레이아웃으로 변경합니다. - 자식을 묶던 `VStack`을 제거하여 `CheckSelectBoxGroup` 및 `RadioSelectBoxRoot`가 직접 아이템들을 감싸도록 변경합니다. - `VStack`에 `gap` 외 스타일이 있다면 `VStack`을 `CheckSelectBoxGroup` / `RadioSelectBoxRoot` 바깥에 남겨두어 레이아웃을 유지할 수 있습니다. - `...`처럼 `asChild`로 `VStack`을 래핑하고 있을 수도 있으니 함께 확인합니다. * Select Box 내부 Checkmark 또는 Radiomark가 자동으로 표시되지 않도록 변경됩니다. - `CheckSelectBox` / `RadioSelectBoxItem`에 `suffix={}` / `suffix={}`를 추가합니다. ```tsx import { VStack } from "@seed-design/react"; // [!code --] import { CheckSelectBoxGroup, CheckSelectBox } from "seed-design/ui/select-box"; // [!code --] // [!code ++] import { // [!code ++] CheckSelectBoxGroup, // [!code ++] CheckSelectBox, // [!code ++] CheckSelectBoxCheckmark, // [!code ++] } from "seed-design/ui/select-box"; // [!code ++] {/* [!code --] */} {/* [!code --] */} {/* [!code --] */} {/* [!code --] */} {/* [!code ++] */} } /> // {/* [!code ++] */} } /> // ; ``` ```tsx import { VStack } from "@seed-design/react"; // [!code --] // [!code --] import { RadioSelectBoxRoot, // [!code --] RadioSelectBoxItem, // [!code --] } from "seed-design/ui/select-box"; // [!code --] // [!code ++] import { RadioSelectBoxRoot, // [!code ++] RadioSelectBoxItem, // [!code ++] RadioSelectBoxRadiomark, // [!code ++] } from "seed-design/ui/select-box"; // [!code ++] {/* [!code --] */} {/* [!code --] */} {/* [!code --] */} {/* [!code --] */} {/* [!code ++] */} } // [!code ++] /> {/* [!code ++] */} } // [!code ++] /> ; ``` ### 6. Radio Group / Item 관련 변경사항 \[#6-radio-group--item-관련-변경사항] 아래 3개 항목은 서로 영향을 줄 수 있으므로 순서대로 진행합니다. #### Radio Item `invalid` prop 제거 \[#radio-item-invalid-prop-제거] \[에러로 발견 가능] `RadioGroupItem`, `RadioChipItem`, `RadioSelectBoxItem`, `ListRadioItem`에서 `invalid` prop을 제거합니다. `data-invalid` 기반으로 스타일링하는 코드가 있다면 확인이 필요합니다. #### `SwitchMark` 및 `RadioMark`의 컴포넌트 이름 변경 \[#switchmark-및-radiomark의-컴포넌트-이름-변경] \[snippet 업데이트 권장] snippet에서 export되는 이름이 변경되었습니다. snippet을 다시 받아 업데이트할 수 있습니다. - npm: npx @seed-design/cli@latest add --on-diff backup ui:switch ui:radio-group - pnpm: pnpm dlx @seed-design/cli@latest add --on-diff backup ui:switch ui:radio-group - yarn: yarn dlx @seed-design/cli@latest add --on-diff backup ui:switch ui:radio-group - bun: bun x @seed-design/cli@latest add --on-diff backup ui:switch ui:radio-group ```tsx import { SwitchMark } from "seed-design/ui/switch"; // [!code --] import { Switchmark } from "seed-design/ui/switch"; // [!code ++] import { RadioMark } from "seed-design/ui/radio-group"; // [!code --] import { Radiomark } from "seed-design/ui/radio-group"; // [!code ++] ``` #### RadioGroup 레이아웃 정리 \[#radiogroup-레이아웃-정리] \[직접 판단 및 코드 수정 필요] RadioGroup 관련 snippet 컴포넌트의 레이아웃이 변경되어 다시 추가가 필요합니다. `@seed-design/react`에서 Radio Group 관련 컴포넌트를 직접 import해서 사용하는 코드가 있다면 신규 snippet 구성에 맞게 수정합니다. - npm: npx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:radio-group - pnpm: pnpm dlx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:radio-group - yarn: yarn dlx @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:radio-group - bun: bun x @seed-design/cli@latest add --seed-react-version 1.2 --on-diff backup ui:radio-group * snippet 컴포넌트 `RadioGroup`이 자체적으로 gap과 100% width를 갖도록 수정합니다. - 자식을 묶던 `VStack`을 제거해주세요. - `VStack`에 `gap` 외 스타일이 있다면 `VStack`을 `RadioGroup` 바깥에 남겨두어 레이아웃을 유지할 수 있습니다. - `...`처럼 `asChild`로 `VStack`을 래핑하고 있을 수도 있으니 함께 확인합니다. * `@seed-design/react` 컴포넌트 `RadioGroup.Root`가 radio 관련 동작을 다루지 않도록 수정하고, 자체 스타일이 추가되었습니다. - radio 관련 헤드리스 동작이 필요한 경우 `@seed-design/react/primitive`의 `RadioGroup.Root`를 사용합니다. ```tsx import { RadioGroup } from "@seed-design/react"; // [!code --] import { RadioGroup } from "@seed-design/react/primitive"; // [!code ++] ``` 1.2 릴리즈에서 "1.3에서 제거 예정"으로 안내했던 토큰 및 옵션(`$gradient.fade-layer-*`, Chip Tabs `brandSolid`, Checkbox `weight`, Switch `size`, 레이아웃 camelCase 값)은 SEED React 2에서 실제로 제거되었습니다. `$color.bg.layer-fill`은 2.0.0에서 제거되었다가 2.1.0에서 다시 추가되어 그대로 사용할 수 있습니다. 자세한 내용은 [v2 업그레이드](/react/updates/upgrade/v2) 가이드를 참고해주세요. SEED React 1.3은 릴리즈되지 않았습니다. --- file: updates/upgrade/v2.mdx # SEED React 2 SEED React 2로 업그레이드할 때 필요한 작업을 안내합니다. SEED React 2로 업그레이드하는 방법을 안내합니다. 신규 기능 등 전체 변경사항은 [Changelog](/react/updates/changelog)에서 확인할 수 있습니다. 이 가이드는 **`1.2.x`에서 `2`로 업그레이드**하는 경우를 기준으로 작성되었습니다. 1.2보다 낮은 버전을 사용 중이라면 [v1 이하 업그레이드](/react/updates/upgrade/v1) 가이드를 따른 뒤 이 문서로 돌아와 주세요. ## 2.0.0부터 시맨틱 버저닝을 준수합니다 \[#200부터-시맨틱-버저닝을-준수합니다] 1.x에서는 마이너 버전에 breaking change가 포함되었으나, 2.0.0부터는 시맨틱 버저닝을 준수하여 각 패키지가 메이저 버전에서만 breaking change를 갖습니다. 시맨틱 버저닝 준수의 시작점을 나타내는 의미로 메이저를 2로 업데이트합니다. 모든 패키지가 항상 같은 메이저로 움직이는 것은 아닙니다. CSS 변경 없이 React 패키지에만 breaking change가 있는 경우처럼, 각 패키지의 버전은 독립적으로 올라갑니다. 문서와 가이드는 React 패키지의 버전을 기준으로 소통합니다. (예: "SEED React 2") *** ## 패키지 업그레이드 \[#패키지-업그레이드] 프로젝트에서 사용하는 `@seed-design/*` 패키지를 아래 버전 이상으로 업그레이드합니다. | 패키지 | 최소 버전 | 비고 | | ------------------------------- | ------- | --------------------------------------------------------------------------- | | `@seed-design/css` | `2.1.0` | | | `@seed-design/react` | `2.0.0` | | | `@seed-design/stackflow` | `2.0.0` | | | `@seed-design/tailwind4-theme` | `2.1.0` | | | `@seed-design/tailwind3-plugin` | `2.1.0` | | | `@seed-design/vite-plugin` | `2.0.0` | 변경사항이 없으며 `1.1.1`과 동일합니다. `@seed-design/css` peer 메이저 상승에 맞춰 버전이 업데이트되었습니다. | | `@seed-design/webpack-plugin` | `2.0.0` | 변경사항이 없으며 `1.1.0`과 동일합니다. `@seed-design/css` peer 메이저 상승에 맞춰 버전이 업데이트되었습니다. | | `@seed-design/rsbuild-plugin` | `2.0.0` | 변경사항이 없으며 `1.2.1`과 동일합니다. `@seed-design/css` peer 메이저 상승에 맞춰 버전이 업데이트되었습니다. | 현재 프로젝트에서 사용하는 패키지만 업그레이드하면 됩니다. 사용하지 않는 패키지를 새로 설치할 필요는 없습니다. - npm: npm install @seed-design/react@latest @seed-design/css@latest @seed-design/stackflow@latest - pnpm: pnpm add @seed-design/react@latest @seed-design/css@latest @seed-design/stackflow@latest - yarn: yarn add @seed-design/react@latest @seed-design/css@latest @seed-design/stackflow@latest - bun: bun add @seed-design/react@latest @seed-design/css@latest @seed-design/stackflow@latest ## 코드 변경 \[#코드-변경] ### 디자인 변경사항이 있는 항목 \[#디자인-변경사항이-있는-항목] 화면에 보이는 결과가 달라질 수 있어 디자인 결정이 필요할 수 있는 항목입니다. #### 1. 그라디언트 토큰 `$gradient.fade-layer-*` 제거 \[#1-그라디언트-토큰-gradientfade-layer--제거] \[직접 판단 필요] `$gradient.fade-layer-floating`, `$gradient.fade-layer-default`가 제거되었습니다. 스크롤 영역에 자연스러운 페이드를 주고자 하는 경우 [Scroll Fog](/react/components/scroll-fog) 컴포넌트를 대신 사용해주세요. Tailwind 유틸리티 클래스나 CSS Variable을 하드코딩하여 사용하고 있는 경우 이 변경은 에러로 잡히지 않습니다. 사용처를 직접 검색해주세요. #### 2. Chip Tabs `variant="brandSolid"` 제거 \[#2-chip-tabs-variantbrandsolid-제거] \[에러로 발견 가능] `variant="neutralSolid"`를 사용해주세요. `seed-design/ui/chip-tabs.tsx` snippet의 deprecate 경고만을 위해 존재하던 분기에서 아래와 같은 타입 에러가 발생할 수 있습니다. ```text This comparison appears to be unintentional because the types '"neutralSolid" | "neutralOutline" | undefined' and '"brandSolid"' have no overlap. ts(2367) ``` snippet을 다시 받거나 해당 분기를 제거해주세요. - npm: npx @seed-design/cli@latest add --on-diff backup ui:chip-tabs - pnpm: pnpm dlx @seed-design/cli@latest add --on-diff backup ui:chip-tabs - yarn: yarn dlx @seed-design/cli@latest add --on-diff backup ui:chip-tabs - bun: bun x @seed-design/cli@latest add --on-diff backup ui:chip-tabs #### 3. App Bar `divider` 옵션 제거 \[#3-app-bar-divider-옵션-제거] \[에러로 발견 가능] App Bar 하단 구분선은 더 이상 표시되지 않습니다. 구분선이나 그림자 없이도 영역 구분이 충분하다는 디자인 판단에 따라 제거되었습니다. #### 참고: `$color.bg.layer-fill` 색상 토큰 \[#참고-colorbglayer-fill-색상-토큰] 이 토큰은 SEED React 2에서 제거될 예정이었으나 대체 토큰의 부재로 3에서 제거될 예정입니다. `@seed-design/css`, `tailwind3-plugin`, `tailwind4-theme` `2.0.0`에서는 실제로 제거되었으나 `2.1.0`에서 다시 추가되었습니다. ### 코드 수정이 필요한 항목 \[#코드-수정이-필요한-항목] #### 1. Snackbar 내부 버튼이 항상 스낵바를 닫도록 변경 \[#1-snackbar-내부-버튼이-항상-스낵바를-닫도록-변경] \[snippet 업데이트 권장] Snackbar 내부 버튼을 누르면 항상 스낵바가 닫히도록 변경합니다. 이 동작은 `@seed-design/react`의 `SeedSnackbar.ActionButton`에 기본으로 내장됩니다. `seed-design/ui/snackbar.tsx` snippet의 `shouldCloseOnAction` 관련 코드는 이제 중복입니다. 동작에는 문제가 없지만, snippet을 다시 받아 중복 로직을 제거하는 것을 권장합니다. 기존 `shouldCloseOnAction`의 기본값도 `true`였습니다. - npm: npx @seed-design/cli@latest add --on-diff backup ui:snackbar - pnpm: pnpm dlx @seed-design/cli@latest add --on-diff backup ui:snackbar - yarn: yarn dlx @seed-design/cli@latest add --on-diff backup ui:snackbar - bun: bun x @seed-design/cli@latest add --on-diff backup ui:snackbar #### 2. 레이아웃 컴포넌트 prop이 discriminated union으로 전환 \[#2-레이아웃-컴포넌트-prop이-discriminated-union으로-전환] \[에러로 발견 가능] 레이아웃 컴포넌트에 margin 지원을 추가하면서, `margin`과 `bleed`를 동시에 사용할 수 없도록 타입을 discriminated union으로 제한했습니다. 이로 인해 `BoxProps`, `FlexProps`, `VStackProps` 등을 그대로 `extends`하던 `interface`는 `type`으로 전환해야 할 수 있습니다. ```tsx interface MyBoxProps extends BoxProps {} // [!code --] type MyBoxProps = BoxProps & {}; // [!code ++] ``` #### 3. Bottom Sheet 옵션 정리 \[#3-bottom-sheet-옵션-정리] \[에러로 발견 가능] - `BottomSheetRoot`의 `noBodyStyles`, `preventScrollRestoration` 제거: 새 스크롤 락 구현이 각 옵션의 기본값(`noBodyStyles: true`, `preventScrollRestoration: false`)처럼 동작합니다. - `BottomSheetRoot`의 `direction` 제거: Bottom Sheet는 항상 아래에서만 열립니다. - `BottomSheetBackdrop`의 `forceMount` 제거 - `DrawerRoot`의 `lazyMount` / `unmountOnExit`로 대체할 수 있습니다. - `BottomSheetContent`의 `onPointerDownOutside`, `onFocusOutside`, `onInteractOutside`, `onEscapeKeyDown`, `onOpenAutoFocus`, `onCloseAutoFocus`, `forceMount` 제거 - `DrawerRoot`의 `onOpenChange` 두 번째 인자 `details`로 대체할 수 있습니다. #### 4. Select Box Checkmark 클래스명 및 import 경로 \[#4-select-box-checkmark-클래스명-및-import-경로] `selectBoxCheckmark` 클래스명 import 경로를 kebab-case로 변경합니다. **커스텀 스타일시트에서 selector를 사용 중인 경우** class를 kebab-case로 변경합니다. \[직접 판단 필요] ```css /* [!code --] */ .seed-selectBoxCheckmark__root { /* [!code --] */ /* ... */ /* [!code --] */ } /* [!code --] */ .seed-selectBoxCheckmark__icon { /* [!code --] */ /* ... */ /* [!code --] */ } /* [!code ++] */ .seed-select-box-checkmark__root { /* [!code ++] */ /* ... */ /* [!code ++] */ } /* [!code ++] */ .seed-select-box-checkmark__icon { /* [!code ++] */ /* ... */ /* [!code ++] */ } ``` **커스텀 컴포넌트에서 recipe를 import하는 경우** 경로를 변경합니다. \[에러로 발견 가능] ```ts import { selectBoxCheckmark } from "@seed-design/css/recipes/selectBoxCheckmark"; // [!code --] import { selectBoxCheckmark } from "@seed-design/css/recipes/select-box-checkmark"; // [!code ++] ``` #### 5. 오버레이 컴포넌트 관련 버그 수정 및 개선 \[#5-오버레이-컴포넌트-관련-버그-수정-및-개선] \[직접 판단 필요] Alert Dialog, Bottom Sheet, Menu Sheet 관련 버그들을 수정했습니다. - `pointer-events`로 클릭을 막던 코드가 모두 제거되었습니다. 이를 오버라이드하던 우회 코드가 있다면 함께 제거할 수 있습니다. - 여러 오버레이가 겹친 상황에서 하위 오버레이가 닫히면 `onOpenChange` reason으로 `"cascadeDismiss"`를 제공합니다. - 트리거 클릭으로 Bottom Sheet가 열릴 때 `onOpenChange` reason `"trigger"`를 제공합니다. - iOS에서 Bottom Sheet 내부 버튼으로 시트를 스와이프할 때 의도치 않은 클릭이 발생하던 문제를 수정했습니다. - AppScreen, Drawer가 열릴 때 포커스가 컴포넌트 안으로 이동하고 그 안에서만 움직이도록 수정했습니다. `@seed-design/react`에서 Stackflow 환경과 잘 맞지 않던 `@radix-ui/react-dialog` 의존성과 패치가 제거되었습니다. pnpm 환경에서 SEED React를 위해서만 이 패키지를 설치했었다면 제거할 수 있습니다. ```shell pnpm why @radix-ui/react-dialog pnpm remove @radix-ui/react-dialog ``` #### 6. 대체 옵션이 존재하는 일부 API 제거 \[#6-대체-옵션이-존재하는-일부-api-제거] \[에러로 발견 가능] **Image Frame `rounded` 제거** `borderRadius="r2"`를 사용해주세요. 스타일은 동일합니다. ```tsx // [!code --] // [!code ++] ``` **Switch `size="small" | "medium"` 제거** 각각 `size="16"`, `size="32"`를 사용해주세요. 스타일은 동일합니다. ```tsx // [!code --] // [!code ++] // [!code --] // [!code ++] ``` **Checkbox `weight="default" | "stronger"` 제거** 각각 `weight="regular"`, `weight="bold"`를 사용해주세요. 스타일은 동일합니다. ```tsx // [!code --] // [!code ++] // [!code --] // [!code ++] ``` **레이아웃 컴포넌트 prop의 camelCase 값 제거** `Box`, `Flex`, `VStack`, `HStack` 등 레이아웃 컴포넌트의 `justifyContent`, `alignItems`, `flexDirection`, `display` 등에서 camelCase 값이 제거되었습니다. kebab-case 값을 사용해주세요. 스타일은 동일합니다. ```tsx // [!code --] // [!code ++] ``` #### 7. Menu Sheet deprecate 안내 \[#7-menu-sheet-deprecate-안내] 기존 Menu Sheet 관련 API는 SEED React 2에서도 계속 동작하지만 deprecated 되었습니다. Menu Sheet를 새롭게 사용하는 경우 [Swipeable Menu Sheet](/react/components/swipeable-menu-sheet) 컴포넌트를 사용해주세요.