LayoutGrid gives you a responsive layout system built on a 12-column grid. Define how many columns each item spans at each screen size, and the layout shifts automatically — no media queries, no CSS wrestling.


Why Responsive Design Matters

Every application reaches users across a wide range of devices. A single layout rarely serves them all well:

Device Screen Width User Context
Smartphone < 600 px On the go, touch input, limited space
Tablet 600 – 1024 px Mixed use, touch or keyboard, moderate space
Desktop > 1024 px Focused work, mouse and keyboard, plenty of space

Responsive design lets you write one layout that adapts to all of them.


Breakpoint Cascade — 12-Column Grid xs < 600 px sm ≥ 600 px md ≥ 960 px lg ≥ 1280 px xl ≥ 1920 px settings cascade upward — unset breakpoints inherit from the next smaller one Example: .WithXs(12) .WithSm(6) .WithLg(3) xs: 12 cols sm: 6 cols md: 6 cols ↑ lg: 3 cols xl: 3 cols ↑ ↑ inherited 12-column row — items wrap when column total exceeds 12 Item A — 6 cols Item B — 6 cols 4 cols 4 cols 4 cols (next row begins here)

Breakpoints cascade upward through xs → sm → md → lg → xl; unset breakpoints inherit the nearest smaller value.


The 12-Column System

LayoutGrid divides the available width into 12 equal columns. Each item declares how many columns it occupies. The columns always add up to 12 per row; items that overflow wrap to the next row automatically.

Columns Proportional Width Typical Use
12 100 % Full-width content, mobile layouts
6 50 % Two equal side-by-side columns
4 33 % Three equal columns
3 25 % Four-column grids, narrow sidebars
Controls.LayoutGrid
    .WithSkin(skin => skin.WithSpacing(2))
    .WithView(Controls.Markdown("12 cols (100%)").AddSkin(Skins.Card), s => s.WithXs(12))
    .WithView(Controls.Markdown("6 cols").AddSkin(Skins.Card), s => s.WithXs(6))
    .WithView(Controls.Markdown("6 cols").AddSkin(Skins.Card), s => s.WithXs(6))
    .WithView(Controls.Markdown("4").AddSkin(Skins.Card), s => s.WithXs(4))
    .WithView(Controls.Markdown("4").AddSkin(Skins.Card), s => s.WithXs(4))
    .WithView(Controls.Markdown("4").AddSkin(Skins.Card), s => s.WithXs(4))

Breakpoints: Adapting to Screen Size

A breakpoint is a screen-width threshold where the layout can change. LayoutGrid provides five, matching standard Material Design conventions:

Method Activates at Screen type Example devices
.WithXs() < 600 px Extra small iPhones, Android phones
.WithSm() ≥ 600 px Small Large phones, small tablets
.WithMd() ≥ 960 px Medium iPads, tablets
.WithLg() ≥ 1280 px Large Laptops, small monitors
.WithXl() ≥ 1920 px Extra large Large monitors, 4 K displays

Cascading upward. Settings inherit toward larger breakpoints. Setting .WithMd(4) applies to medium screens and every larger size unless you explicitly override it with .WithLg() or .WithXl().


Designing for Small Screens (Smartphones)

Mobile screens are narrow. The guiding principle is stack first: make every item full-width on extra-small screens with WithXs(12), then introduce side-by-side columns as the screen grows.

Key guidelines:

Controls.LayoutGrid
    .WithSkin(skin => skin.WithSpacing(2))
    .WithView(Controls.Markdown("**Primary Content**\n\nFull width on mobile").AddSkin(Skins.Card), s => s
        .WithXs(12)    // Full width on phones
        .WithMd(8))    // 2/3 width on tablets+
    .WithView(Controls.Markdown("**Sidebar**\n\nBelow on mobile, side on tablet").AddSkin(Skins.Card), s => s
        .WithXs(12)    // Full width on phones (stacks below)
        .WithMd(4))    // 1/3 width on tablets+ (appears beside)

Designing for Medium Screens (Tablets)

Tablets offer enough room for two-column layouts and are often used in both portrait and landscape orientations. A 3 + 9 or 4 + 8 split works well for navigation-plus-content patterns.

Key guidelines:

Controls.LayoutGrid
    .WithSkin(skin => skin.WithSpacing(3))
    .WithView(Controls.Markdown("**Navigation**").AddSkin(Skins.Card), s => s
        .WithXs(12)    // Full width on phone
        .WithMd(3))    // Narrow sidebar on tablet
    .WithView(Controls.Markdown("**Main Content Area**\n\nMost of the screen on tablet").AddSkin(Skins.Card), s => s
        .WithXs(12)    // Full width on phone
        .WithMd(9))    // Wide main area on tablet

Designing for Large Screens (Desktops)

Desktop users have space to spare — use it to show more information at once without overwhelming them. Card grids and multi-column panels shine here, but be mindful that very wide text lines are harder to read.

Key guidelines:

Controls.LayoutGrid
    .WithSkin(skin => skin.WithSpacing(2))
    .WithView(Controls.Markdown("Card 1").AddSkin(Skins.Card), s => s
        .WithXs(12).WithSm(6).WithLg(3))  // 1 col -> 2 cols -> 4 cols
    .WithView(Controls.Markdown("Card 2").AddSkin(Skins.Card), s => s
        .WithXs(12).WithSm(6).WithLg(3))
    .WithView(Controls.Markdown("Card 3").AddSkin(Skins.Card), s => s
        .WithXs(12).WithSm(6).WithLg(3))
    .WithView(Controls.Markdown("Card 4").AddSkin(Skins.Card), s => s
        .WithXs(12).WithSm(6).WithLg(3))

Common Responsive Patterns

These three patterns cover the majority of real-world layouts.

Dashboard Cards

Items flow from a single column on phones to a four-column grid on wide monitors:

Controls.LayoutGrid
    .WithSkin(skin => skin.WithSpacing(3))
    .WithView(metricCard1, s => s.WithXs(12).WithSm(6).WithLg(3))  // 1 -> 2 -> 4 columns
    .WithView(metricCard2, s => s.WithXs(12).WithSm(6).WithLg(3))
    .WithView(metricCard3, s => s.WithXs(12).WithSm(6).WithLg(3))
    .WithView(metricCard4, s => s.WithXs(12).WithSm(6).WithLg(3))

A narrow navigation sidebar that stacks below the content on mobile:

Controls.LayoutGrid
    .WithView(sidebar, s => s.WithXs(12).WithMd(3))   // Full width then sidebar
    .WithView(content, s => s.WithXs(12).WithMd(9))   // Full width then main

Equal Thirds

Three equal columns that stack vertically on mobile and spread out on tablet and above:

Controls.LayoutGrid
    .WithView(col1, s => s.WithXs(12).WithMd(4))  // Stack on mobile, thirds on tablet+
    .WithView(col2, s => s.WithXs(12).WithMd(4))
    .WithView(col3, s => s.WithXs(12).WithMd(4))

Tile Flow

A card catalog does not want breakpoints at all: it wants tiles of a minimum width that fill the row and wrap, however many there are and however wide the screen is. That is the grid's tile-flow mode — declare the minimum tile width and nothing else:

Controls.LayoutGrid
    .WithMinTileWidth("180px")
    .WithView(Controls.Markdown("**Tile 1**").AddSkin(Skins.Card))
    .WithView(Controls.Markdown("**Tile 2**").AddSkin(Skins.Card))
    .WithView(Controls.Markdown("**Tile 3**").AddSkin(Skins.Card))
    .WithView(Controls.Markdown("**Tile 4**").AddSkin(Skins.Card))
    .WithView(Controls.Markdown("**Tile 5**").AddSkin(Skins.Card))

Every child is a tile at least that wide. As many tiles as fit share a row in equal columns, and the rest wrap onto the next row. A tile never grows wider than the container, so on a phone the grid is one column. The per-item spans (WithXs … WithXxl) do not apply in this mode. The gap is WithSpacing(n) × 4px, or 12px when no spacing is set.

Every renderer draws the same result. The web renderers (Blazor, React) use a CSS grid with repeat(auto-fill, minmax(min(100%, w), 1fr)). React Native has no CSS grid, so it measures the container and computes the column count. A view never writes that CSS itself: hand-rolled display: grid styles render as an opaque style string on native, and they cannot see the list problem below.

Tiles from a bound list

When the tiles come from data, put the mode on the bound list itself. The list's rendered items are then the tiles:

// ✅ every course row is its own tile
new ItemTemplateControl(card, new JsonPointerReference("")) { DataContext = coursesPointer }
    .WithMinTileWidth("300px")

🚨 Never wrap a bound list in a grid container instead. A grid lays out its children, and a bound list is ONE child, so the whole list becomes one grid cell and every tile stacks in a single column. That is the exact failure a hand-rolled display: grid stack around an ItemTemplateControl produces once lists render vertically by default.

Method On Effect
.WithMinTileWidth(w) LayoutGrid (shorthand for .WithSkin(s => s.WithMinTileWidth(w))) Static children become tiles of at least w
.WithMinTileWidth(w) ItemTemplateControl Each bound row becomes a tile of at least w; Orientation/Wrap do not apply

Grid Configuration

Control the container itself via .WithSkin():

Method Purpose Values
.WithSpacing(n) Gap between items 1, 2, 3 (multiples of the base spacing unit)
.WithJustify(j) Horizontal alignment of the row "start", "center", "end"
.WithMinTileWidth(w) Switches to tile flow (above) a CSS length, e.g. "280px"

Item Configuration

Each item added with .WithView(content, skin => ...) accepts these sizing methods:

Method Screen size Activates at
.WithXs(cols) Extra small < 600 px
.WithSm(cols) Small ≥ 600 px
.WithMd(cols) Medium ≥ 960 px
.WithLg(cols) Large ≥ 1280 px
.WithXl(cols) Extra large ≥ 1920 px

cols is an integer from 1 to 12. Items with no explicit size for a breakpoint inherit from the next smaller breakpoint that is set.


See Also

Reconnecting…
The connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.