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 = OblyxSpacingStep._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 = OblyxSpacingStep._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 = OblyxSpacingStep._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 = OblyxSpacingStep._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 = OblyxSpacingStep._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 = OblyxSpacingStep._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 = OblyxSpacingStep._1, columnGap = OblyxSpacingStep._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 take the same OblyxGridAlignment enum and 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 = OblyxSpacingStep._3, align = OblyxGridAlignment.CENTER, justify = OblyxGridAlignment.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).