Semantic Layouts

Use data-layout attributes on semantic HTML elements instead of custom element wrappers. Same CSS, same behavior, better semantics.

Why Semantic Layouts

Vanilla Breeze layout primitives work on any element, not just custom elements. Using them on semantic HTML gives you the same layouts with additional benefits.

Fewer Nesting Levels

Combine center + gap on a single element instead of nesting center inside stack. Less indentation, easier to scan.

Better Accessibility

HTML validators and screen readers understand <section>, <header>, and <article> natively. No extra ARIA needed.

LLM-Friendly Markup

AI tools parse semantic HTML better than custom elements. Your code is more understandable to both humans and machines.

Same CSS, Same Behavior

The data-layout attributes use the exact same CSS as the custom elements. Nothing changes except the tag name.

Key Insight: Semantic layouts are not a replacement for custom elements. They are an alternative authoring approach. Both are fully supported and share the same underlying CSS system.

Two Approaches, Same CSS

Every layout primitive in Vanilla Breeze can be used as either a custom element or a data attribute on any HTML element.

Layout Custom Element Data Attribute
Stack <layout-stack> data-layout="stack"
Center <layout-center> data-layout="center"
Grid <layout-grid> data-layout="grid"
Cluster <layout-cluster> data-layout="cluster"
Sidebar <layout-sidebar> data-layout="sidebar"
Cover <layout-cover> data-layout="cover"
Switcher <layout-switcher> data-layout="switcher"

All data-layout attributes (gap, min, max, align, justify) work identically on both custom elements and semantic elements. The only difference is the tag name in your HTML.

Side-by-Side Comparison

Custom Elements

<layout-center data-layout-max="wide"> <layout-stack data-layout-gap="2xl"> <layout-stack data-layout-gap="s"> <h2>Everything you need</h2> <p class="lead">Build faster without compromising quality.</p> </layout-stack> <layout-grid data-layout-min="250px" data-layout-gap="l"> <div>Feature 1</div> <div>Feature 2</div> <div>Feature 3</div> </layout-grid> </layout-stack> </layout-center>

Semantic HTML

<section data-layout="center" data-layout-max="wide" data-layout-gap="2xl"> <header data-layout="stack" data-layout-gap="s"> <h2>Everything you need</h2> <p class="lead">Build faster without compromising quality.</p> </header> <div data-layout="grid" data-layout-min="250px" data-layout-gap="l"> <div>Feature 1</div> <div>Feature 2</div> <div>Feature 3</div> </div> </section>
Notice: The semantic version uses <section> and <header> instead of generic wrappers. Screen readers announce these landmarks automatically. The data-layout-gap on center also eliminates the need for a nested stack.

Building a Feature Section

Let us transform a real feature section from custom elements to semantic HTML, one step at a time.

The Starting Point

Here is a typical feature section built with custom element wrappers. It works, but notice how many nesting levels are required.

<!-- 5 levels of nesting --> <layout-center data-layout-max="wide"> <!-- 1 --> <layout-stack data-layout-gap="2xl"> <!-- 2 --> <layout-stack data-layout-gap="s"> <!-- 3 --> <h2>Everything you need to build faster</h2> <p class="lead">Our platform provides...</p> </layout-stack> <layout-grid data-layout-min="250px"> <!-- 4 --> <layout-stack data-layout-gap="s"> <!-- 5 --> <h3>Lightning Fast</h3> <p>Optimized for speed...</p> </layout-stack> </layout-grid> </layout-stack> </layout-center>

Step 1: Replace layout-center + layout-stack

The outermost <layout-center> wrapping a <layout-stack> is a common pattern. With data attributes, we can combine both behaviors on a single <section> element by using data-layout="center" with data-layout-gap.

<!-- Step 1: layout-center + layout-stack &#x2192; section with center + gap --> <section data-layout="center" data-layout-max="wide" data-layout-gap="2xl"> <layout-stack data-layout-gap="s"> <h2>Everything you need to build faster</h2> <p class="lead">Our platform provides...</p> </layout-stack> <layout-grid data-layout-min="250px"> <layout-stack data-layout-gap="s"> <h3>Lightning Fast</h3> <p>Optimized for speed...</p> </layout-stack> </layout-grid> </section>

Step 2: Replace the inner layout-stack

The heading group is naturally a <header> element. We apply data-layout="stack" directly to it.

<!-- Step 2: layout-stack on header &#x2192; header with data-layout="stack" --> <section data-layout="center" data-layout-max="wide" data-layout-gap="2xl"> <header data-layout="stack" data-layout-gap="s"> <h2>Everything you need to build faster</h2> <p class="lead">Our platform provides...</p> </header> <layout-grid data-layout-min="250px"> <layout-stack data-layout-gap="s"> <h3>Lightning Fast</h3> <p>Optimized for speed...</p> </layout-stack> </layout-grid> </section>

Step 3: Replace layout-grid

The grid wrapper becomes a plain <div> with data-layout="grid". Each feature item also gets data-layout="stack".

<!-- Step 3: layout-grid &#x2192; div with data-layout="grid" --> <!-- 3 levels of nesting (down from 5) --> <section data-layout="center" data-layout-max="wide" data-layout-gap="2xl"> <header data-layout="stack" data-layout-gap="s"> <h2>Everything you need to build faster</h2> <p class="lead">Our platform provides...</p> </header> <div data-layout="grid" data-layout-min="250px" data-layout-gap="l"> <div data-layout="stack" data-layout-gap="s"> <h3>Lightning Fast</h3> <p>Optimized for speed...</p> </div> </div> </section>

The Result

5 nesting levels
3 nesting levels

The semantic version is flatter, uses real HTML landmarks, and produces the exact same visual result. Every data-layout attribute maps to the same CSS rules as its custom element counterpart.

The Center + Gap Pattern

The most common refactoring: eliminating a nested stack inside a center element.

In Vanilla Breeze, when data-layout-gap is added to a center element, the center switches from display: block to display: flex; flex-direction: column with the specified gap. This means a single element can both center its content and space its children vertically.

Before: Two Elements

<layout-center data-layout-max="wide"> <layout-stack data-layout-gap="2xl"> <h2>Section Title</h2> <p>Content here...</p> </layout-stack> </layout-center>

After: One Element

<section data-layout="center" data-layout-max="wide" data-layout-gap="2xl"> <h2>Section Title</h2> <p>Content here...</p> </section>
How It Works: The CSS rule [data-layout="center"][data-layout-gap] activates display: flex; flex-direction: column, turning the center into a flex container with vertical gap. This is the same behavior as layout-center[data-layout-gap] on the custom element.

Common Compositions

Here are the most useful semantic layout patterns you will reach for regularly.

Center + Gap (replaces center > stack)

The most common pattern. Use on <section>, <main>, or <article> to center content with vertical spacing.

<section data-layout="center" data-layout-max="wide" data-layout-gap="2xl"> <h2>Section Title</h2> <p>Content here...</p> </section>

Sidebar on Article or Section

The sidebar layout is semantic-aware: it automatically recognizes <nav> and <aside> as sidebar elements and <main>, <article>, <section> as content elements.

<div data-layout="sidebar" data-layout-gap="xl"> <aside> <nav>Sidebar navigation...</nav> </aside> <article> <h2>Main content</h2> <p>The article takes up the remaining space.</p> </article> </div>

Grid on a Div

Use data-layout="grid" on a plain div to create responsive grids. Visual components like <layout-card> remain as custom elements inside.

<div data-layout="grid" data-layout-min="250px" data-layout-gap="l"> <layout-card>Card 1</layout-card> <layout-card>Card 2</layout-card> <layout-card>Card 3</layout-card> </div>

Cover on Body or Main

The cover layout vertically centers a principal element. Use data-layout-principal on the child you want centered.

<body data-layout="cover" data-layout-min="100dvh"> <header>Top bar</header> <main data-layout-principal> <h1>Centered content</h1> </main> <footer>Footer</footer> </body>

Cluster on a Div for Button Groups

Use data-layout="cluster" for horizontal groups of items that wrap naturally.

<div data-layout="cluster" data-layout-gap="s"> <button>Save</button> <button class="secondary">Cancel</button> <button class="ghost">Reset</button> </div>

When to Keep Custom Elements

Not everything should become a data attribute. Visual components always stay as custom elements.

The key distinction is between layout primitives and visual components:

  • Layout primitives (stack, center, grid, cluster, sidebar, cover, switcher) control spacing and arrangement. These are good candidates for data-layout attributes.
  • Visual components (card, badge, text, brand-mark, form-field) provide visual styling like shadows, borders, backgrounds, and typography. These should remain as custom elements.
<!-- Visual components stay as custom elements --> <div data-layout="grid" data-layout-min="250px" data-layout-gap="l"> <layout-card data-variant="elevated" data-padding="l"> <h3>Premium Plan</h3> <p>Everything you need.</p> </layout-card> <layout-badge data-variant="success">New</layout-badge> <layout-text data-size="lg">Large text block</layout-text> </div>

In this example, the grid is a data attribute (layout concern), while cards and badges remain as custom elements (visual concern). This is the recommended pattern: semantic HTML for structure, custom elements for styled components.

Rule of Thumb: If the element adds visual decoration (shadows, borders, backgrounds, badges), keep it as a custom element. If it only arranges children (spacing, alignment, columns), use data-layout on a semantic element.

Decision Guide

Use this table to decide which approach fits your situation.

Scenario Recommended Approach Why
Quick prototyping Custom elements Fastest to type, self-documenting tag names
Simple page, minimal nesting Either Both work well when structure is shallow
Production code Data attributes Better semantics, cleaner HTML, fewer nesting levels
Complex nested layouts Data attributes Significantly reduces nesting depth
Accessibility-sensitive context Data attributes Screen readers understand native landmarks
Working with LLMs or AI tools Data attributes Standard HTML is better understood by AI
Visual components (card, badge) Custom elements These provide styling, not just layout
Teaching HTML concepts Custom elements Tag names make the layout intent explicit

You can freely mix both approaches in the same page. Use custom elements where they make the code clearer and data attributes where they reduce unnecessary nesting. The CSS is the same either way.

Bottom Line: There is no wrong choice. Both approaches produce identical results. Choose whichever makes your HTML most readable for your team and your tools.

Page Layouts & Grid Identity

Semantic layouts extend beyond component-level patterns to full page structure with the grid identity system.

VB includes a three-tier grid identity system where semantic HTML elements auto-register to grid areas based on their element type. At the page level, data-page-layout on the <body> turns <header>, <nav>, <main>, <aside>, and <footer> into grid areas automatically — no classes needed.

<body data-page-layout="sidebar-left"> <header>Site header</header> <nav>Navigation</nav> <main data-layout="stack" data-layout-gap="l"> Content with nested layouts... </main> <footer>Site footer</footer> </body>

At the component level, data-layout="regions" generalizes the header/content/footer pattern to any element, and data-layout="media" provides a figure+content media object. These compose with page layouts for full three-tier nesting.

Learn more: See the Grid Identity page for the full system documentation, including page templates, named area overrides, main-level layouts, and composing all three tiers together.