ComponentsLayout

Selection Slot

Shows your normal tools until something is selected. Then it shows the selection toolbar in the same place. The row keeps its height, so nothing below it moves.

Selection toolbar swap

A button selects sample records and the toolbar becomes the selection. Clear returns focus to the button that opened it.

2 selected

Table query bar

Select rows to swap the table tools for bulk actions. Below 36rem the actions fold into a menu. Board and List have no row selection, so the tools come back.

CustomerStatusTotal
Amara OseiPaid$1,240.00
Lukas BrandtPending$86.50
Priya NatarajanRefunded$312.99
Mateo AlvarezPaid$58.00

Files toolbar

Selecting files or folders swaps the filters for a toolbar of actions. File-only actions appear when a file is selected. Below 44rem the actions fold into a menu. Trash keeps the filters even with a selection.

  • Q3 planningSep 30
  • Supplier contract 2026.pdfOct 12
  • Harvest yield forecast — greenhouse north and south wings.xlsxOct 15
  • Brand photosAug 2

Usage

Selection Slot usageTSX
import { SelectionSlot } from "@tulipfarm/sprout/selection-slot";

<SelectionSlot
  active={selected.length > 0}
  selection={
    <div role="toolbar" aria-label="Selected items" className="flex items-center gap-2">
      <p role="status" className="min-w-0 flex-1 truncate">{selected.length} selected</p>
      <Button variant="outline" size="sm">Move</Button>
      <Button variant="ghost" size="sm" aria-label="Clear selection" onClick={clear}>
        Clear
      </Button>
    </div>
  }
>
  <FilterBar />
</SelectionSlot>

active chooses what shows. children are the tools for no selection, such as search and filters. selectionis the toolbar for selected items. Other props, such asclassName and ref, go to the outerdiv.

There are no variants or sizes. The slot has no padding, border, or color. Its content sets the look, so it fits any toolbar.

Optional selection

Selection only in the table layoutTSX
const selection = layout === "table" && rows.length > 0
  ? <BulkRecordActions records={rows} clear={clear} />
  : undefined;

<div className="min-w-0 flex-1">
  <SelectionSlot active={!!selection} selection={selection}>
    <fieldset disabled={editing} aria-label="Table tools">…</fieldset>
  </SelectionSlot>
</div>

selection can be undefined whileactive is false. Put the slot in a min-w-0 flex-1wrapper when it shares a row with other controls. A disabledfieldset in the tools still works, and so do disabled or busy buttons in the selection.

Fit the width

Fold actions into a menu in narrow slotsCSS
.selected-wide { display: none; }

@container sprout-selection (width >= 36rem) {
  .selected-wide { display: flex; }
  .selected-menu { display: none; }
}

/* Tailwind: hidden @xl/sprout-selection:flex */

The slot is a size container named sprout-selection. Use it to show buttons when the slot is wide and a "Selected actions" menu when it is narrow. The examples above switch at 36rem for tables and 44rem for files.

Focus

When the selection opens, the slot remembers the focused element. If focus was in the tools, which are now hidden, it moves to the first control in the selection. If focus was somewhere else, such as a row checkbox, it stays there.

When the selection closes and its focused button goes away, focus goes back to the remembered element, if it is still on the page. Clear returns you to the checkbox or button you started from.

Accessibility

The hidden tools stay mounted, so their state survives. They areinert and aria-hidden, so Tab, clicks, and screen readers skip them. Give the selection a role and name, such asrole="toolbar" with aria-label="Selected items", and put the count in role="status" so it is announced. Give an icon-only or short button, such as Clear, anaria-label.

The selection fades in over 180ms. The tools hide at once, so the two never overlap. The fade stops when reduced motion is requested.

Theme tokens

The slot has no color of its own. The fade uses--sprout-motion-normal and --sprout-motion-ease.