Workflow board
.c-workflow-board provides the CSS contract for BPM and SaaS work queues:
lanes, swimlanes, WIP/count metadata, work cards, selected cards, dragging
cards, and drop-target lanes. Porchlight styles the board only. Your app owns
drag/drop behavior, ordering, permissions, live updates, and persistence.
Semantic HTML
<section class="c-workflow-board" aria-labelledby="work-title">
<div class="c-workflow-board__header">
<div>
<h2 class="c-workflow-board__title" id="work-title">Claims queue</h2>
<p class="c-workflow-board__meta">42 open items</p>
</div>
</div>
<section class="c-workflow-board__swimlane" aria-labelledby="lane-retail">
<div class="c-workflow-board__swimlane-header">
<h3 class="c-workflow-board__swimlane-title" id="lane-retail">
Retail banking
</h3>
<span class="c-workflow-board__swimlane-meta">16 requests</span>
</div>
<div class="c-workflow-board__lanes" data-scroll="inline">
<section class="c-workflow-lane" aria-labelledby="lane-new">
<header class="c-workflow-lane__header">
<div class="c-workflow-lane__heading">
<h4 class="c-workflow-lane__title" id="lane-new">New</h4>
<span class="c-workflow-lane__count">8</span>
</div>
<span class="c-workflow-lane__wip" data-state="warning"
>WIP 8 / 10</span
>
</header>
<div class="c-workflow-lane__body">
<article class="c-workflow-card" data-selected>
<div class="c-workflow-card__header">
<h5 class="c-workflow-card__title">KYC exception review</h5>
<span class="c-badge" data-tone="warning">SLA</span>
</div>
<p class="c-workflow-card__body">
Missing beneficial owner attestation.
</p>
<div class="c-workflow-card__meta">
<span>REQ-1048</span>
<time datetime="2026-06-29T15:30:00-05:00">3:30 PM</time>
</div>
</article>
</div>
</section>
</div>
</section>
</section>
State hooks
| Selector or attribute | Purpose |
|---|---|
.c-workflow-board__lanes[data-scroll="inline"] | Opt into horizontal lane scrolling when the app wants stable columns. |
.c-workflow-lane[data-drop-target], .is-drop-target | Highlight a lane as the current drop target. |
.c-workflow-lane[aria-disabled="true"], [data-disabled], .is-disabled | Show a lane that cannot accept changes. |
.c-workflow-lane__wip[data-state="warning"] | Warn that the WIP limit is near. |
.c-workflow-lane__wip[data-state="danger"] | Show an exceeded WIP limit. |
.c-workflow-card[data-selected], .is-selected | Show the selected/focused work item. |
.c-workflow-card[aria-selected="true"] | Selected state only when the card has a role that supports aria-selected. |
.c-workflow-card[data-dragging], .is-dragging | Show the item currently being dragged. |
.c-workflow-card[data-overdue], [data-priority="high"] | Add danger emphasis for urgent work. |
Accessibility responsibilities
Use real headings, sections, buttons, links, and forms for the workflow surface.
If the board is keyboard-reorderable, your app must provide the keyboard model,
focus management, and announcements. Use an aria-live region for changes such
as “Moved REQ-1048 to Underwriting” or “New task added to Appeals.” Keep
aria-selected, aria-disabled, and any counts in sync with application state.
For drag/drop, Porchlight only exposes visual state hooks. Apps should not rely on CSS classes as the source of truth for ordering or permissions.
SSR, hypermedia, and frameworks
The contract is rendered HTML. Server-rendered pages can emit the current board state directly. Hypermedia apps can swap a lane or card fragment and preserve the same classes. Component frameworks can map internal state to the same attributes without React-, Vue-, Alpine-, or htmx-specific selectors.
Preview
See the workflow board preview.