Navigation

Grid

<oblyx-grid> is a two-dimensional CSS grid layout. Its children are ordinary grid items; <oblyx-grid-span> is an optional wrapper for a child that needs to cover more than one track. Unlike oblyx-stack and oblyx-layout, which are flexbox underneath, oblyx-grid really is display: grid.

Basic grid

1
2
3
4
5
HTMLKotlin
<oblyx-grid columns="3" gap="3">
  <div class="docs-demo-box">1</div>
  <div class="docs-demo-box">2</div>
  <div class="docs-demo-box">3</div>
  <div class="docs-demo-box">4</div>
  <div class="docs-demo-box">5</div>
</oblyx-grid>
oblyxGrid(columns = 3, gap = OblyxGridGap._3) {
  div(classes = "docs-demo-box") { +"1" }
  div(classes = "docs-demo-box") { +"2" }
  div(classes = "docs-demo-box") { +"3" }
  div(classes = "docs-demo-box") { +"4" }
  div(classes = "docs-demo-box") { +"5" }
}

Spanning tracks

<oblyx-grid-span> covers multiple columns or rows. It IS the grid item its parent lays out (grid-column/grid-row are set on the tag itself, not an inner wrapper), so it composes as a plain child alongside items with no span at all. columns-full spans every column, for a row that should stretch edge to edge regardless of the grid's track count.

Grid span

Spans 2 columns
3
4
columns-full: spans every column
5
HTMLKotlin
<oblyx-grid columns="4" gap="3">
  <oblyx-grid-span columns="2" class="docs-demo-box">Spans 2 columns</oblyx-grid-span>
  <div class="docs-demo-box">3</div>
  <div class="docs-demo-box">4</div>
  <oblyx-grid-span columns-full class="docs-demo-box">columns-full: spans every column</oblyx-grid-span>
  <div class="docs-demo-box">5</div>
</oblyx-grid>
oblyxGrid(columns = 4, gap = OblyxGridGap._3) {
  oblyxGridSpan(columns = 2) { classes = setOf("docs-demo-box"); +"Spans 2 columns" }
  div(classes = "docs-demo-box") { +"3" }
  div(classes = "docs-demo-box") { +"4" }
  oblyxGridSpan(columnsFull = true) { classes = setOf("docs-demo-box"); +"columns-full: spans every column" }
  div(classes = "docs-demo-box") { +"5" }
}

Responsive columns

There's no breakpoint system here, matching upstream. Setting min-column-width(with columns left unset) switches the grid to an intrinsic auto-fill minmax() track template that the browser recomputes continuously as the container resizes, with no media query involved. Drag the bottom-right corner of the frame below to see the column count change in real time.

Responsive grid

Resize the dashed frame to change the column count.

1
2
3
4
5
6
HTMLKotlin
<div class="docs-demo-resize">
  <oblyx-grid min-column-width="140" gap="3">
    <div class="docs-demo-box">1</div>
    <div class="docs-demo-box">2</div>
    <div class="docs-demo-box">3</div>
    <div class="docs-demo-box">4</div>
    <div class="docs-demo-box">5</div>
    <div class="docs-demo-box">6</div>
  </oblyx-grid>
</div>
div {
  classes = setOf("docs-demo-resize")
  oblyxGrid(minColumnWidth = 140, gap = OblyxGridGap._3) {
    div(classes = "docs-demo-box") { +"1" }
    div(classes = "docs-demo-box") { +"2" }
    div(classes = "docs-demo-box") { +"3" }
    div(classes = "docs-demo-box") { +"4" }
    div(classes = "docs-demo-box") { +"5" }
    div(classes = "docs-demo-box") { +"6" }
  }
}

Fill vs. fit

repeat-mode only matters when there's room for more tracks than there are items.fill (the default) leaves the extra tracks empty, so items keep their natural track width;fit collapses the empty tracks instead, letting the present items stretch to fill the row. Both frames below are the same width with the same two items: only repeat-mode differs.

repeat-mode comparison

Resize either frame to see the difference persist at every width.

repeat-mode="fill" (default): empty tracks stay empty

1
2

repeat-mode="fit": empty tracks collapse, items stretch

1
2
HTMLKotlin
<div>
  <p class="docs-demo-label">repeat-mode="fill" (default): empty tracks stay empty</p>
  <div class="docs-demo-resize" style="margin-bottom: 20px;">
    <oblyx-grid min-column-width="100" gap="3">
      <div class="docs-demo-box">1</div>
      <div class="docs-demo-box">2</div>
    </oblyx-grid>
  </div>
  <p class="docs-demo-label">repeat-mode="fit": empty tracks collapse, items stretch</p>
  <div class="docs-demo-resize">
    <oblyx-grid min-column-width="100" repeat-mode="fit" gap="3">
      <div class="docs-demo-box">1</div>
      <div class="docs-demo-box">2</div>
    </oblyx-grid>
  </div>
</div>
div {
  p(classes = "docs-demo-label") { +"repeat-mode=\"fill\" (default): empty tracks stay empty" }
  div {
    classes = setOf("docs-demo-resize")
    oblyxGrid(minColumnWidth = 100, gap = OblyxGridGap._3) {
      div(classes = "docs-demo-box") { +"1" }
      div(classes = "docs-demo-box") { +"2" }
    }
  }
  p(classes = "docs-demo-label") { +"repeat-mode=\"fit\": empty tracks collapse, items stretch" }
  div {
    classes = setOf("docs-demo-resize")
    oblyxGrid(minColumnWidth = 100, repeatMode = OblyxGridRepeatMode.FIT, gap = OblyxGridGap._3) {
      div(classes = "docs-demo-box") { +"1" }
      div(classes = "docs-demo-box") { +"2" }
    }
  }
}

Capping the column count

max-columns caps how many tracks the responsive mode will create, without abandoning the intrinsic resize behavior: the track minimum is clamped by the cap, but the track maximum stays 1fr, so the present columns still stretch to fill the row rather than leaving dead space on a narrow viewport.

Grid with max-columns

Resize the frame: it never exceeds 3 columns, but a lone column still fills the width.

1
2
3
4
5
6
HTMLKotlin
<div class="docs-demo-resize">
  <oblyx-grid min-column-width="100" max-columns="3" gap="3">
    <div class="docs-demo-box">1</div>
    <div class="docs-demo-box">2</div>
    <div class="docs-demo-box">3</div>
    <div class="docs-demo-box">4</div>
    <div class="docs-demo-box">5</div>
    <div class="docs-demo-box">6</div>
  </oblyx-grid>
</div>
div {
  classes = setOf("docs-demo-resize")
  oblyxGrid(minColumnWidth = 100, maxColumns = 3, gap = OblyxGridGap._3) {
    div(classes = "docs-demo-box") { +"1" }
    div(classes = "docs-demo-box") { +"2" }
    div(classes = "docs-demo-box") { +"3" }
    div(classes = "docs-demo-box") { +"4" }
    div(classes = "docs-demo-box") { +"5" }
    div(classes = "docs-demo-box") { +"6" }
  }
}

Gap

gap sets both axes at once, from the same spacing-token scale oblyx-stack and oblyx-layout use. row-gap and column-gap override one axis independently when they're also set.

Grid with independent row and column gap

1
2
3
4
5
6
HTMLKotlin
<oblyx-grid columns="3" row-gap="1" column-gap="6">
  <div class="docs-demo-box">1</div>
  <div class="docs-demo-box">2</div>
  <div class="docs-demo-box">3</div>
  <div class="docs-demo-box">4</div>
  <div class="docs-demo-box">5</div>
  <div class="docs-demo-box">6</div>
</oblyx-grid>
oblyxGrid(columns = 3, rowGap = OblyxGridRowGap._1, columnGap = OblyxGridColumnGap._6) {
  div(classes = "docs-demo-box") { +"1" }
  div(classes = "docs-demo-box") { +"2" }
  div(classes = "docs-demo-box") { +"3" }
  div(classes = "docs-demo-box") { +"4" }
  div(classes = "docs-demo-box") { +"5" }
  div(classes = "docs-demo-box") { +"6" }
}

Alignment

align (align-items) and justify (justify-items) position each item within its own track when the item is smaller than the track. Both default to stretch, matching upstream.

Grid item alignment

Short
A taller
cell
Short
HTMLKotlin
<oblyx-grid columns="3" gap="3" align="center" justify="center" row-height="80">
  <div class="docs-demo-box">Short</div>
  <div class="docs-demo-box">A taller<br />cell</div>
  <div class="docs-demo-box">Short</div>
</oblyx-grid>
oblyxGrid(columns = 3, gap = OblyxGridGap._3, align = OblyxGridAlign.CENTER, justify = OblyxGridJustify.CENTER, rowHeight = 80) {
  div(classes = "docs-demo-box") { +"Short" }
  div(classes = "docs-demo-box") { +"A taller cell" }
  div(classes = "docs-demo-box") { +"Short" }
}

Reference

<oblyx-grid>

AttributeTypeDefaultDescription
columnsnumber | undefinedFixed track count -> repeat(columns, 1fr). Mutually exclusive with min-column-width's responsive mode; if both are set, this wins. Unset (and no min-column-width either) -> a single 1fr column, matching upstream's no-columns default.
min-column-widthnumber | undefinedMinimum track width in pixels for the responsive auto-fill/ auto-fit mode (upstream's columns={{minWidth}}). Ignored when columns is set.
max-columnsnumber | undefinedCaps the column count in responsive mode (upstream's columns={{minWidth, max}}). Meaningless without min-column-width.
repeat-modefillfitfillauto-fill (default) vs auto-fit in responsive mode.
gap00-511-523456810Gap between tracks on both axes, as a spacing-token step. Overridden per-axis by row-gap/column-gap when they're also set.
row-gap00-511-523456810Row gap override.
column-gap00-511-523456810Column gap override.
row-heightnumber | undefinedFixed row height in pixels (grid-auto-rows), for masonry-style layouts combined with oblyx-grid-span row spans. Unset -> auto.
alignstartcenterendstretchstretchCross-axis-per-item alignment (align-items).
justifystartcenterendstretchstretchCross-axis-per-item alignment on the inline axis (justify-items).

<oblyx-grid-span>

AttributeTypeDefaultDescription
columnsnumber | undefinedNumber of columns to span (grid-column: span columns). Mutually exclusive with columns-full, which wins if both are set.
columns-fullbooleanfalseSpans every column (grid-column: 1 / -1). An explicit boolean rather than overloading columns with a 'full' string value: attributes are the API, and a dedicated flag reads as intent rather than a magic string.
rowsnumber | undefinedNumber of rows to span (grid-row: span rows).