Raft 设计系统

组件 · 导航

Sidebar

The desktop column next to the rail that lists what the selected destination contains, with rows that mark where you are, what is unread and what mentions you.

稳定也叫 Sidebar row
import { Sidebar } from "@botiverse/raft-ui";
本页尚未翻译
译文完成之前,这里显示英文原文。 阅读英文版

The sidebar is the desktop column between the rail and the main area. It lists what the destination selected on the AppRail contains: channels, agents and people, for example. Its header names the destination, and its rows show where you are, what is unread and what mentions you.

sidebar/sidebar

When to use

SituationUse
The contents of a desktop destinationSidebar
The same list on a phoneSidebar rows, on the page itself; below md they are 44px
The destinations themselvesAppRail
Activity and notificationsInboxItem

Which destinations have a sidebar, and what each one lists, is the product's choice (Raft Chat). A destination without a sidebar puts its content next to the rail (Page structure).

Anatomy

  • SidebarRoot: the column, on layer-sidebar, with a line-seam along its right edge.
  • SidebarHeader: the 56px header, with a SidebarHeaderTitle and, on its right, the working summary when someone is working.
  • SidebarBody and SidebarContent: the scrolling list.
  • SidebarSection: a group of rows.
    • SidebarSectionHeader: holds a SidebarSectionDisclosure (the button that collapses the group, with SidebarSectionChevron, SidebarSectionTitle and SidebarSectionCount) and, in SidebarSectionTrailing or SidebarSectionControls, SidebarSectionControl buttons such as New. SidebarSectionAttention is the group's pink pixel.
    • SidebarList: the rows. SidebarSectionEmpty replaces them when the group is empty.
  • SidebarMenu, with SidebarMenuIcon, SidebarMenuTitle, SidebarMenuAside and SidebarMenuCount: a single destination in a section header, such as Saved.
  • SidebarGroupLabel: a plain group heading that doesn't collapse.
  • SidebarItem: one row.
    • First, a SidebarItemChannelIcon, a SidebarItemIcon or a 19px Avatar.
    • SidebarItemContent: the SidebarItemTitle (the name) and an optional SidebarItemSubtitle (a description).
    • SidebarItemAside: what follows: SidebarItemMeta, SidebarItemMetaIcon, SidebarItemStatus, the unread SidebarItemCount (with SidebarItemThreadCount and SidebarItemCountSeparator), or the mention marker SidebarItemIndicator.
sidebar-item/compositions/channel-item

States

StateLook
activeA yellow sticker: you are here. It doesn't change on hover; a press lays sticker-medium
tone="unread"A bold name and a pink SidebarItemCount. When someone mentions the person, a yellow SidebarItemIndicator replaces the count
tone="muted"A bell-off SidebarItemMetaIcon, the name in foreground-muted, and the unread number as quiet text in SidebarItemMeta
tone="away"Offline, or not joined: the name in foreground-hint
Menu opendata-popup-open (a context menu, or press and hold on a phone): fill-subtle, the same subtle tier as the hover, so the row the menu acts on stays marked; an active row stays the sticker (Hover and press)
interactive={false}A display-only row, such as this computer, whose action is a button inside it: a div that doesn't react to hover or press
sidebar-item/compositions/tones-item

Behavior

  • The sidebar is 240px wide and can be dragged between 200px and 400px; it keeps one width on every page that has it (Spacing).
  • A name stays on one line and ends with an ellipsis. A description after it gives up its room first; the name truncates only once the description is gone.
  • A row whose content is 2 lines (a computer's name and whether it runs agents) uses wrap with SidebarItemContent stacked: it grows from 36px, its text stays 8px from the row's edges, and its icon lines up with the first line. The name still stays on one line.
  • A collapsed group shows how many rows it holds in SidebarSectionCount.
  • While an agent works, what it is doing replaces its runtime (Working agents), in text-footnote and foreground-muted, and takes at most 58% of the row.

Content

  • Title the header with the destination's name.
  • Mark a channel with a bare # icon, a private channel with lock, and agents and people with their 19px avatar.
  • Write an agent's runtime in Space Mono after its name.

Accessibility

  • SidebarRoot renders a div. Render it as an aside or a nav and name it after the destination: aria-label="Chat".
  • SidebarItem renders the Base UI Button. For a row that navigates, render a link (render={<a href="…" />}) and set aria-current="page" on the active row: active only changes how it looks.
  • Give the unread count a name that says what it counts ("2 unread"), and give an icon-only SidebarItemMetaIcon a name ("Muted").
  • SidebarSectionDisclosure is a Base UI Collapsible trigger, which sets aria-expanded.
  • On a display-only row, focus lands only on the controls inside it.

Rules

  • Draw a row the same way in every group: original, pinned or custom. A group never changes a row (no wrapping, no hidden description in one group), because the same conversation would look like two different things.
  • Don't use wrap to let a name run onto more lines, including in a pinned group. Use it only for a row whose content is 2 lines.
  • Make every row 36px high, in text-subhead, on a desktop. Below md the row grows on its own to at least 44px in text-body at 600: there it is a phone list of places (Home, Members, the Settings list), which a finger has to hit. Don't add phone sizes at the callsite. The Settings list sat at 36px on phones because the size lived in one product class that only Home's rows were given.
  • Don't put the server switcher in the sidebar header. It is in the web top bar, or at the top of the rail when there is no top bar.
  • Don't use SidebarLiveActivity for a live bar at the bottom of the sidebar. Working agents appear in the header's working summary and in their own rows instead (Working agents).

API

SidebarBody, SidebarLiveActivity, SidebarContent, SidebarGroupLabel, SidebarHeader, SidebarHeaderTitle, SidebarItemAside, SidebarItemCount, SidebarItemCountSeparator, SidebarItemChannelIcon

Prop类型默认值
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarItem

Prop类型默认值
activeboolean
interactive`false` renders a display-only `div`: no button, no type, nothing to press. Its own controls (an action button) stay the targets.booleantrue
toneSomething waits for you (a 700 name), the place is muted (an ink-2 name), or it is away: offline or not joined (an ink-3 name).RowTone"default"
wrapFor a row whose content is two lines (a name and a second line): the row grows from its fixed height and its content aligns to the first line. The name itself stays on one line and truncates, in every group (Sidebar's rules); never use this to let a long name wrap.booleanfalse
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarItemIcon, SidebarItemIndicator

Prop类型默认值
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarItemContent

Prop类型默认值
stackedbooleanfalse
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarItemMeta, SidebarItemMetaIcon, SidebarItemStatus, SidebarItemSubtitle

Prop类型默认值
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarItemThreadCount

Thread segment of an unread count: a thread icon followed by the number of threads with new replies. Works inside SidebarItemCount or as quiet text.
Prop类型默认值
labelVisually hidden words read after the number, so screen readers hear what the count is (the icon is decorative). Pass localized copy.string"threads with new replies"
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarItemTitle

Prop类型默认值
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarList

Prop类型默认值
hiddenUntilFoundAllows the browser's built-in page search to find and expand the panel contents.booleanfalse
keepMountedWhether to keep the element in the DOM while the panel is hidden. This prop is ignored when `hiddenUntilFound` is used.booleanfalse
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarMenu, SidebarMenuAside, SidebarMenuCount, SidebarMenuIcon, SidebarMenuTitle, SidebarRoot

Prop类型默认值
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarControl

Prop类型默认值
loadingboolean
loadingLabelstring
size`md` (32px) and `icon-md` for rows, cards, banners, toolbars and panel headers; `lg` and `icon-lg` (the control height) for dialog footers, forms and beside a field; `xl` for a full-width call to action. The size follows the place: see Button › Size by place."md" | "lg" | "xl" | "icon-md" | "icon-lg" | DeprecatedButtonSize
variant"primary" | "accent" | "warning" | "danger" | "default" | "outline" | "information" | "muted" | "success" | "danger-secondary" | "danger-outline" | "ghost" | "on-frame"
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarSection

Prop类型默认值
defaultOpenWhether the collapsible panel is initially open.booleantrue
disabledWhether the component should ignore user interaction.booleanfalse
onOpenChangeEvent handler called when the panel is opened or closed.(open: boolean, eventDetails: CollapsibleRootChangeEventDetails) => void
openWhether the collapsible panel is currently open.boolean
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarSectionEmpty

Prop类型默认值
hiddenUntilFoundAllows the browser's built-in page search to find and expand the panel contents.booleanfalse
keepMountedWhether to keep the element in the DOM while the panel is hidden. This prop is ignored when `hiddenUntilFound` is used.booleanfalse
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarSectionControl

Prop类型默认值
loadingbooleanfalse
loadingLabelstring"Loading"
size`md` (32px) and `icon-md` for rows, cards, banners, toolbars and panel headers; `lg` and `icon-lg` (the control height) for dialog footers, forms and beside a field; `xl` for a full-width call to action. The size follows the place: see Button › Size by place."md" | "lg" | "xl" | "icon-md" | "icon-lg" | DeprecatedButtonSize"icon-md"
variant"primary" | "accent" | "warning" | "danger" | "default" | "outline" | "information" | "muted" | "success" | "danger-secondary" | "danger-outline" | "ghost" | "on-frame""ghost"
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarSectionControls, SidebarSectionDisclosure, SidebarSectionChevron, SidebarSectionCount, SidebarSectionHeader

Prop类型默认值
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarSectionAttention

Prop类型默认值
presenceA status light (online, busy, error, offline, external): a fixed meaning that needs its word nearby, or an aria-label. It takes the place of `variant`'s colour."online" | "busy" | "error" | "offline" | "external"
size"xs" | "sm" | "md" | "lg""md"
variant"primary" | "accent" | "warning" | "danger" | "default" | "information" | "muted" | "success"
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

SidebarSectionTitle, SidebarSectionTrailing

Prop类型默认值
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

改动

  • 0.8.1 · FixedBelow md, a SidebarItem row is at least 44px high in the 16px title type, so a phone sidebar row can be tapped without help from the app.
  • 0.6.1 · AddedSidebarItem takes tone (RowTone, shared with ListRow) and interactive={false} for a display-only row whose own buttons are the targets.
  • 0.6.0 · Breaking ChangesProps that no longer change anything are removed: Badge uppercase; Checkbox size, variant, color; Status pulse, attention (status never animates); Switch / SwitchThumb size; SidebarItem variant; SidebarItemCount / SidebarItemIndicator variant and appearance (the count is always the pink sticker, the indicator the yellow @); NotificationCenterActionButton variant; DrawerContent width (one-go forms use the wide Dialog); AppRailItemIndicator attention; AppRailItemAttention children; Steps size, track, renderIndicator, renderTraveler; FilePreviewBadge type, color. Remove them at call sites.
  • 0.5.64 · ChangedSidebarItemThreadCount draws the package's ThreadIcon (was lucide MessageSquare), at 12px with a 1.75 stroke; badge height is unchanged.
  • 0.5.60 · AddedSidebarItemThreadCount (thread icon + number of followed threads with new replies, with a visually hidden, localizable label for screen readers) and SidebarItemCountSeparator (the dot between counts), so a channel row's SidebarItemCount can read 5 · [thread] 1. Both also work as quiet text inside SidebarItemMeta for muted channels. SidebarItemCount stays as tight as a plain number when it holds the thread icon.
  • 0.5.35 · ChangedSidebarSection's attention badge no longer hides on its own while a section menu is open. The section's own open state and header hover still hide it; to also hide it while a menu is open, the header must mirror the menu's open state onto itself as data-popup-open (the docs composition shows the wiring).
  • 0.5.0 · Breaking ChangesBrutal SidebarItem active styling is neutral by default. Active items now use a white surface instead of pink. Pass variant="accent" to preserve the previous pink treatment, or variant="primary" for yellow.