SEED Design

V3 Improvements

V2(Sprout) 대비 V3에서 개선된 점을 소개합니다.

Concepts

Composition

V3에서는 컴포넌트를 조합하여 복잡한 UI를 구성하는 방법과 원칙을 제공합니다. as Prop, asChild Prop, 슬롯 등을 통해 컴포넌트를 조합할 수 있습니다.

Composition 페이지에서 자세히 설명합니다.

Composition
// Text 컴포넌트는 기본적으로 span 태그를 사용하지만, as Prop을 사용해 다른 태그로 변경할 수 있습니다.
<Text as="p">단락 텍스트</Text>
<Text as="span">인라인 텍스트</Text>
<Text as="h3">제목 텍스트</Text>

// ContextualFloatingButton은 기본적으로 button 태그를 사용하지만, asChild를 사용해 anchor로 변경할 수 있습니다.
<ContextualFloatingButton asChild>
  <a href="/create" aria-label="Create">
    <Icon svg={<IconPlusLine />} />
  </a>
</ContextualFloatingButton>

Snippet

V3에서는 자주 사용되는 UI 패턴을 스니펫으로 제공하여 개발 효율성을 높이는 방법을 제공합니다. Compound Components 패턴과 Snippet의 조합을 통해 복잡한 컴포넌트를 쉽게 구현할 수 있습니다.

Snippet 페이지에서 자세히 설명합니다.

Snippet
<Checkbox.Root>
  <Checkbox.Control>
    <Checkbox.Indicator />
  </Checkbox.Control>
  <Checkbox.Label>Click me</Checkbox.Label>
  <Checkbox.HiddenInput />
</Checkbox.Root>

Icon Usage

컴포넌트 안에서 아이콘을 사용하는 방식이 달라졌습니다.

Icon Composition 페이지에서 자세히 설명합니다.

Icon Usage
import { IconPlusFill } from "@karrotmarket/react-monochrome-icon";
import { PrefixIcon, SuffixIcon, Icon } from "@seed-design/react"; 
import { ActionButton } from "seed-design/ui/action-button";

export default function ActionButtonPrefixIcon() {
  return (
    <>
      <BoxButton prefix={<IconPlusFill />} /> {/* Sprout */}
      <BoxButton suffix={<IconPlusFill />} /> {/* Sprout */}

      <ActionButton>
        <PrefixIcon svg={<IconPlusFill />} />
        Prefix Icon
      </ActionButton>
      <ActionButton>
        <SuffixIcon svg={<IconPlusFill />} />
        Suffix Icon
      </ActionButton>
      <ActionButton layout="iconOnly" aria-label="Add">
        <Icon svg={<IconPlusFill />} />
      </ActionButton>
    </>
  );
}

Components

Avatar 업데이트

자세한 사용법은 Avatar 문서를 참고해주세요.

import { Avatar } from "@daangn/sprout-components-avatar";
import { Avatar } from "seed-design/ui/avatar"; // snippet 제공
  • size 옵션 재구성
  • 이미지 로딩 실패 시 대체 콘텐츠를 위한 fallback prop 제공
import { 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 (
    <Flex gap="x4">
      <Avatar
        size="80"
        src="https://avatars.githubusercontent.com/u/54893898?v=4"
        fallback={<IdentityPlaceholder />}
      >
        <AvatarBadge>
          <div style={{ background: "green", width: 20, height: 20, borderRadius: 9999 }} />
        </AvatarBadge>
      </Avatar>
      <Avatar size="80" src={undefined} fallback={<IdentityPlaceholder />} />
    </Flex>
  );
}

AlertDialog 업데이트

V2의 Dialog와 AlertDialog는 V3에서 AlertDialog로 제공돼요.

import { Dialog, DialogContainer, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@daangn/sprout-components-dialog";
import { AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger } from "seed-design/ui/alert-dialog"; // snippet 제공
  • 기존에 DialogHeader로만 구조화되어 있던 부분을 세분화 (AlertDialogContent, AlertDialogHeader, AlertDialogFooter)
  • 액션에 관한 슬롯 추가 AlertDialogAction
  • AlertDialogTrigger로 명시적으로 trigger를 제공하거나 혹은 open prop을 통해 외부에서 제어 가능
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.
    <AlertDialogRoot>
      <AlertDialogTrigger asChild>
        <ActionButton>열기</ActionButton>
      </AlertDialogTrigger>
      <AlertDialogContent layerIndex={50}>
        <AlertDialogHeader>
          <AlertDialogTitle>주의</AlertDialogTitle>
          <AlertDialogDescription>이 작업은 되돌릴 수 없습니다.</AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <ResponsivePair gap="x2">
            <AlertDialogAction variant="neutralWeak">취소</AlertDialogAction>
            <AlertDialogAction variant="neutralSolid">확인</AlertDialogAction>
          </ResponsivePair>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialogRoot>
  );
};

export default AlertDialogSingle;

버튼 업데이트

BoxButton, TextButton

V2의 BoxButtonTextButton은 V3에서 ActionButton으로 변경되었어요.

import { BoxButton, TextButton } from "@daangn/sprout-components-button";
import { ActionButton } from "seed-design/ui/action-button"; // snippet 제공
  • size 및 variant 옵션 재구성
    • xlarge size 제거
    • PrimaryNeutral Solid
    • Primary lowNeutral Weak
    • SecondaryNeutral Weak
    • DangerCritical Solid
    • TextButton을 대체하는 ghost variant 추가
  • layout prop 추가 (withText, iconOnly)
  • prefix, suffix: 아이콘 배치 방식이 children으로 통합
import { ActionButton } from "seed-design/ui/action-button";

export default function ActionButtonPreview() {
  return <ActionButton>라벨</ActionButton>;
}

BoxToggleButton, CapsuleToggleButton

V2의 BoxToggleButton, CapsuleToggleButton은 V3에서 각각 ToggleButton, ReactionButton으로 변경되었어요.

import { BoxToggleButton, CapsuleToggleButton } from "@daangn/sprout-components-button";
import { ToggleButton } from "seed-design/ui/toggle-button"; // snippet 제공
import { ReactionButton } from "seed-design/ui/reaction-button"; // snippet 제공
  • size 옵션 재구성
import { useState } from "react";
import { ToggleButton } from "seed-design/ui/toggle-button";

export default function ToggleButtonPreview() {
  const [pressed, setPressed] = useState(false);

  return (
    <ToggleButton pressed={pressed} onPressedChange={setPressed}>
      {pressed ? "선택됨" : "미선택"}
    </ToggleButton>
  );
}
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 (
    <ReactionButton>
      <PrefixIcon svg={<IconFaceSmileCircleFill />} />
      도움돼요
      <Count>1</Count>
    </ReactionButton>
  );
}

Chip 업데이트

V2에서는 @daangn/sprout-components-chips 패키지에서 여러 칩 관련 컴포넌트들을 제공했어요. V3에서는 이러한 컴포넌트들이 목적에 따라 Chip으로 재구성되었어요.

  • layout prop 추가 (withText, iconOnly)
  • prefix, suffix prop 대신 children으로 아이콘 전달
  • count prop 제거

ChipButton

V2의 ChipButton은 V3에서 Chip.Button으로 변경되었어요.

import { ChipButton } from "@daangn/sprout-components-chips";
import { Chip } from "seed-design/ui/chip"; // snippet 제공
import { HStack, VStack } from "@seed-design/react";
import { Chip } from "seed-design/ui/chip";

export default function ChipPreview() {
  return (
    <VStack gap="x3" align="center">
      <HStack gap="x2">
        <Chip.Button>
          <Chip.Label>Button Chip</Chip.Label>
        </Chip.Button>
        <Chip.Toggle>
          <Chip.Label>Toggle Chip</Chip.Label>
        </Chip.Toggle>
      </HStack>
      <Chip.RadioRoot defaultValue="option1" aria-label="Options">
        <HStack gap="x2">
          <Chip.RadioItem value="option1">
            <Chip.Label>Radio Chip 1</Chip.Label>
          </Chip.RadioItem>
          <Chip.RadioItem value="option2">
            <Chip.Label>Radio Chip 2</Chip.Label>
          </Chip.RadioItem>
        </HStack>
      </Chip.RadioRoot>
    </VStack>
  );
}

ChipToggleButton

V2의 ChipToggleButton은 V3에서 Chip.Toggle으로 변경되었어요.

import { ChipToggleButton } from "@daangn/sprout-components-chips";
import { Chip } from "seed-design/ui/chip"; // snippet 제공

ChipRadio, ChipRadioGroup

V2의 ChipRadio, ChipRadioGroup는 V3에서 Chip.RadioRootChip.RadioItem으로 변경되었어요.

import { ChipToggleButton, ChipRadio, ChipRadioGroup } from "@daangn/sprout-components-chips";
import { Chip } from "seed-design/ui/chip"; // snippet 제공

ChipFilter

V2의 ChipFilter는 V3에서 제거되었어요. 필요한 경우 Chip.Button이나 Chip.Toggle로 대체할 수 있어요.

Checkbox 업데이트

자세한 사용법은 Checkbox 문서를 참고해주세요.

import { Checkbox } from "@daangn/sprout-components-checkbox";
import { Checkbox } from "seed-design/ui/checkbox"; // snippet 제공
  • size 및 variant 옵션 재구성
    • circle variant 제거, square 사용
    • small size 제거
  • bold prop을 weight으로 재구성
import { Checkbox } from "seed-design/ui/checkbox";

export default function CheckboxPreview() {
  return <Checkbox label="Hello World" defaultChecked />;
}

TextField 업데이트

V2에서는 TextField, MultilineTextField 컴포넌트를 따로 제공했지만, V3에서는 조합형 컴포넌트로 통합되었어요. 자세한 사용법은 TextFieldMultilineTextField 문서를 참고해주세요.

import { TextField, MultilineTextField } from "@daangn/sprout-components-text-field";
import { TextField, TextFieldInput, TextFieldTextarea } from "seed-design/ui/text-field"; // snippet 제공
  • size 재구성
    • largexlarge
    • mediumlarge
    • smallmedium
  • TextFieldInput: <input>
  • TextFieldTextarea: <textarea>
  • maxGraphemeCount prop 추가 (기존 maxLength prop을 override하던 동작을 명시적으로 변경)
import { TextField, TextFieldInput } from "seed-design/ui/text-field";

export default function TextFieldPreview() {
  return (
    <TextField>
      <TextFieldInput autoFocus />
    </TextField>
  );
}

InlineAlert 업데이트

V2에서는 @daangn/sprout-components-inline-alert 패키지에서 여러 배너 관련 컴포넌트들을 제공했어요. V3에서는 PageBanner 컴포넌트로 제공되고 있어요.

import { InlineBanner, ActionableInlineBanner, DismissibleInlineBanner } from "@daangn/sprout-components-banner";
import { PageBanner, ActionablePageBanner, DismissiblePageBanner } from "seed-design/ui/page-banner"; // snippet 제공
  • variant -> tone으로 변경 및 옵션 재구성
  • variant 옵션 추가
import { VStack } from "@seed-design/react";
import {
  ActionablePageBanner,
  DismissiblePageBanner,
  PageBanner,
} from "seed-design/ui/page-banner";

export default function PageBannerPreview() {
  return (
    <VStack gap="x4" width="full">
      <PageBanner description="Ut veniam in ea ea anim laborum magna dolore ea laborum duis ut aute mollit amet." />
      <ActionablePageBanner description="Ut veniam in ea ea anim laborum magna dolore ea laborum duis ut aute mollit amet." />
      <DismissiblePageBanner description="Ut veniam in ea ea anim laborum magna dolore ea laborum duis ut aute mollit amet." />
    </VStack>
  );
}

Callout 업데이트

자세한 사용법은 Callout 문서를 참고해주세요.

import { Callout, ActionableCallout, DismissibleCallout } from "@daangn/sprout-components-callout";
import { Callout, ActionableCallout, DismissibleCallout } from "seed-design/ui/callout"; // snippet 제공
  • varianttone으로 변경 및 옵션 재구성
import { VStack } from "@seed-design/react";
import { ActionableCallout, Callout, DismissibleCallout } from "seed-design/ui/callout";

export default function CalloutPreview() {
  return (
    <VStack gap="x4" width="full">
      <Callout description="Aute nulla proident tempor minim eiusmod. In nostrud officia irure laborum." />
      <ActionableCallout description="Aute nulla proident tempor minim eiusmod. In nostrud officia irure laborum." />
      <DismissibleCallout description="Aute nulla proident tempor minim eiusmod. In nostrud officia irure laborum." />
    </VStack>
  );
}

SelectBox 업데이트

V2에서는 @daangn/sprout-components-select-box 패키지에서 여러 SelectBox 관련 컴포넌트들을 제공했어요. V3에서는 이러한 컴포넌트들이 목적에 따라 재구성되었어요. 자세한 사용법은 CheckSelectBoxRadioSelectBox 문서를 참고해주세요.

SelectBoxCheck

import { SelectBoxCheck } from "@daangn/sprout-components-select-box";
import { CheckSelectBox, CheckSelectBoxGroup } from "seed-design/ui/select-box"; // snippet 제공
  • SelectBoxCheck -> CheckSelectBox
import { VStack } from "@seed-design/react";
import { CheckSelectBox, CheckSelectBoxGroup } from "seed-design/ui/select-box";

export default function CheckSelectBoxPreview() {
  return (
    <CheckSelectBoxGroup>
      <VStack gap="spacingY.componentDefault">
        <CheckSelectBox label="Apple" defaultChecked />
        <CheckSelectBox
          label="Melon"
          description="Elit cupidatat dolore fugiat enim veniam culpa."
        />
        <CheckSelectBox label="Mango" />
      </VStack>
    </CheckSelectBoxGroup>
  );
}

SelectBoxRadio, SelectBoxRadioGroup

import { SelectBoxRadio, SelectBoxRadioGroup } from "@daangn/sprout-components-select-box";
import { RadioSelectBoxItem, RadioSelectBoxRoot } from "seed-design/ui/select-box"; // snippet 제공
  • SelectBoxRadio -> RadioSelectBoxItem
  • SelectBoxRadioGroup -> RadioSelectBoxRoot
import { VStack } from "@seed-design/react";
import { RadioSelectBoxItem, RadioSelectBoxRoot } from "seed-design/ui/select-box";

export default function RadioSelectBoxPreview() {
  return (
    <RadioSelectBoxRoot defaultValue="apple" aria-label="Fruit">
      <VStack gap="spacingY.componentDefault">
        <RadioSelectBoxItem value="apple" label="Apple" />
        <RadioSelectBoxItem
          value="melon"
          label="Melon"
          description="Elit cupidatat dolore fugiat enim veniam culpa."
        />
        <RadioSelectBoxItem value="mango" label="Mango" />
      </VStack>
    </RadioSelectBoxRoot>
  );
}

Tabs 업데이트

Tabs

자세한 사용법은 Tabs 문서를 참고해주세요.

import { Tabs, TabList, Tab, TabPanelGroup, TabPanel } from "@daangn/sprout-components-tabs";
import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs"; // snippet 제공
  • size 옵션 재구성
  • dot -> notification으로 변경
  • layout -> triggerLayout으로 변경 (fill, hug)
  • stickyList prop 추가: 스크롤 시 탭 목록 고정 가능
  • 컨텐츠의 layout도 조절 가능 (contentLayout)
  • 렌더링 관련 prop 변경
    • isLazy -> lazyMount
    • lazyMode (unmount, keepMounted) -> unmountOnExit (boolean)
import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "seed-design/ui/tabs";

export default function TabsPreview() {
  return (
    <div style={{ width: "360px" }}>
      <TabsRoot defaultValue="1">
        <TabsList>
          <TabsTrigger value="1">라벨1</TabsTrigger>
          <TabsTrigger value="2">라벨2</TabsTrigger>
          <TabsTrigger value="3">라벨3</TabsTrigger>
        </TabsList>
        <TabsContent value="1">
          <Content>Content 1</Content>
        </TabsContent>
        <TabsContent value="2">
          <Content>Content 2</Content>
        </TabsContent>
        <TabsContent value="3">
          <Content>Content 3</Content>
        </TabsContent>
      </TabsRoot>
    </div>
  );
}

const Content = (props: React.PropsWithChildren) => {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        height: "300px",
        backgroundColor: "var(--seed-color-bg-layer-default)",
      }}
    >
      {props.children}
    </div>
  );
};

Switch 업데이트

자세한 사용법은 Switch 문서를 참고해주세요.

import { Switch } from "@daangn/sprout-components-switch";
import { Switch } from "seed-design/ui/switch"; // snippet 제공
  • size 옵션 재구성
  • tone 옵션 추가
  • children으로 제공되던 label 대신 label prop 추가
import { Switch } from "seed-design/ui/switch";

export default function SwitchPreview() {
  return <Switch defaultChecked />;
}

Spinner 업데이트

Spinner는 V3에서 ProgressCircle로 변경되었어요.

import { Spinner } from "@daangn/sprout-components-spinner";
import { ProgressCircle } from "seed-design/ui/progress-circle"; // snippet 제공
  • size 및 tone 옵션 재구성
  • 값이 결정된 상태(value prop 제공)와 미결정 상태(indeterminate prop) 지원
import { ProgressCircle } from "seed-design/ui/progress-circle";

export default function ProgressCirclePreview() {
  return <ProgressCircle tone="neutral" size="40" />;
}

Snackbar 업데이트

자세한 사용법은 Snackbar 문서를 참고해주세요.

import { SnackBar, useSnackbarAdapter } from "@daangn/sprout-components-snackbar";
import { Snackbar, SnackbarProvider, useSnackbar } from "seed-design/ui/snackbar"; // snippet 제공
  • variant 옵션 재구성
  • SnackbarProvideruseSnackbar hook을 통한 편리한 선언적 사용 방식 지원 (기존과 동일)
import { ActionButton } from "seed-design/ui/action-button";
import { Snackbar, SnackbarProvider, useSnackbarAdapter } from "seed-design/ui/snackbar";

function Component() {
  const adapter = useSnackbarAdapter();

  return (
    <ActionButton
      onClick={() =>
        adapter.create({
          timeout: 5000,
          onClose: () => {},
          render: () => <Snackbar message="알림 메세지" actionLabel="확인" onAction={() => {}} />,
        })
      }
    >
      실행
    </ActionButton>
  );
}

export default function SnackbarPreview() {
  return (
    <SnackbarProvider>
      <Component />
    </SnackbarProvider>
  );
}

HelpBubble 업데이트

자세한 사용법은 HelpBubble 문서를 참고해주세요.

import { HelpBubbleAnchor, HelpBubbleTrigger } from "@daangn/sprout-components-help-bubble";
import { HelpBubbleTrigger, HelpBubbleAnchor } from "seed-design/ui/help-bubble"; // snippet 제공
import { IconILowercaseSerifCircleLine } from "@karrotmarket/react-monochrome-icon";
import { HelpBubbleTrigger } from "seed-design/ui/help-bubble";
import { ActionButton } from "seed-design/ui/action-button";
import { Icon } from "@seed-design/react";

export default function HelpBubblePreview() {
  return (
    <HelpBubbleTrigger defaultOpen title="아래 버튼이나 바깥 영역을 클릭해서 닫아보세요.">
      <ActionButton variant="ghost" size="small" layout="iconOnly" aria-label="도움말">
        <Icon svg={<IconILowercaseSerifCircleLine />} />
      </ActionButton>
    </HelpBubbleTrigger>
  );
}

FAB(Floating Action Button) 업데이트

V2의 FloatingActionButton과 ExtendedFAB는 용도에 따라 FloatingActionButtonContextualFloatingButton으로로 재구성되었어요.

import { FloatingActionButton } from "@daangn/sprout-components-floating-action-button";
import { ExtendedFab } from "@daangn/sprout-components-fab";
import { FloatingActionButton } from "seed-design/ui/floating-action-button"; // snippet 제공
import { ContextualFloatingButton } from "seed-design/ui/contextual-floating-button"; // snippet 제공
import { FloatingActionButton } from "seed-design/ui/floating-action-button";
import IconPlusLine from "@karrotmarket/react-monochrome-icon/IconPlusLine";

export default function FloatingActionButtonPreview() {
  return <FloatingActionButton icon={<IconPlusLine />} label="Example FAB" />;
}
import { IconBellFill } from "@karrotmarket/react-monochrome-icon";
import { PrefixIcon } from "@seed-design/react";
import { ContextualFloatingButton } from "seed-design/ui/contextual-floating-button";

export default function ContextualFloatingButtonPreview() {
  return (
    <ContextualFloatingButton>
      <PrefixIcon svg={<IconBellFill />} />
      알림 설정
    </ContextualFloatingButton>
  );
}

신규 컴포넌트

컴포넌트는 꾸준히 추가되고 있습니다. 사이드바에서 최신 목록을 확인해보세요.

레이아웃 관련 컴포넌트

레이아웃 구성을 위한 다양한 컴포넌트가 제공됩니다.

import { Box, Flex, VStack, HStack } from "@seed-design/react";
  • Box: 기본 레이아웃 컨테이너
  • Flex: Flex 레이아웃을 구성하기 위한 컴포넌트
  • VStack: 수직 방향으로 요소를 쌓는 레이아웃
  • HStack: 수평 방향으로 요소를 쌓는 레이아웃

Last updated on