Pattern: RF task menu
Who uses it: a warehouse floor worker, first screen
after signing into the handheld — every RF shift starts here.
What "done" looks like: the next task is one tap away,
with 3-6 items visible at once, never a menu to search. Not
for a general app launcher with dozens of destinations — that's
App launch, which this reuses
the mechanism from at a different scale: same .bo-widget-grid
container-query grid, glove-sized tiles instead of icon tiles, and
data-density="spacious" for one-handed, often-gloved use. An
RF device runs a handful of tasks per shift, not a suite — this menu
stays that short on purpose (roadmap 109.7).
The screen — try it
Shown the way the scanner gets it: an isolated 360×640 document styled
by ONLY the rf-essentials profile (roadmap 59.2,
Chrome/WebView 108) — not the docs bundle. One screen, not two: this
page rendered the same component a second time at desktop width until
roadmap 135.2.
What is in the profile — all 14 of them. dashboard/dashboard (the source of .bo-widget-grid/
.bo-widget) joined the profile for this pattern (109.7) —
its @container queries need Chrome 105+, already inside this
profile's 108 floor, so no guard work was needed.
Markup
<div class="bo-widget-grid" style="--bo-widget-min: 8rem" data-density="spacious">
<a href="/receive" class="bo-widget" style="text-align: center; padding-block: var(--bo-space-5)">
<span style="font-size: var(--bo-font-size-lg); font-weight: var(--bo-font-weight-semibold)">Receive</span>
<span class="bo-badge" aria-label="4 open">4</span>
</a>
<!-- one .bo-widget per task -->
</div> Anatomy
- Task grid —
.bo-widget-gridwith a raised--bo-widget-minfor glove-sized tap targets; auto-fills the 360px screen at 3-4 tiles. - Task tile —
.bo-widgetas a link; label + an open-count.bo-badge, nothing else — an RF worker already knows the task by name, so no icon set is pulled into the profile for it (grill verdict below). - Zero-state tile (Count, above) — a 0 count still renders the tile; it does not disappear, so the menu's shape never shifts mid-shift.
Data contract
| Request | Returns / on failure |
|---|---|
| GET /rf/tasks | one entry per task type with its open count; 401 → redirect to login, never a silent empty menu |
| GET /rf/tasks/:type | the queue for that task (see RF task queue); 204 for an empty queue, not a 200 with an empty array a client has to interpret |
States
| State | What ships |
|---|---|
| Loading | tiles render immediately with a skeleton count; never block the whole grid on one slow endpoint |
| Zero open | the tile stays, badge reads 0 — see Count above; a vanished tile reads as "not my job today," which is wrong |
| Session lost | the reconnect state: the menu re-renders from the server when the link returns. Cached menus and stale-data cues are an APP/infrastructure layer (the reference products sell session persistence as a server product) — this framework ships the visuals a reconnect needs, and promises nothing it does not ship |
| Permission-scoped | a task type the worker's role can't run is omitted from the grid, not shown disabled — RF screen real estate at 360px is too scarce for a tile nobody can use |
| No JS | fully functional — this page has no JS dependency; each tile is a real <a href> (native navigation to the task queue), and the page imports no @busy-office/ui/js behavior |
Components used
complexity 1 of 4