C&C Docs
Log in
Pages

Mobile block variants — m. prefix

You are reading the desktop page. This page and its mobile twin are two files at one URL. Preview the other one with the instructions below — the URL never changes.

A block file whose name starts with m. is the mobile variant of the block beside it. The server picks between the two per request, before a byte of HTML is sent, from the same $device.mobile a template reads. Use it when a phone needs a structurally different page — different navigation, a different interaction model — not merely a restyled one. For restyling, reach for gale's breakpoint variants (md:flex) first.

Two files, one URL
implementation/apps/docs/
└─ blocks/
   ├─ mobile-blocks.html      →  /mobile-blocks   (desktop)
   ├─ mobile-blocks.mjs       →  its controller
   ├─ m.mobile-blocks.html    →  /mobile-blocks   (mobile)
   └─ m.mobile-blocks.mjs     →  ITS controller

One registered route. Two files. The server picks per request.
Result
implementation/apps/docs/ └─ blocks/ ├─ mobile-blocks.html → /mobile-blocks (desktop) ├─ mobile-blocks.mjs → its controller ├─ m.mobile-blocks.html → /mobile-blocks (mobile) └─ m.mobile-blocks.mjs → ITS controller One registered route. Two files. The server picks per request.

Previewing the variant

Chrome / Edge: open devtools and toggle device mode (the phone icon). Chromium then sends Sec-CH-UA-Mobile: ?1, which settles it outright — reload and this page becomes its mobile twin.

Firefox / Safari: those browsers send no client hints, so narrow the window below 600px and reload twice. The runtime writes your viewport into the cnc_vp cookie on mount, so the server learns it from the second request on — the first reload after resizing still renders desktop. That flip happens between requests, never mid-page.

No m. file means the plain block serves every viewer. The variant is a whole block, with its own controller:

Each file gets its own controller
// blocks/mobile-blocks.mjs      — runs for desktop viewers
export default {
  layout: 'two-column',
  async $save(draft) { /* the desktop flow */ },
}

// blocks/m.mobile-blocks.mjs    — runs for mobile viewers
export default {
  layout: 'stacked',
  async $save(draft) { /* the phone flow, whatever it needs to be */ },
}
Result
// blocks/mobile-blocks.mjs — runs for desktop viewers export default { layout: 'two-column', async $save(draft) { /* the desktop flow */ }, } // blocks/m.mobile-blocks.mjs — runs for mobile viewers export default { layout: 'stacked', async $save(draft) { /* the phone flow, whatever it needs to be */ }, }

Blocks only. A widget has no mobile variant — variant is resolved statically at render time and cannot respond to a viewport. Inside either file, $device is still readable as usual.