Mobile

How to build mobile-native experiences with Vanilla Breeze: safe areas, app shells, bottom sheets, touch targets, forms, and horizontal scroll.

Philosophy

Most CSS frameworks ignore mobile-specific concerns, leaving you to bolt on safe areas, touch targets, and viewport fixes. VB takes the opposite approach: mobile support ships inside the core.

  • Default — every page gets 44px touch targets on coarse pointers, 100dvh body height, smooth scrolling, and responsive layout collapse. No attributes required.
  • Enhanced — pages designed for mobile add viewport-fit=cover to unlock safe-area tokens, use data-page-layout="app-shell" for bottom-nav shells, and use <dialog data-position="bottom"> for action sheets.
  • Granular — individual inputs get the right virtual keyboard via inputmode, autocomplete, and enterkeyhint.

Because VB’s design tokens drive all sizing and spacing, the @media (pointer: coarse) query only needs to set --size-touch-min: 2.75rem — every button, input, and select automatically adapts.

The viewport-fit Unlock

Modern phones have notches, Dynamic Islands, and home indicators that occupy real pixels. To respect these areas, add viewport-fit=cover to your viewport meta tag:

<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">

This activates VB’s safe-area tokens, which are defined automatically in the token layer:

/* VB exposes these tokens automatically */ :root { --safe-top: env(safe-area-inset-top, 0px); --safe-right: env(safe-area-inset-right, 0px); --safe-bottom: env(safe-area-inset-bottom, 0px); --safe-left: env(safe-area-inset-left, 0px); } /* Use them on edge-touching elements */ .my-fixed-bar { padding-block-end: calc(var(--size-m) + var(--safe-bottom)); }

What auto-consumes safe areas

Element Token used
dialog[data-position="bottom"] --safe-bottom (padding)
data-page-layout="app-shell" nav (mobile) --safe-bottom (padding)

For custom fixed bars or sticky footers, apply safe-area tokens manually using calc().

What Works Already

These mobile features are built into VB’s core — no extra attributes or configuration needed.

Concern VB solution
Touch targets @media (pointer: coarse) enforces 44px min on buttons, inputs, selects
Full-height pages body { min-block-size: 100dvh }
Tap highlight -webkit-tap-highlight-color: transparent in reset
iOS text zoom text-size-adjust: none in reset
Smooth scrolling scroll-behavior: smooth (respects reduced-motion)
Sidebar collapse data-layout="sidebar" wraps naturally (intrinsic CSS, no media query)
Column ↔ row flip data-layout="switcher" flips at configurable threshold
Split stack data-layout="split" stacks at < 48rem
Hover-only motion @media (hover: none) disables hover lift/scale
Scroll snap Built into data-layout="reel" and <carousel-wc>
Scroll containment overscroll-behavior: contain on dialogs and sticky nav/aside

App Shell Pattern

The data-page-layout="app-shell" layout creates a header/main/nav grid. On screens narrower than 48rem, the nav automatically moves below main — becoming a bottom navigation bar. Safe-area bottom padding is applied automatically.

<body data-page-layout="app-shell" data-layout-gap="none"> <header data-layout="cluster" data-layout-justify="between" data-layout-sticky> <brand-mark>AppName</brand-mark> <button class="icon-only ghost" aria-label="Notifications"> <icon-wc name="bell"></icon-wc> </button> </header> <main data-layout="stack" data-layout-gap="l"> <!-- scrollable content --> </main> <nav aria-label="Main navigation"> <ul> <li><a href="#" aria-current="page"><icon-wc name="home"></icon-wc> Home</a></li> <li><a href="#"><icon-wc name="search"></icon-wc> Search</a></li> <li><a href="#"><icon-wc name="folder"></icon-wc> Projects</a></li> <li><a href="#"><icon-wc name="user"></icon-wc> Profile</a></li> </ul> </nav> </body>

See also: Application Shells pattern

Bottom Sheet

VB’s native <dialog> styling includes a bottom drawer variant. It slides up from the bottom, has rounded top corners, respects safe areas, and prevents scroll chaining — all with zero JavaScript beyond the native showModal() API.

<button commandfor="my-sheet" command="show-modal"> Show Options </button> <dialog id="my-sheet" data-position="bottom"> <header data-layout="cluster" data-layout-justify="between"> <h2>Options</h2> <button commandfor="my-sheet" command="close" class="ghost icon-only" aria-label="Close"> <icon-wc name="x"></icon-wc> </button> </header> <section> <nav aria-label="Actions"> <ul> <li><a href="#"><icon-wc name="share"></icon-wc> Share</a></li> <li><a href="#"><icon-wc name="bookmark"></icon-wc> Save</a></li> </ul> </nav> </section> </dialog>

The commandfor / command attributes use the Invokers API to open and close the dialog declaratively. In browsers that don’t support Invokers yet, fall back to document.getElementById('my-sheet').showModal().

See also: Dialog element docs

Mobile Forms

The single biggest mobile form improvement is using the right HTML attributes. These are native HTML — not VB features — but VB’s <form-field> component makes them easy to use correctly.

The attributes that matter

Attribute What it does on mobile
type="email" Shows keyboard with @ key prominent
type="tel" Shows numeric dialpad
inputmode="numeric" Numeric keyboard without spinners (use for credit cards, ZIP codes)
autocomplete="email" Enables one-tap autofill from saved contacts
autocomplete="cc-number" iOS shows camera to scan physical card; Android offers Google Pay
autocomplete="one-time-code" iOS shows SMS suggestion bar above keyboard
enterkeyhint="next" Shows “Next” on the Enter key (guides user through the form)
enterkeyhint="send" Shows “Send” on the last field
<form class="stacked" novalidate> <form-field> <label for="email">Email</label> <input type="email" id="email" required autocomplete="email" enterkeyhint="next"> </form-field> <form-field> <label for="phone">Phone</label> <input type="tel" id="phone" autocomplete="tel" inputmode="tel" enterkeyhint="next"> </form-field> <form-field> <label for="zip">ZIP Code</label> <input type="text" id="zip" autocomplete="postal-code" inputmode="numeric" pattern="[0-9]{5}" enterkeyhint="send"> </form-field> <button type="submit" class="full-width">Submit</button> </form>

See also: autocomplete reference, inputmode reference, enterkeyhint reference

Hero and Horizontal Scroll

Viewport height units

VB’s cover layout supports three viewport height units:

Value Unit Use when
data-layout-min="100svh" Small viewport height Hero sections — always fits when browser chrome is visible
data-layout-min="100dvh" Dynamic viewport height Full-screen app panels — updates as chrome hides on scroll
data-layout-min="100vh" Legacy viewport height Fallback — broken on mobile (ignores browser chrome)
<!-- Hero that fits in one mobile screen (browser chrome visible) --> <section data-layout="cover" data-layout-min="100svh" data-layout-centered> <h1>Big Headline</h1> <p>Fits without scrolling.</p> </section> <!-- Full-height app panel (dynamic, updates as chrome hides) --> <section data-layout="cover" data-layout-min="100dvh"> ... </section>

Horizontal card reel

The data-layout="reel" provides a horizontal scroll-snap container. It includes scroll-snap-type: x mandatory, momentum scrolling, and hidden scrollbars. Use it for card feeds, feature showcases, and image galleries.

<section data-layout="reel" data-layout-gap="m" data-layout-item-width="m"> <layout-card>Card 1</layout-card> <layout-card>Card 2</layout-card> <layout-card>Card 3</layout-card> <layout-card>Card 4</layout-card> </section>

Browser Support

Feature Chrome Firefox Safari Edge
env(safe-area-inset-*) 69+ 65+ 11.2+ 79+
dvh / svh 108+ 101+ 15.4+ 108+
overscroll-behavior 63+ 59+ 16+ 18+
scroll-snap-type 69+ 68+ 11+ 79+
@media (pointer: coarse) 41+ 64+ 9+ 12+
Invokers API (commandfor) 135+ No No 135+

All core features have 95%+ support on current mobile browsers. The Invokers API is the newest; for broader support, use element.showModal() as a JavaScript fallback.