Status bar
tier 1 — the frameOne strip across the bottom of the window that says the app is not working normally — the circuit dropped, the session expired, something threw. One class family for every case.
Status bar
All five modifiers are shown together here; in an app one shows at a time. The bar's own
controls are .status-bar-action — filled, and never a .btn, whose
tint disappears on a crimson bar — .status-bar-dismiss for the error bar's
close button, and .status-bar-attempt for the retry counter, hidden until
Blazor has put a number in it.
| Modifier | What it says |
|---|---|
.status-bar--reconnecting | the circuit dropped and is being re-established — the only one that pulses |
.status-bar--paused | the server released the circuit and kept the state |
.status-bar--failed | reconnecting is over; only a reload helps |
.status-bar--expired | the session is gone from the server |
.status-bar--error | an unhandled error reached the browser |
<div class="status-bar status-bar--reconnecting">
<i class="ri-wifi-off-line"></i>
<span>
Connection lost. Reconnecting…
<span class="status-bar-attempt">attempt 3 of 8</span>
</span>
</div>
<div class="status-bar status-bar--paused">
<i class="ri-pause-circle-line"></i>
<span>Paused. Your work is held on the server.</span>
</div>
<div class="status-bar status-bar--failed">
<i class="ri-close-circle-line"></i><span>Could not reconnect.</span>
<button class="status-bar-action" type="button"><i class="ri-refresh-line"></i> Retry</button>
</div>
<div class="status-bar status-bar--expired">
<i class="ri-error-warning-line"></i><span>This session has expired on the server.</span>
<button class="status-bar-action" type="button"><i class="ri-refresh-line"></i> Reload</button>
</div>
<div class="status-bar status-bar--error">
<i class="ri-alert-line"></i><span>An unhandled error has occurred.</span>
<button class="status-bar-action" type="button"><i class="ri-refresh-line"></i> Reload</button>
<button class="status-bar-dismiss" type="button" aria-label="Dismiss this message"><i class="ri-close-line"></i></button>
</div>
The status bar in the host page
Paste this into App.razor, inside <body> and before the
component that carries the render mode; supply it, or Blazor injects an unstyled box of its
own. The bar is a child of the id, never the id itself —
#components-reconnect-modal is the container Blazor sets a state class on, and
you write the rows while the framework sets those classes:
| Blazor sets | The row that shows |
|---|---|
components-reconnect-show | .status-bar--reconnecting |
components-reconnect-retrying | .status-bar--reconnecting, with its counter revealed |
components-reconnect-paused | .status-bar--paused, or the reconnecting row if there is none |
components-reconnect-failed | .status-bar--failed |
components-reconnect-resume-failed | .status-bar--failed |
components-reconnect-rejected | .status-bar--expired |
components-reconnect-hide | none — connected |
retrying is added alongside show and only reveals the
counter; .status-bar--paused is optional and falls back to the reconnecting
row, but the other three are not — omit one and that state renders as an empty strip. The
reload buttons are plain onclick handlers: the circuit is gone in those
states, so a Blazor handler cannot run.
<div id="components-reconnect-modal">
<div class="status-bar status-bar--reconnecting">
<i class="ri-wifi-off-line"></i>
<span>
Connection lost. Reconnecting…
<!-- Blazor fills these two by id, if they are there. -->
<span class="status-bar-attempt">
attempt <span id="components-reconnect-current-attempt">1</span>
of <span id="components-reconnect-max-retries">8</span>
</span>
</span>
</div>
<div class="status-bar status-bar--paused">
<i class="ri-pause-circle-line"></i>
<span>Paused. Your work is held on the server.</span>
</div>
<div class="status-bar status-bar--failed">
<i class="ri-close-circle-line"></i><span>Could not reconnect.</span>
<button class="status-bar-action" type="button" onclick="location.reload()">
<i class="ri-refresh-line"></i> Retry
</button>
</div>
<div class="status-bar status-bar--expired">
<i class="ri-error-warning-line"></i><span>This session has expired on the server.</span>
<button class="status-bar-action" type="button" onclick="location.reload()">
<i class="ri-refresh-line"></i> Reload
</button>
</div>
</div>
The unhandled-error bar
#blazor-error-ui is the second place the bar mounts, and the framework reveals
it by setting display: block inline. Put .status-bar--error on
the strip inside the id and never on the id itself: that inline declaration beats
every rule in every layer, so this one bar would lay out as inline text while the reconnect
rows stayed flex rows.
<!-- Blazor's own element, and the second place the status bar mounts. The framework
reveals it by setting `display: block` inline on an unhandled circuit error.
The id is the CONTAINER and `.status-bar` is the strip inside it. An inline
`display: block` beats every rule in every layer, so a `.status-bar` on the id
itself would lay this one bar out as inline text while the reconnect rows stayed
flex rows.
Both controls are plain `onclick` handlers, for the same reason the reconnect
rows' are: the circuit is gone by the time this bar is visible, so a Blazor
handler cannot run. The dismiss is a <button>, not the project template's <span>:
it is a control, and it should be focusable and announced as one. -->
<div id="blazor-error-ui">
<div class="status-bar status-bar--error">
<i class="ri-alert-line"></i>
<span>An unhandled error has occurred.</span>
<button class="status-bar-action" type="button" onclick="location.reload()">
<i class="ri-refresh-line"></i> Reload
</button>
<button class="status-bar-dismiss" type="button" aria-label="Dismiss this message"
onclick="this.closest('#blazor-error-ui').style.display='none'">
<i class="ri-close-line"></i>
</button>
</div>
</div>