Skip to content
Barua UI v0.2
Patterns

System Pages

The full-page states every app needs but nobody designs first — errors, maintenance windows, empty lists, loading screens and first-run onboarding. One calm scaffold (.b-syspage), one outcome panel (.b-result) and an onboarding stack, all composed from components you already know.

Error Page Anatomy

Every error page is the same four pieces stacked on .b-syspage, a flex column that centers itself in the viewport: .b-syspage__code — the oversized status number wearing the accent-to-indigo gradient — .b-syspage__title for the human headline, .b-syspage__desc for one or two sentences on what actually happened, and .b-syspage__actions, a button row with exactly one primary way out. Swap the code for an icon and the same scaffold covers maintenance and any other full-page interruption.

Say what happened in plain language and offer one clear next step — go home, try again, request access. The gradient number supplies the drama; the copy stays calm.
Blame the user, print a stack trace, or dead-end the page. An error page with no action — or eight of them — strands people at the exact moment they need a way out.

404 Page

The classic. Keep the description honest — most 404s are stale links, not user error — and route people home or to a human. The demo pins min-height: 0; the real page keeps the default 60vh so it centers in the viewport.

404
Page not found

The page you're looking for was moved, renamed, or never existed. Check the address, or head back somewhere familiar.

Accessibility. Error pages are real pages: give them a descriptive <title> ("Page not found — Barua"), make .b-syspage__title the page's only <h1>, and render the scaffold inside a <main> landmark so screen-reader users land straight on the explanation. Return the real HTTP status too — the semantics live in the response, not the visuals.

403 Page

Denied, not broken. Offer the two exits people actually want: request access, or hop identities. Pair "Switch account" with the Account Switcher from Authentication so nobody has to sign out just to sign back in.

403
Access denied

Your account doesn't have permission to view this workspace. Ask an admin to let you in, or try a different account.

500 Page

Own it. Say the fault is on your side, promise the data is safe, and hand over a reference code support can actually use — a plain <code> line tucked under the actions.

500
Something broke on our side

An unexpected error stopped this request. It's not you — your mail is safe, and the team has already been paged.

View status page

Reference: ERR-2AF4-9C01

Maintenance Page

The same scaffold minus the status code — an icon where the number would be, a concrete time window, and an indeterminate .b-progress so the page feels alive rather than abandoned.

Scheduled maintenance

Barua is getting a planned upgrade between 02:00 and 04:30 UTC. Mail keeps arriving the whole time and will be waiting when we're back.

Started 02:00 UTC · Live updates on the status page

Offline State

A .b-result--warning panel, not a dead end — reassure people their drafts are safe locally. Toggling it (listening to navigator.onLine and the online / offline events) is app-side wiring; Barua ships the look.

You're offline

Barua can't reach the network. Drafts are saved on this device and will send themselves the moment you're back online.

Permission State

Ask in context, right before the capability is used. A .b-result--confirm panel primes the real browser prompt — explain the payoff first and the OS dialog stops feeling like an ambush.

Turn on notifications?

Get a ping the moment important mail lands — VIP senders and mentions only, never the newsletter flood.

Loading State

Two tools, both from Feedback: a centered spinner when you don't know the shape of what's coming, skeletons when you do. Full-page loading is a .b-spinner--lg plus one quiet line of .b-footnote text — never a paragraph.

Loading your inbox…

Skeleton page

When the layout is known, ghost it instead: compose .b-skeleton variants inside the real containers so nothing jumps when content arrives.

Accessibility. Mark the region being replaced with aria-busy="true" while it loads and remove it when content arrives. Announce the transition through a polite live region — role="status" or aria-live="polite" — with a short label like "Loading your inbox". Skeletons are decorative; hide them from assistive tech with aria-hidden="true" rather than labelling each shimmering bar.

Empty State

The component lives in Content — this is the pattern. On first run, render .b-empty inside the exact container the content will eventually fill, with the creation action right there instead of hidden in a toolbar.

Rules
No rules yet

Rules file, label and archive mail before it ever hits your inbox. Start with one for newsletters — future you says thanks.

Onboarding

The pitch page. .b-onboarding__hero gives one word the gradient treatment via <em>, a single primary CTA leads, and at most three .b-onboarding-feature rows carry the argument — icons from .b-icon-tile.

Email that finally feels effortless

One inbox for every account, triaged before you even open it. No folders to invent, no zero to chase.

Smart triage
People, receipts and newsletters sort themselves into calm, separate lanes.
Instant search
Any message, attachment or contact in milliseconds — even offline.
Private by default
Indexing happens on your device. Nobody reads your mail to sell you shoes.

Welcome Screen

The Apple-style first-launch sheet: app icon up top (.b-auth-card__logo), a "Welcome to" hero, a short feature list, and a single Continue button in a .b-form-actions footer. In a real sheet, pin that footer to the bottom edge so Continue never scrolls away.

Welcome to Barua
All of your accounts
Work, personal and that address from 2011 — unified in one inbox, color-coded per identity.
Fast everywhere
Full offline sync means the plane, the tunnel and the dead zone all feel the same.
You're in control
Trackers stripped, images proxied, and one switch to turn any of it back on.