C&C Docs
Log in

app-layout

An application shell built on CSS grid. A short areas string names the edges the page has — header, footer, left, right — and every child is placed into one of them by its tag name. The center track takes the remaining space and scrolls on its own, the edges size to their content, and a named sidebar slot is projected into a drawer for narrow screens.

Quick start

Write areas as the edges you want, then drop a <header> and a <main> inside. The uppercase letters are edges of the outer grid; lowercase letters are edges of a nested one, and c — the center — is the only one most layouts need. app-layout is height: 100dvh by default, so the examples on this page override it to sit inside a fixed-height box.

Header

The center area is the only track that grows, and it scrolls by itself — the header stays put.

Tall content to demonstrate scrolling
<app-layout areas="Tc" style="height: 100%">
  <header class="bg-primary-600 text-white px-4 py-2">Header</header>
  <main class="p-4">Content</main>
</app-layout>

The areas string

Each letter names one area, and its case picks the grid. Uppercase letters are edges of the outer grid; lowercase letters are edges of a nested grid that fills whatever the outer grid leaves over. Letters you leave out simply produce no track, so Tc is a two-row grid and LTcRB is a three-by-three one.

Most layouts need one lowercase letter — c, the center. Write more and you get a real inner grid: LTctbB is a left rail, top bar and bottom bar on the outside, wrapped around a header / center / footer on the inside. A child picks its grid the same way, through the case of its data-area: data-area="T" is the outer top bar, data-area="t" the inner one. Children that carry no data-area are matched from their tag name against the outer grid first, then the inner one.

Letter Area Sizing
T Top bar Row height is min-content.
B Bottom bar Row height is min-content.
L Left rail Column width is auto — give the child its own width class.
R Right rail Column width is auto — give the child its own width class.
M Mobile footer A second bottom row, for a cnc-tabbar. Do not combine it with B — see Known gaps.
c Center The only 1fr track in each axis. Scrolls independently and is a flex column.
t b l r m Inner edges The same five edges, one grid in. They size like their uppercase counterparts, inside the rect the outer grid left over.

Letter order matters

Each area claims the whole grid and is then trimmed by the areas declared before it. So the first letter wins the corner: LTc gives a left rail that runs the full height with the header beside it, while TLc gives a header that runs the full width with the rail beneath it. Both examples are below.

Mapping children to areas

A child picks its area from its tag name. Anything that does not match — a <div>, a <section>, a widget element — lands in the center unless it carries data-area, which always wins.

data-area is case-sensitive, because the two grids share letters: T is the outer top bar and t the inner one. A tag name carries no case, so a child that falls back to its tag is matched against the outer grid first and the inner one second — which is why <main> still lands in the center of a Tc layout.

Child Area Notes
<header> t Contributes T to an inferred areas string.
<footer> b Contributes B.
<nav> l Contributes L.
<aside> r Contributes R.
<main> c Contributes C, which the grid reads as the center.
anything else c Placed in the center, but contributes no letter when the areas string is being inferred.
data-area="r" r Overrides the tag map, so a <div data-area="r"> becomes the right rail.

Your element is replaced by a div

The widget re-renders each child's contents into its own area wrapper: your class and inline style are carried over, but the tag itself is not. A <header class="p-2"> comes out of the server as <div data-area="t" class="p-2" style="grid-area: …">. Do not attach an id, an event handler, or a CSS selector to the tag name of a direct child — put them one level deeper.

Known gaps

Two things the widget currently does not do. They are documented here rather than demoed as working.

  • B and M collide. Both resolve to the last row, so TcBM renders the footer and the mobile footer in the same cell and leaves an empty row above them. Use one or the other.
  • mobileFooter is inert. The property is declared with a default of 0, and neither the template nor the stylesheet reads it. There is no responsive rule that shows or hides the M area either — it is an ordinary row at every viewport width.

Usage guidance

Use app-layout once, at the root of an application template, where the whole viewport is the frame and only the middle should scroll. It is not a general-purpose grid: for arranging content inside a page, reach for cnc-layout or plain Tailwind grid classes, and for a resizable multi-pane editor shell reach for cnc-workspace. Keep routing and content state in the block, not in the layout.

← Back to Widgets