# ChannelSegments

> The Unibox channel filter: a rail of icons where one shared pill glides to the selected channel, and only that segment spells out its name.

## Facts

- **Product**: Max
- **Family**: Unibox
- **Global**: `window.DigitalCrew.ChannelSegments`
- **Source**: `max-agent: src/features/unibox/ui/components/channel-segments.tsx`
- **Import in the app**: `import { ChannelSegments } from "@/features/unibox/ui/components/channel-segments";`
- **Live preview**: /design/components/channel-segments/preview.html
- **Page**: /design/components/channel-segments

## Guidelines

**Use it for** switching the conversation list between All, Mail, LinkedIn, WhatsApp and Meetings. It fits any width, down to the 224px tablet rail.

**What you provide**
- `options`: `ChannelSegmentOption[]`, each `{ value, label, icon, needsAttention?, hint? }`.
  - `value` is one of `"all" | "email" | "linkedin" | "whatsapp" | "meeting"`.
  - `needsAttention` means an account stopped syncing.
  - `hint` is the tooltip's second line, such as "2 accounts".
- `value`, `onChange` and `className`.

In the app, only channels the workspace actually has get a segment.

**Anatomy**
- The rail is `rounded-full border border-border/60 bg-muted/50 p-1 backdrop-blur-sm` with `gap-1`.
- Segments are `h-8 rounded-full px-2.5 text-xs font-medium` with `size-3.5` icons, `text-muted-foreground` when inactive.
- The selected segment takes the leftover width (`flex-1`) on a `bg-background shadow-sm ring-1 ring-border/60` pill.
- `needsAttention` adds a `size-1.5 bg-amber-500` dot.

**Motion**: the pill (`layoutId`) and the expanding label move on `SPRING_GLIDE`, and the label exits in 0.12s. Reduced motion sets durations to 0.

**Keyboard**: the ARIA tabs pattern. Arrow keys, Home and End select, and the rail scrolls itself rather than the page.

**Don't** put counts inside the segments, and don't mount two rails at once: they share the pill's `layoutId`.

## API

```ts
export interface ChannelSegmentOption {
    value: ChannelFilter;
    label: string;
    icon: LucideIcon;
    /**
     * An account behind this channel stopped syncing. The dot turns "this
     * channel looks empty" into "this channel has gone quiet, and here is why"
     * — the reconnect link itself lives in the alerts banner above the list.
     */
    needsAttention?: boolean;
    /** Second line of the tooltip, e.g. "2 accounts". */
    hint?: string;
}
interface ChannelSegmentsProps {
    options: ChannelSegmentOption[];
    value: ChannelFilter;
    onChange: (value: ChannelFilter) => void;
    className?: string;
}
export declare function ChannelSegments({ options, value, onChange, className, }: ChannelSegmentsProps): JSX.Element;
```

## Example

```html
<div id="root" class="p-6"></div>
<script>
(function () {
  var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
  var OPTIONS = [
    { value: "all", label: "All", icon: I.Inbox },
    { value: "email", label: "Mail", icon: I.Mail, hint: "2 accounts" },
    { value: "linkedin", label: "LinkedIn", icon: I.Linkedin, hint: "1 account" },
    { value: "whatsapp", label: "WhatsApp", icon: I.MessageCircleMore, hint: "1 account, 1 needs reconnecting", needsAttention: true }
  ];
  var COUNTS = { all: 128, email: 74, linkedin: 41, whatsapp: 13 };
  function App() {
    var st = React.useState("all"), value = st[0], setValue = st[1];
    var touched = React.useRef(false);
    React.useEffect(function () {
      // Glide once on load so the pill and the expanding label are visible.
      var t = setTimeout(function () { if (!touched.current) setValue("linkedin"); }, 700);
      return function () { clearTimeout(t); };
    }, []);
    var current = OPTIONS.filter(function (o) { return o.value === value; })[0];
    return h("div", { className: "mx-auto flex w-[340px] flex-col gap-2" },
      h(DC.ChannelSegments, { options: OPTIONS, value: value, onChange: function (v) { touched.current = true; setValue(v); } }),
      h("div", { className: "flex items-center justify-between px-1 text-[11px] text-muted-foreground" },
        h("span", { className: "tabular-nums" }, COUNTS[value] + " conversations"),
        h("span", null, current.hint || "3 channels, 4 accounts")));
  }
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>
```

## More in Unibox

- [ChatListItem](/design/components/chat-list-item.md): One conversation in the Unibox list: a motion row that staggers in, glides when a new reply reorders the inbox, and pops its unread badge.
- [MessageBubble](/design/components/message-bubble.md): One message in a Unibox thread: your side in the brand gradient, theirs in muted glass, each rising in from its own side of the conversation.
- [MessageComposer](/design/components/message-composer.md): The Unibox reply box: text, emoji and staged images in one rounded field, with a send button that springs awake when there is something to send.
- [ReplySuggestions](/design/components/reply-suggestions.md): The band between a Unibox thread and its composer: ask for a few replies, watch them arrive, pick one.
- [SyncProgressPanel](/design/components/sync-progress-panel.md): The dialog behind the Unibox sync button.
- [UniboxIdlePanel](/design/components/unibox-idle-panel.md): The Unibox's empty conversation pane: a breathing glass orb with the three channels in slow orbit and a quiet "Listening for new replies" line.
