Sidebar | CoUI
본문으로 건너뛰기
LogoCoUI

Sidebar

사이드바 — 앱의 신원 · 검색 · 메뉴 · 계정을 담는 옆 내비게이션

목차

Sidebar 는 화면의 읽는 방향 시작 쪽에 붙는 고정 폭 내비게이션입니다. 위에 앱의 신원(워크스페이스)과 검색, 가운데에 메뉴 행, 아래에 도움말과 로그인한 사람을 둡니다. 사이드바는 영역만 나누고 행을 직접 그리지 않습니다 — 행은 부품 컴포넌트를 조합해 넣습니다.

부품역할
SidebarItem메뉴 한 줄 — 아이콘 · 이름 · 점 · 개수 · 뒤 아이콘
SidebarSection행 묶음 위의 조용한 제목
SidebarGroup하위 행을 접고 펴는 행
SidebarHeader 워크스페이스 또는 사람의 신원 행 (plain · card)
SidebarSearch검색을 여는 입력 모양의 버튼, 단축키는 Kbd
SidebarRailItem아이콘 레일의 정사각형 목적지

기본 사용법 (Basic Usage)#

Sidebar(
  header: SidebarHeader(
    leading: const Avatar(initials: 'NA'),
    title: const Text('나음 병원'),
    subtitle: const Text('본원 · 관리자'),
    trailing: const Icon(LucideIcons.chevronsUpDown),
    onPressed: () {},
  ),
  search: SidebarSearch(
    placeholder: '검색',
    shortcut: const ['⌘', 'K'],
    onPressed: () {},
  ),
  children: [
    SidebarItem(
      leading: const Icon(LucideIcons.house),
      label: const Text('홈'),
      selected: true,
      onPressed: () {},
    ),
    const SidebarSection(label: Text('업무')),
    SidebarGroup(
      leading: const Icon(LucideIcons.folder),
      label: const Text('프로젝트'),
      children: [
        SidebarItem(
          level: CoreSidebarItemLevel.nested,
          label: const Text('디자인 시스템'),
          onPressed: () {},
        ),
      ],
    ),
  ],
)
Sidebar(
  header: SidebarHeader(
    leading: Avatar(initials: 'NA'),
    title: Text('나음 병원'),
    subtitle: Text('본원 · 관리자'),
    trailing: Icon(LucideIcons.chevronsUpDown),
    onPressed: () {},
  ),
  search: SidebarSearch(
    placeholder: '검색',
    shortcut: ['⌘', 'K'],
    onPressed: () {},
  ),
  children: [
    SidebarItem(
      leading: Icon(LucideIcons.house),
      label: Text('홈'),
      selected: true,
      onPressed: () {},
    ),
    SidebarSection(label: Text('업무')),
    SidebarGroup(
      leading: Icon(LucideIcons.folder),
      label: Text('프로젝트'),
      children: [
        SidebarItem(
          level: CoreSidebarItemLevel.nested,
          label: Text('디자인 시스템'),
          onPressed: () {},
        ),
      ],
    ),
  ],
)

Live Preview#

구조 (Anatomy)#

standard    패널  top    header · search
                  body   children (스크롤)
                  bottom footer · help · user
twoPane     레일  railHeader · railItems · railFooter
            패널  top    panelTitle · panelAction · search
                  body   children (스크롤)
                  bottom footer · help · user
collapsed   레일  railHeader · railItems · railFooter
  • 패널의 위아래 영역은 고정되고 가운데 본문만 스크롤됩니다.
  • 페이지와 맞닿는 끝쪽 경계선은 가장 바깥 열이 그립니다 — collapsed 는 레일, 나머지는 패널.

Figma: CoUI 라이브러리 ❖ Sidebar 페이지.

사용 시기 (When to Use)#

  • 메뉴가 많고 구역 · 그룹 · 2단 메뉴가 필요한 관리자 · 업무 화면.
  • 화면 왼쪽(오른쪽에서 왼쪽으로 쓰는 언어는 오른쪽)에 늘 머무는 내비게이션.

모바일의 아래 탭 바는 NavigationBar 를, 화면 일부의 탭 전환은 Tabs 를 씁니다.

Props / Parameters#

속성타입기본값설명
type CoreSidebarType standard standard · twoPane · collapsed
header W? null 패널 맨 위 신원 행 — 보통 SidebarHeader
search W? null 검색 트리거 — 보통 SidebarSearch
panelTitle W? null 두 칸 패널의 제목
panelAction W? null 패널 제목 끝의 동작
children List<W> [] 패널 본문의 행
footer W? null 도움말 위의 내용(사용량 카드 같은 것)
helpW?null도움말 행
userW?null맨 아래 로그인한 사람
railHeader W? null 레일 맨 위 표식(로고)
railItems List<W> [] 레일의 목적지 — 보통 SidebarRailItem
railFooter W? null 레일 맨 아래(설정 · 아바타)
semanticLabel String? null 내비게이션 랜드마크의 접근성 이름
sidebarStyle CoreSidebarStyle? null 인스턴스 스타일

SidebarItem#

속성타입기본값설명
labelW필수목적지 이름
leadingW?null이름 앞 아이콘
count W? null 끝의 개수 — 보통 Badge
trailingW?null맨 끝 아이콘
dotboolfalse안 읽음 점
level CoreSidebarItemLevel top top(36) · nested(32)
selectedboolfalse지금 있는 곳
enabled bool? null null 이면 onPressed 가 있을 때 켜짐
onPressed VoidCallback? null 눌렀을 때
sidebarItemStyle CoreSidebarItemStyle? null 인스턴스 스타일

나머지 부품#

컴포넌트속성
SidebarSection label (필수) · action · sidebarSectionStyle
SidebarGroup label (필수) · children (필수) · leading · expanded · initiallyExpanded ( true ) · onExpandedChanged · sidebarGroupStyle
SidebarHeader title (필수) · variant ( plain ) · leading · subtitle · trailing · onPressed · sidebarHeaderStyle
SidebarSearch placeholder (필수) · shortcut · onPressed · sidebarSearchStyle
SidebarRailItem icon (필수) · label (필수) · dot · selected · enabled · onPressed · sidebarRailItemStyle

CoreSidebarStyle 필드#

필드타입설명
backgroundColor CoreColor? The panel's face. null defers to [defaultBackgroundColor].
borderColor CoreColor? The line on the edge that meets the page. null defers to [defaultBorderColor].
borderWidth double? Width of the edge line (logical px). null defers to [defaultBorderWidth].
panelWidth double? Width of the panel (logical px). null defers to the type's row in [defaultsByType].
railWidth double? Width of the icon rail (logical px). null defers to [defaultRailWidth].
railBackgroundColor CoreColor? The rail's face. null defers to the type's row in [defaultsByType].
railPadding CoreEdgeInsets? The rail's insets. null defers to [defaultRailPadding].
railSpacing double? Spacing between the rail's items. null defers to [defaultRailSpacing].
railHeaderGapStyle CoreGapStyle? Extra room after the rail's header. null defers to [defaultRailHeaderGapStyle].
topPadding CoreEdgeInsets? The top region's insets (header, search, panel title). null defers to the type's row in [defaultsByType].
topSpacing double? Spacing between the top region's parts. null defers to [defaultTopSpacing].
bodyPadding CoreEdgeInsets? The body's insets. null defers to the type's row in [defaultsByType].
bodySpacing double? Spacing between the body's rows. null defers to [defaultBodySpacing].
bottomPadding CoreEdgeInsets? The bottom region's insets. null defers to [defaultBottomPadding].
bottomSpacing double? Spacing between the bottom region's parts. null defers to [defaultBottomSpacing].
panelTitleStyle CoreTextStyle? The two-pane panel title's text. null defers to [defaultPanelTitleStyle].
panelTitlePadding CoreEdgeInsets? Insets around the panel title row. null defers to [defaultPanelTitlePadding].
panelTitleSpacing double? Spacing between the panel title and its action. null defers to [defaultPanelTitleSpacing].
panelActionIconStyle CoreIconStyle? The panel title's action glyph. null defers to [defaultPanelActionIconStyle].

스타일 시스템 (Style System)#

모든 chrome 은 각 컴포넌트의 단일 Style 슬롯으로만 들어갑니다. 사이드바는 틀(면 · 경계 · 폭 · 영역 여백)만, 행은 자기 Style 을 가집니다.

Resolve chain#

CoreSidebarStyle.defaultX → defaultsByType[type]
  → CoreSidebarTheme.style
  → CoreSidebarTheme.variantStyles[type]
  → widget.sidebarStyle

CoreSidebarItemStyle.defaultX → defaultsByLevel[level]
  → CoreSidebarItemTheme.style
  → CoreSidebarItemTheme.variantStyles[level]
  → widget.sidebarItemStyle

테마로 덮어쓰기#

CoreComponentTheme(
  sidebar: CoreSidebarTheme(
    style: CoreSidebarStyle(panelWidth: CoreLayout.layout280),
  ),
  sidebarItem: CoreSidebarItemTheme(
    variantStyles: {
      CoreSidebarItemLevel.nested: CoreSidebarItemStyle(
        minHeight: CoreSize.size28,
      ),
    },
  ),
)

변형 (Variants)#

type: standard#

한 패널. 폭 248, 바탕 surfaceContainerLow, 끝쪽 경계 outlineVariant.

type: twoPane#

64 레일 옆에 240 패널. 레일은 한 단 깊은 surfaceContainer 로 칠해져 두 열로 읽힙니다. 패널 위에는 레일에서 고른 영역의 제목(panelTitle)이 옵니다.

type: collapsed#

레일만 남깁니다. 각 목적지의 이름은 툴팁과 접근성 이름이 말합니다.

동작 스펙 (Behavior)#

인터랙션#

  • SidebarItem · SidebarRailItem · SidebarSearch 와 onPressed 가 있는 SidebarHeader 는 누를 수 있습니다. hover · focus · press 는 onSurface 상태 레이어(3 %)로 표시합니다.
  • SidebarGroup 의 행을 누르면 하위 행이 열리고 닫힙니다.

상태 전환#

  • 미선택 행의 글자와 아이콘은 onSurfaceSubtle, 지금 있는 곳은 surfaceContainer 바탕에 onSurface 600 입니다.
  • 비활성 행은 disabledContent 로 칠해지고, 선택돼 있어도 선택으로 그리지 않습니다.

애니메이션#

  • 행 바탕 전환은 Clickable 의 시계를 따릅니다.
  • 그룹이 열고 닫히는 시간은 CoreDuration.normal(200ms) 이며 동작 줄이기에서 0 이 됩니다.

사용 가이드라인 (Usage Guidelines)#

Do#

  • 한 사이드바에서 selected 는 한 행에만 둡니다.
  • 하위 행에는 level: CoreSidebarItemLevel.nested 를 줍니다.

Don't#

  • 화면 아래 탭 바 자리에 사이드바를 쓰지 않습니다 — NavigationBar 를 씁니다.
  • 사용량 카드 같은 제품 콘텐츠를 사이드바 부품으로 만들지 않고 footer 슬롯에 Card · Progress 를 조합해 넣습니다.

접근성 (Accessibility)#

역할#

  • Sidebar 는 내비게이션 랜드마크입니다 (Web <nav>, Flutter navigation 시맨틱). semanticLabel 로 이름을 붙입니다.
  • 지금 있는 곳은 Web 에서 aria-current="page", Flutter 에서 selected 로 알립니다.
  • SidebarGroup 의 행은 aria-expanded 로 열림을 알립니다. 닫힌 하위 행은 Web 에서 inert 라 키보드와 보조 기술에 닿지 않습니다.
  • SidebarSection 은 제목(heading)입니다.

키보드#

  • Tab 으로 누를 수 있는 부품을 차례로 지나고, 위 · 아래 화살표로 행 사이를 옮깁니다.
  • Enter · Space 로 행을 누릅니다.

스크린 리더#

  • SidebarRailItem 은 보이는 이름이 없어 label 이 접근성 이름입니다.
  • SidebarSearch 는 placeholder 가 접근성 이름이고 단축키는 Web 에서 aria-keyshortcuts 로 알립니다.

포커스 관리#

포커스 링은 각 행의 모서리를 따릅니다(Clickable).

알려진 제약#

  • 미선택 행의 onSurfaceSubtle 은 라이트 테마의 surfaceContainerLow 위에서 4.5 : 1 아래로 내려갑니다. Tabs 의 미선택 글자와 같이 받아들인 예외입니다. 지금 있는 곳은 onSurface 라 이 제약 밖입니다.

전역 축(동작 줄이기 · 고대비 · 색 강제 모드 · 시맨틱 역할 · 최소 터치 타겟)은 접근성 페이지가 한 곳에서 다룹니다.

크로스 플랫폼 차이점 (Platform Differences)#

항목FlutterWeb
화살표 이동 Shortcuts → NextFocusIntent / PreviousFocusIntent keydown 에서 다음 · 이전 role="button" 에 포커스
상태 레이어 위젯이 hover · focus · press 를 감지해 겹침 색을 고름 범위 <style> 규칙이 :hover · :focus-visible · :active 로 칠함
그룹 열기AnimatedSizegrid-template-rows 전환

추가 차이 없음 — 이름 · 기본값 · 순서는 두 플랫폼이 같습니다.

  • NavigationBar: 화면 아래 탭 바와 레일
  • Tabs: 화면 일부의 탭 전환
  • Kbd: SidebarSearch 의 단축키

조합 예제#

Sidebar(
  footer: Card(
    child: Progress(value: 0.6),
  ),
  children: [...],
)