Accordion

콘텐츠의 숨겨진 내용을 확장하거나 축소하여 보여주는 요소입니다. 클릭하여 원하는 정보만 선택적으로 볼 수 있어, 제한된 공간에서 많은 정보를 효율적으로 구성하고 탐색할 수 있도록 돕습니다.

Basic accordion

내용을 최소화하거나 펼칠 때 사용합니다.

import { Accordion, AccordionSummary, AccordionDetails, AccordionDescription } from '@wanteddev/wds';

const Demo = () => {
  return (
    <Accordion sx={{ width: '100%' }}>
      <AccordionSummary>
        제목
      </AccordionSummary>
      <AccordionDetails>
        <AccordionDescription>
          제목에 대한 상세 내용을 입력해주세요.<br />
          긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
        </AccordionDescription>
      </AccordionDetails>
    </Accordion>
  )
}

export default Demo;

Anatomy

아래와 같은 구조로 조합하여 사용합니다.

<Accordion>
  <AccordionSummary />

  <AccordionDetails>
    <AccordionDescription />
    <AccordionContent />
  </AccordionDetails>
</Accordion>

Accordion summary

With accordion summary content

leadingContent를 추가하거나 기본 trailingContent를 교체할 수 있습니다.

trailingContent가 a 태그이거나 클릭 이벤트가 있는 경우 e.stopPropagation 을 통해 아코디언이 열리고 닫히는 동작을 막을 수 있습니다.

AccordionSummaryContent 컴포넌트로 감싸서 사용하고, rotate 옵션을 통해 열리고 닫힐 때 회전 여부를 결정할 수 있습니다.

import { Accordion, AccordionSummary, AccordionDetails, AccordionDescription, AccordionSummaryContent, IconButton, FlexBox } from '@wanteddev/wds';
import { IconCircleCheckFill, IconChevronDown, IconArrowRight, IconCaretDown, IconPlus } from '@wanteddev/wds-icon';
import { useState } from 'react';

const Demo = () => {
  const [expandedCheckAccordion, setExpandedCheckAccordion] = useState(false);

  return (
    <FlexBox flexDirection="column" gap="12px" sx={{ width: '100%' }}>
      <Accordion disabled>
        <AccordionSummary>
          Disabled, true
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>

      <Accordion>
        <AccordionSummary
          leadingContent={
            <AccordionSummaryContent variant="icon">
              <IconPlus />
            </AccordionSummaryContent>
          }
        >
          LeadingContent, Icon<br />
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>

      <Accordion>
        <AccordionSummary
          trailingContent={
            <AccordionSummaryContent variant="icon" rotate>
              <IconCaretDown />
            </AccordionSummaryContent>
          }
        >
          TrailingContent, Icon, Rotate<br />
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>

      <Accordion
        expanded={expandedCheckAccordion}
        onChange={setExpandedCheckAccordion}
      >
        <AccordionSummary
          trailingContent={
            <AccordionSummaryContent
              variant="icon"
            >
              <IconCircleCheckFill
                sx={(theme) => ({
                  color: expandedCheckAccordion 
                    ? theme.semantic.primary.normal 
                    : theme.semantic.label.alternative,
                })}
              />
            </AccordionSummaryContent>
          }
        >
          TrailingContent, Icon<br />
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>
      
      <Accordion>
        <AccordionSummary
          trailingContent={
            <AccordionSummaryContent variant="icon-button" rotate>
              <IconButton>
                <IconChevronDown
                  sx={(theme) => ({
                    color: theme.semantic.label.normal,
                  })}
                />
              </IconButton>
            </AccordionSummaryContent>
          }
        >
          TrailingContent, IconButton, Rotate
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>

      <Accordion>
        <AccordionSummary
          trailingContent={
            <AccordionSummaryContent
              variant="icon-button"
            >
              <IconButton
                as="a"
                href="https://www.wanted.co.kr"
                target="_blank"
                onClick={(e) => {
                  e.stopPropagation();
                }}
              >
                <IconArrowRight />
              </IconButton>
            </AccordionSummaryContent>
          }
        >
          TrailingContent, IconButton<br />
          trailingContent onClick
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>
    </FlexBox>
  )
}

export default Demo;

Padding

AccordionSummary의 verticalPadding, fillWidth으로 간격을 조정할 수 있습니다.

import { Accordion, AccordionSummary, AccordionDetails, AccordionDescription, FlexBox } from '@wanteddev/wds';

const Demo = () => {
  return (
    <FlexBox flexDirection="column" gap="12px" sx={{ width: '100%' }}>
      <Accordion>
        <AccordionSummary fillWidth>
          FillWidth, true
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>

      <Accordion divider={false}>
        <AccordionSummary>
          Divider, false
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>

      <Accordion>
        <AccordionSummary>
          VerticalPadding, Large (default)
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>

      <Accordion>
        <AccordionSummary verticalPadding="medium">
        VerticalPadding, Medium
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>

      <Accordion>
        <AccordionSummary verticalPadding="small">
          VerticalPadding, Small
        </AccordionSummary>
        <AccordionDetails>
          <AccordionDescription>
            제목에 대한 상세 내용을 입력해주세요.<br />
            긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
          </AccordionDescription>
        </AccordionDetails>
      </Accordion>
    </FlexBox>
  )
}

export default Demo;

Nested accordion

Accordion을 중첩해서 사용할 수 있습니다.

중첩 인터렉션 영역이 잘리는 경우가 생길 수 있습니다. disableAnimation 를 사용하거나 하위 Accordion은 fillWidth 로 사용하여 인터렉션 영역을 보호할 수 있습니다.

import { FlexBox, Accordion, AccordionSummary, AccordionDetails, AccordionDescription } from '@wanteddev/wds';
import type { CSSProperties } from 'react';

const Demo = () => {
  return (
    <FlexBox gap="12px" flexDirection="column" sx={{ width: '100%' }}>
      <Accordion disableAnimation>
        <AccordionSummary>
          최상위 Accordion, disableAnimation
        </AccordionSummary>
        <AccordionDetails sx={{ gap: '4px' }}>
          <Accordion divider={false} disableAnimation>
            <AccordionSummary>
              하위 Accordion, disableAnimation
            </AccordionSummary>
            <AccordionDetails>
              <AccordionDescription>
                제목에 대한 상세 내용을 입력해주세요.<br />
                긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
              </AccordionDescription>
            </AccordionDetails>
          </Accordion>
          <Accordion divider={false} disableAnimation>
            <AccordionSummary>
              하위 Accordion, disableAnimation
            </AccordionSummary>
            <AccordionDetails>
              <AccordionDescription>
                제목에 대한 상세 내용을 입력해주세요.<br />
                긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
              </AccordionDescription>
            </AccordionDetails>
          </Accordion>
        </AccordionDetails>
      </Accordion>

      <Accordion>
        <AccordionSummary fillWidth>
          최상위 Accordion, fillWidth
        </AccordionSummary>
        <AccordionDetails sx={{ gap: '4px', padding: 0 }}>
          <Accordion divider={false}>
            <AccordionSummary fillWidth>
              하위 Accordion, fillWidth
            </AccordionSummary>
            <AccordionDetails>
              <AccordionDescription>
                제목에 대한 상세 내용을 입력해주세요.<br />
                긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
              </AccordionDescription>
            </AccordionDetails>
          </Accordion>
          <Accordion divider={false}>
            <AccordionSummary fillWidth>
              하위 Accordion, fillWidth
            </AccordionSummary>
            <AccordionDetails>
              <AccordionDescription>
                제목에 대한 상세 내용을 입력해주세요.<br />
                긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
              </AccordionDescription>
            </AccordionDetails>
          </Accordion>
        </AccordionDetails>
      </Accordion>
    </FlexBox>
  )
}

export default Demo;

With accordion content

AccordionContent를 이용하여 AccordionDetails 내부에 콘텐츠를 추가할 수 있습니다.

import { Accordion, AccordionSummary, AccordionDetails, AccordionDescription, AccordionContent, TextButton } from '@wanteddev/wds';
import { IconExternalLink } from '@wanteddev/wds-icon';

const Demo = () => {
  return (
    <Accordion sx={{ width: '100%' }}>
      <AccordionSummary>
        제목
      </AccordionSummary>
      <AccordionDetails>
        <AccordionDescription>
          제목에 대한 상세 내용을 입력해주세요.<br />
          긴 컨텐츠라면 접은 상태를 기본 값으로 사용하세요.
        </AccordionDescription>
        <AccordionContent>
          <TextButton
            size="small"
            color="assistive"
            trailingContent={<IconExternalLink />}
          >
            자세히 알아보기
          </TextButton>
        </AccordionContent>
      </AccordionDetails>
    </Accordion>
  )
}

export default Demo;

Accessibility

WAI-ARIA Accordion Pattern 을 준수합니다.

API

Accordion

NameTypesdefaultValue
expanded
boolean
-
defaultExpanded
boolean
-
disabled
boolean
false
divider
boolean
true
onChange
(expanded: boolean) => void
-
disableAnimation
boolean
false
sx
SxProp
-

AccordionSummary

ListCell과 동일한 props를 사용합니다.

NameTypesdefaultValue
leadingContent
ReactNode
-
trailingContent
ReactNode
-
children
ReactNode
-
disabled
boolean
-
sx
SxProp
-
xl
Merge<Pick<ListCellDefaultProps, "verticalPadding" | "fillWidth" | "interactionPadding">, { sx?: CSSInterpolation; }> | undefined
-
lg
Merge<Pick<ListCellDefaultProps, "verticalPadding" | "fillWidth" | "interactionPadding">, { sx?: CSSInterpolation; }> | undefined
-
md
Merge<Pick<ListCellDefaultProps, "verticalPadding" | "fillWidth" | "interactionPadding">, { sx?: CSSInterpolation; }> | undefined
-
sm
Merge<Pick<ListCellDefaultProps, "verticalPadding" | "fillWidth" | "interactionPadding">, { sx?: CSSInterpolation; }> | undefined
-
xs
Merge<Pick<ListCellDefaultProps, "verticalPadding" | "fillWidth" | "interactionPadding">, { sx?: CSSInterpolation; }> | undefined
-
verticalPadding
"small" | "medium" | "large" | "none"
"large"
fillWidth
boolean
-
interactionPadding
Property.PaddingLeft<string | number> | undefined
-
ellipsis
boolean
-
disableInteraction
boolean
-
textProps
Merge<TypographyProps, { caption?: ReactNode; captionProps?: ComponentProps<typeof Typography>; children?: ReactNode; sx?: SxProp; }>
-
flexDirection
Property.FlexDirection | undefined
-
flexWrap
Property.FlexWrap | undefined
-
justifyContent
Property.JustifyContent | undefined
-
alignItems
Property.AlignItems | undefined
-
alignContent
Property.AlignContent | undefined
-
order
Property.Order | undefined
-
flex
Property.Flex<string | number> | undefined
-
flexGrow
Property.FlexGrow | undefined
-
flexShrink
Property.FlexShrink | undefined
-
flexBasis
Property.FlexBasis<string | number> | undefined
-
alignSelf
Property.AlignSelf | undefined
-
gap
Property.Gap<string | number> | undefined
-
rowGap
Property.RowGap<string | number> | undefined
-
columnGap
Property.ColumnGap<string | number> | undefined
-

AccordionSummaryContent

ListCellContent과 유사한 props를 사용합니다.

NameTypesdefaultValue
rotate
boolean
false
children
ReactNode
-
disabled
boolean
-
sx
SxProp
-
variant
"button" | "switch" | "chevron" | "icon" | "radio" | "checkbox" | "icon-button" | "badge" | "avatar" | "large-icon" | "value" | "thumbnail" | "custom"
-
chevron
boolean
-

AccordionDetails

NameTypesdefaultValue
as
ElementType
-
forceMount
boolean
false
wrapperSx
SxProp
-
sx
SxProp
-

AccordionDescription

Typography와 동일한 props를 사용합니다.

NameTypesdefaultValue
xl
Merge<Pick<TypographyDefaultProps, "variant" | "weight" | "align">, { sx?: CSSInterpolation; }> | undefined
-
lg
Merge<Pick<TypographyDefaultProps, "variant" | "weight" | "align">, { sx?: CSSInterpolation; }> | undefined
-
md
Merge<Pick<TypographyDefaultProps, "variant" | "weight" | "align">, { sx?: CSSInterpolation; }> | undefined
-
sm
Merge<Pick<TypographyDefaultProps, "variant" | "weight" | "align">, { sx?: CSSInterpolation; }> | undefined
-
xs
Merge<Pick<TypographyDefaultProps, "variant" | "weight" | "align">, { sx?: CSSInterpolation; }> | undefined
-
color
ThemeColorsToken
-
children
ReactNode
-
sx
SxProp
-
variant
"display1" | "display2" | "display3" | "title1" | "title2" | "title3" | "heading1" | "heading2" | "headline1" | "headline2" | "body1" | "body1-reading" | "body2" | "body2-reading" | "label1" | "label1-reading" | "label2" | "caption1" | "caption2"
-
weight
"medium" | "regular" | "bold"
-
align
Property.TextAlign | undefined
-
noWrap
boolean
-
display
Property.Display | undefined
-

AccordionContent

NameTypesdefaultValue
as
ElementType
-
children
ReactNode
-
sx
SxProp
-

© 2026 Wanted Lab, Inc.