Mobile
iOS-feeling patterns for the small screen — glass chrome, safe areas, 44pt targets. Headers collapse from large titles, tab bars blur the content behind them, and rows swipe to reveal actions.
Mobile Header
.b-mobile-header is the sticky glass chrome at the top of
a mobile view: a three-column __bar with a
.b-back-btn at the start, a centered __title
and an __action--end slot. One note on the demos on this
page: mobile chrome is normally position: fixed or
sticky, so each demo sits inside a phone-width frame and
the component gets an inline position: static override to
render inside it — drop the override in a real app.
Large title
Add __large-title under the bar for the iOS large-title
pattern; on scroll, collapse it and reveal the small centered title.
Bottom Tab Bar
.b-bottomnav fixes a glass tab bar to the bottom edge
with safe-area padding built in. Use three to five
__items, mark the current one with
.is-active, and attach a .b-badge--count to
an icon for unread counts.
Bottom Sheet
The full .b-bottom-sheet — a <dialog>
that springs up from the bottom edge with a scrim — is documented on
the Overlays page. Shown here statically
inside the frame: a __grabber pill on top of a
__body list of actions.
Mobile Toolbar
.b-mobile-toolbar is the bottom action bar for the
current screen — glass, hairline on top, safe-area padding below, and
icon buttons bumped up to the large control size for easy reach.
Swipe Actions
.b-swipe is a CSS-only take on the iOS swipe-to-act row:
a horizontal scroller with scroll-snap-type: x mandatory.
The __content row fills 100% of the width and snaps to
the start; the __actions sit just off-screen and snap to
the end. Dragging left reveals them, and the scroll snap makes the
row rest either fully closed or fully open — try it, the demo below
actually scrolls.
<button>s, so they stay focusable and
announced.Pull to Refresh
.b-ptr is the visual affordance above a list. It sits at
height: 0 until your scroll handler adds a state class:
.is-pulling springs it open to its full height as the
user drags, .is-ready rotates the __arrow
180° to signal "release to refresh", and .is-refreshing
swaps the arrow for a spinner while data loads. Shown here frozen in
the refreshing state.
Touch Target
Small controls stay small visually but should never be small to a
thumb. The .b-touch-target utility overlays an invisible
::after hit area of at least
var(--b-touch-target) — 44px — centered on the control,
so a compact close button still catches an imprecise tap. The button
below renders at its extra-small size; the whole 44×44 area around it
is pressable.
--b-touch-target token standardizes on Apple's 44px
minimum, comfortably above the WCAG floor. Apply
.b-touch-target to any control smaller than 44px, and
keep at least a hairline of spacing between adjacent expanded
targets so taps don't overlap.Safe Area
On notched and home-indicator phones, the OS reserves the screen edges
and reports them as env(safe-area-inset-*) values.
Barua's fixed chrome — .b-mobile-header,
.b-bottomnav, .b-mobile-toolbar,
.b-bottom-sheet — already pads with them. For your own
bars, use .b-safe-area (all four edges) or
.b-safe-bottom, which pads with
max(var(--b-space-3), env(safe-area-inset-bottom)) so it
still breathes on squared-off screens.
The insets report 0 until you opt in with
viewport-fit=cover in the viewport meta tag — without it,
the browser letterboxes your page instead:
<meta name="viewport"
content="width=device-width, initial-scale=1, viewport-fit=cover">
Collapsing Large Title
NavigationStack's signature move: the large title shrinks away as
content scrolls, and the bar title fades in. Add
data-b-collapse to a .b-mobile-header —
barua.js watches the nearest scroll parent and toggles
.is-collapsed.