Grids and Wrapping
display_grid lays a container's children out in rows and columns at once.
The container lists its tracks with grid_template_columns and grid_template_rows,
and each child fills the next free cell, or the cells it asks for.
The examples on this page size every track with fr(1), one share of the grid's free space.
Dashboard tiles
Equal tracks on both axes give a grid of equal tiles, and gap separates them.
@component
def tiles() -> Div:
return Div(
style=display_grid | grid_template_columns(fr(1), fr(1), fr(1)) | grid_template_rows(fr(1), fr(1)) | gap(1),
children=[Text(style=border, content=f"tile {n}") for n in range(1, 7)],
)
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│tile 1 │ │tile 2 │ │tile 3 │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│tile 4 │ │tile 5 │ │tile 6 │
│ │ │ │ │ │
│ │ │ │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
Cells spanning tracks
grid_column and grid_row place a child by grid line, counted from 1,
and span(n) stretches it across n tracks.
Children without a placement fill the cells that are left, in order.
Place a spanning child on both axes, as both are here. A child placed on rows alone is placed before one placed on columns alone, whatever their order among the children, so mixing the two can push a child into an extra row below the template.
@component
def spans() -> Div:
return Div(
style=display_grid | grid_template_columns(fr(1), fr(1), fr(1)) | grid_template_rows(fr(1), fr(1), fr(1)),
children=[
Text(
style=grid_row(1) | grid_column(1, span(2)) | border | border_heavy,
content="grid_row(1)\ngrid_column(1, span(2))",
),
Text(
style=grid_row(1, span(3)) | grid_column(3) | border | border_heavy,
content="grid_row(\n 1, span(3),\n)\ngrid_column(3)",
),
Text(style=border, content="auto"),
Text(style=border, content="auto"),
Text(style=border, content="auto"),
Text(style=border, content="auto"),
],
)
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┏━━━━━━━━━━━━━━━━━━┓
┃grid_row(1) ┃┃grid_row( ┃
┃grid_column(1, span(2)) ┃┃ 1, span(3), ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛┃) ┃
┌──────────────────┐┌──────────────────┐┃grid_column(3) ┃
│auto ││auto │┃ ┃
│ ││ │┃ ┃
└──────────────────┘└──────────────────┘┃ ┃
┌──────────────────┐┌──────────────────┐┃ ┃
│auto ││auto │┃ ┃
│ ││ │┃ ┃
└──────────────────┘└──────────────────┘┗━━━━━━━━━━━━━━━━━━┛
Auto flow
Children beyond the template's cells get new tracks, which the grid adds as it needs them.
By default, children fill each row in turn and the grid adds rows,
like the seventh child on the left.
grid_auto_flow_column fills each column in turn and adds columns instead.
@component
def auto_flow() -> Div:
return Div(
style=row | gap(2),
children=[
Div(
style=display_grid | grid_template_columns(fr(1), fr(1), fr(1)) | fill(1) | border | border_heavy,
children=[
Text(
content=" grid_template_columns(fr(1), fr(1), fr(1)) ",
style=position_absolute | inset_top(-1) | inset_left(1),
),
*(Text(style=border, content=str(n)) for n in range(1, 8)),
],
),
Div(
style=display_grid
| grid_template_rows(fr(1), fr(1), fr(1))
| grid_auto_flow_column
| fill(1)
| border
| border_heavy,
children=[
Text(
content=" grid_template_rows(fr(1), fr(1), fr(1)) ",
style=position_absolute | inset_top(-1) | inset_left(1),
),
Text(content=" grid_auto_flow_column ", style=position_absolute | inset_bottom(-1) | inset_left(1)),
*(Text(style=border, content=str(n)) for n in range(1, 8)),
],
),
],
)
┏━ grid_template_columns(fr(1), fr(1), fr(1)) ━━┓ ┏━ grid_template_rows(fr(1), fr(1), fr(1)) ━━━━━┓
┃┌──────────────┐┌─────────────┐┌──────────────┐┃ ┃┌──────────────┐┌─────────────┐┌──────────────┐┃
┃│1 ││2 ││3 │┃ ┃│1 ││4 ││7 │┃
┃└──────────────┘└─────────────┘└──────────────┘┃ ┃└──────────────┘└─────────────┘└──────────────┘┃
┃┌──────────────┐┌─────────────┐┌──────────────┐┃ ┃┌──────────────┐┌─────────────┐ ┃
┃│4 ││5 ││6 │┃ ┃│2 ││5 │ ┃
┃└──────────────┘└─────────────┘└──────────────┘┃ ┃└──────────────┘└─────────────┘ ┃
┃┌──────────────┐ ┃ ┃┌──────────────┐┌─────────────┐ ┃
┃│7 │ ┃ ┃│3 ││6 │ ┃
┃└──────────────┘ ┃ ┃└──────────────┘└─────────────┘ ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┗━ grid_auto_flow_column ━━━━━━━━━━━━━━━━━━━━━━━┛
Wrapping without a grid
When children have different sizes and there's no grid for them to line up in,
flex_wrap lets a row start a new line when the next child doesn't fit.
gap_width spaces the children within a line.
@component
def wrap() -> Div:
return Div(
style=row | flex_wrap | align_children_start | gap_width(1),
children=[Text(style=border | pad_x(1), content=tag) for tag in TAGS],
)
┌────────┐ ┌─────────┐ ┌──────┐ ┌──────────┐
│ layout │ │ flexbox │ │ grid │ │ wrapping │
└────────┘ └─────────┘ └──────┘ └──────────┘
┌───────────┐ ┌──────┐ ┌─────────┐ ┌───┐
│ alignment │ │ text │ │ borders │ │ z │
└───────────┘ └──────┘ └─────────┘ └───┘
┌─────────────┐ ┌─────┐
│ positioning │ │ gap │
└─────────────┘ └─────┘