The families
Every documented data-state value, painted by its family. The region's own text is the real status; the paint only reinforces it.
loading
Nothing has arrived yet.
refreshing
Content held while it reloads.
optimistic
Saved locally — server confirmation pending.
error
The request failed; retry.
rolled-back
The server rejected it; reverted to the draft.
empty
Nothing here yet.
stale
Shown, but known to be outdated.
partial
An incomplete set, not an empty one.
fresh
Updated a moment ago.
confirmed
The optimistic change stuck.
full
Deliberately unpainted — nothing to flag.
Don't show empty while loading
A region still pending never paints the empty surface. Toggle the busy signal on this empty region — aria-busy="true" wins, because "nothing here" about data that has not arrived is a lie.
No projects yet
Projects load on open — this panel stays pending until they land.
.bf-empty-state — the panel is the space
A decorative glyph, a heading, an explanation, and the way out — grid-centered, filling the parent's remaining space. The dashed frame below is the parent's definite block size; the panel inside takes all of it.
No projects yet
Create your first project, or import one from a template.
Markup
<div style="display: grid; block-size: 22rem">
<div class="bf-empty-state">
<span aria-hidden="true">◌</span>
<h3>No projects yet</h3>
<p>Create your first project, or import one from a template.</p>
<button type="button" data-variant="primary">New project</button>
</div>
</div>The bf:* event contract
Act with any surface below — switch the theme, change the tab, sort the table, remove a chip, dismiss the alert, open the toast. Every module reports what it did as a bubbling CustomEvent; the log is one document listener.
Production deploys are locked behind a manual approval.
Staging rebuilds on every push to main.
Preview environments expire after 48 hours.
| api | 12 |
| web | 3 |
| worker | 5 |
Stack
css js a11y
Deploy failed.
Deploy succeeded.
Event log
Listener
document.addEventListener(
"bf:themechange" /* … */,
(e) => log(e.type, e.detail)
);