← All proposals

LabeledDivider

Net-new LabeledDivider: a horizontal rule with a pill-shaped label centered on it.

Net-new componentOpen in Storybook

proposals/LabeledDivider/HANDOFF.md96 lines · View on GitHub

Handoff: LabeledDivider

What and why

A horizontal rule with a pill label centered on it, to mark a point in a stream: a date, a jump in time or a state change such as "Zappy engaged". PDS v2 has no divider and nothing that labels a break in a stream, so event logs, runtime and edge logs, threads and agent timelines draw these by hand. The label can be swapped for any control with the action slot (2026-10-21; for example a "Show more" Button that unfolds content under the divider, the screen owns the control and the content), or optionally become a time selector: a MenuButton with a chevron that opens a menu to jump in time, with an optional last item that opens the PDS Datepicker in a Modal. Tones: neutral, agentic (AI gradient border, lines and bolt, with shimmer) and agentic-filled (discovery fill and border; two stylings under review). Figma: https://www.figma.com/design/AMGWJbraJoi2PKWnxo29Ah/PDS-Proposals?node-id=374-52, page Labeled Divider (4 stories, light only; captures of Storybook with library icons swapped in, sent 2026-10-20). Context: context.md.

Exploration and decisions

  • Named LabeledDivider because it is use-case neutral (StreamDivider, TimelineDivider and EventDivider each fit only some uses). If a plain Divider is added later, this can stay as the labeled form.
  • The time selector reuses the local MenuButton (secondary, same size) between the two lines instead of a new menu. The chevron means a time selector only, so there is no disclosure mode. The consumer owns the options and callbacks.
  • "Jump to a specific date" uses the PDS Datepicker inside the PDS Modal, because Datepicker has no inline or controlled-open mode. The date takes two steps (click the field, then a day).
  • Icons are prefixIcon and suffixIcon props, using the local Icon wrapper.
  • Sizes xs and s (default), named after the type tokens. A medium size was dropped as too large.
  • Tones: accent was removed. agentic-filled (the purple discovery fill) was removed 2026-10-21; agentic (the gradient) is the one agentic tone. A zappy icon on the agentic tone is the plain foreground color (black); the gradient bolt was removed 2026-10-21.
  • Shimmer: the PDS shimmer band is an extra background layer on the agentic border and lines, using the package keyframes. Off for reduced motion. The label text and the selector's border do not shimmer.
  • Type uses the semantic pds-copy-s / pds-copy-xs classes on the root, not font primitives.

Full list and reasons: decisions.md.

API

LabeledDivider (a div; extends div props):

Prop Type Default Purpose
className string none Additional class names.
datePicker LabeledDividerDatePicker none Adds a separator and a last menu item that opens a Datepicker in a Modal. Only used when menuItems is set.
id string none ID of the trigger button. Required for the selector (with menuItems).
action ReactNode none Swaps the label for any control, centered on the line (for example a "Show more" Button). The screen owns it, aria-expanded and the content it reveals. Ignores label, menuItems and the icons; the root is a group.
label string none (required) Text on the divider. Announced to screen readers on the static form.
menuItems MenuButtonProps['menuItems'] none Turns the label into a selector button with a chevron that opens this menu. Needs id.
prefixIcon IconProps['iconName'] none Icon before the label.
size 'xs' | 's' 's' Text size, icon size and label padding; the selector's button takes the same size.
suffixIcon IconProps['iconName'] none Icon after the label (before the chevron on the selector).
tone 'neutral' | 'agentic' 'neutral' agentic: AI gradient border, lines and shimmer. The selector always uses the gradient border when the tone is not neutral.

LabeledDividerDatePicker: menuLabel (required, the menu item text), modalTitle (required), onChange(date) (required; the modal closes after a pick), pickerLabel (required, the field label), maxDate and value (optional).

Variants and states: sizes xs / s; tones neutral / agentic; static, selector or an action slot (a swapped-in control such as "Show more"); with or without icons; with or without the date picker. Stories: Default, With time selector, With action, Agentic, All sizes and variants. If menuItems is set without id, it renders the static form and ignores menuItems and datePicker.

Tokens used

  • --pds-animation-duration-ai-shimmer
  • --pds-border-radius-button
  • --pds-border-width-default
  • --pds-color-border-separator
  • --pds-color-foreground-default
  • --pds-color-status-discovery-background
  • --pds-color-status-discovery-border
  • --pds-color-status-discovery-foreground
  • --pds-color-surface-default
  • --pds-gradient-ai-active
  • --pds-gradient-ai-shimmer
  • --pds-spacing-2xs
  • --pds-spacing-3xs
  • --pds-spacing-4xs
  • --pds-spacing-m
  • --pds-spacing-s
  • --pds-spacing-xs

Also pds-copy-s / pds-copy-xs for type. --pds-spacing-l appears only in labeled-divider-story.css (story layout), so it is left out.

Impact

From impact.md: no existing PDS component is changed and no --pds-* token is redefined. One opt-in addition: .pds-labeled-divider__selector .pds-button (gap), which applies only inside the divider. The impact report lists LogsTableWithDivider as building on it. EventLogWithAgent also imports it in its stories (the report does not list it). Neither is marked ready.

Reused vs net-new

  • Reused: local MenuButton (the selector, secondary, agentic when the tone is not neutral), local Icon wrapper, PDS Modal and Datepicker (from the package), the pds-copy-* classes, PDS gradient, shimmer and status tokens.
  • Net-new: .pds-labeled-divider (--agentic, --xs, __icon, __label, __line, __selector); all props; custom CSS variables --labeled-divider-label-background, -label-border, -label-color, -line-color, -padding-block, -padding-inline. No raw values kept. Details in custom.md.

Accessibility

  • Static form: role='separator' with aria-label from label; the visible pill and both lines are aria-hidden, so the label is announced once.
  • Selector form: not a separator. It is the MenuButton (a real button with its own menu semantics and keyboard handling); the lines are aria-hidden. Needs id.
  • The date modal and Datepicker are the PDS components, with a title and a labelled field from the datePicker props.
  • The shimmer stops under prefers-reduced-motion.
  • The a11y panel has not been run, and light and dark were not checked by eye in a session.

Decisions and open questions

  • Is a modal the right container for the date picker, or a popover anchored to the divider? The Datepicker has no inline or controlled-open mode today, so the pick takes two steps.
  • Is a vertical orientation needed for left-to-right timelines?
  • Should the lead icon show on the selector in the neutral tone? It is supported; confirm it looks right.
  • Check that the MenuButton height at each size matches the static pill.
  • Silent fallback: menuItems without id renders the static divider. Should the port warn or require id by type?
  • Dependencies: MenuButton (and through it Button, Icon, IconButton, Spinner) and Icon are all marked ready. AgentAvatar appears in the dependency graph but is not imported by this component. Nothing it needs is unready.

Port notes

Would live in src/components/ (a dividers or layout category folder; generator npm run generate:component). Not yet matching v2: the local mergeClasses stand-in (use @libs/components/utils); the local MenuButton and Icon wrappers (use the ported ones); Modal and Datepicker already come from the package. Relies on the package pds-ai-shimmer-sweep keyframes being loaded. pds-copy-* is !important, so a rule on the same element cannot override size or weight.

Preview

npm run storybook, story Component Proposals / Labeled Divider. Figma: https://www.figma.com/design/AMGWJbraJoi2PKWnxo29Ah/PDS-Proposals?node-id=374-52, page Labeled Divider (4 stories, light only; captures of Storybook with library icons swapped in, sent 2026-10-20).