Skip to main content

The problem

A reader looking at a table of a hundred campaigns doesn’t want to delete, export, or tag them one at a time. They want to check the ones they mean and act on all of them at once. Without a deliberate pattern for this, every team either repeats a row of action buttons on every row — expensive to scan and expensive to click fifty times — or bolts a bespoke selection model onto whichever table needed it first. Selection is also where ambiguity becomes expensive. “Select all” over a table sitting behind an active filter has at least three plausible meanings: every row on the current page, every row matching the filter, or every row that exists. Get the scope wrong on an export and the reader gets a stale file. Get it wrong on a delete and the reader loses data that was never on screen. This pattern is not the checkbox. The pattern is the bar that appears once something is selected, and the rule for what “select all” is allowed to mean.

Deciding what select-all means

The header checkbox never silently means more than the page. Titan’s own design library already answers this — a menu on the header checkbox with Select all n items, Select all on page, and Deselect all — none of it exists in code yet (see Known issues); this pattern is that menu’s specification, not a competing proposal. Where the question feels genuinely open, it is only the implementation that’s undecided — the scope distinction itself is design intent already recorded.

Vocabulary

When this applies

  • A reader needs to act on more than one row at a time, on the same data type, with the same action.
  • The table or list is long enough that per-row controls would need to be pressed repeatedly to get the same result.
  • The action — export, tag, archive, delete — makes sense applied to a batch, not only to one record.

When it doesn’t

Structure

Behavior

Constraints

Content

Accessibility

  • The selection count is a live region, announced politely on change. A keyboard user checking rows gets no other confirmation that anything happened — see Table’s own accessibility section.
  • The bar appearing does not steal focus. Focus stays on the checkbox the reader just pressed.
  • Each row checkbox’s accessible name identifies the row: “Select row: Q3 Paid Search,” not “Checkbox.”
  • The header checkbox’s accessible name states its scope explicitly — “Select all on this page” — rather than the generic “Select all,” since what it selects is exactly the fact this pattern has to disambiguate.
  • Bulk action buttons are reachable in the same tab order as the rest of the toolbar; the bar does not trap focus.

Variations

Anti-patterns

Per-row action buttons repeated down every row. Cheap to build, expensive for anyone who has to click the same button fifty times to do one job. This is the exact case this pattern exists to replace. A “select all” that quietly means everything, including rows never shown. A reader who checked a header box expecting “this page” and got “every campaign in the account” has just run a bulk action on data they never saw. Scope has to be explicit, especially for anything destructive. Selection that survives a filter change unannounced. A reader who filtered to “Active,” selected four rows, then cleared the filter still has four rows selected — rows that may no longer even be visible. Acting on that selection now applies to a set the reader can’t see and didn’t choose in this view. Treating a partial bulk failure as a full success or a full failure. Clearing the whole selection because most of it worked hides the two rows that didn’t; failing the whole batch because two rows errored discards eighteen that succeeded. Either one makes the reader redo work that already happened correctly.

Known issues

Gaps in the current rules

  • What “select all N matching” should do if N is large enough that running the action synchronously would time out — Variations gestures at a background job without naming a threshold.
  • Whether a retry after a partial failure should re-fetch the rows first, in case they changed between the first attempt and the retry.
  • Whether selection should ever persist across a page reload within the same session, rather than clearing unconditionally.

Why it works this way

The header checkbox is scoped to the page, never to everything, because “select all” over a filtered table has no single obvious meaning. The larger scope is a separate, explicitly-labeled action rather than an implicit consequence of the same click — a reader who wanted the page gets the page, and a reader who wanted everything has to say so. Selection clears on any change to the underlying result set because a selection is a claim about specific rows, not a count. A changed filter is a changed context entirely — carrying four selected rows across a filter change silently redefines what “those four” refers to, and the reader never agreed to that redefinition. Table and List, the two containers this pattern selects from. Checkbox, the row and header controls. Destructive confirmation, for the bulk-destructive variation a destructive batch action routes through. Filtering & search, for the narrowing that precedes selection and clears it on change.

Status

Exemplar page — first pass. This is a proposal for review, not established policy. No Titan design-library review or shipped Invoca screen backs this page — it is built from general interaction-design practice and from constraints Table, List, and Checkbox already establish. Two real, current limits on what this pattern can promise — not workarounds this page invents around them — are recorded in Known issues rather than restated here.
Last modified on September 17, 2026