Documentation

gridElement
Question mark

Arranges content in a grid.

The grid element allows you to arrange content in a grid. You can define the number of rows and columns, as well as the size of the gutters between them. There are multiple sizing modes for columns and rows that can be used to create complex layouts.

While the grid and table elements work very similarly, they are intended for different use cases and carry different semantics. The grid element is intended for presentational and layout purposes, while the table element is intended for, in broad terms, presenting multiple related data points. Set and show rules on one of these elements do not affect the other. Refer to the Accessibility Section to learn how grids and tables are presented to users of Assistive Technology (AT) like screen readers.

Sizing the tracks

A grid's sizing is determined by the track sizes specified in the arguments. There are multiple sizing parameters: columns, rows and gutter. Because each of the sizing parameters accepts the same values, we will explain them just once, here. Each sizing argument accepts an array of individual track sizes. A track size is either:

To specify a single track, the array can be omitted in favor of a single value. To specify multiple auto tracks, enter the number of tracks instead of an array. For example, columns: 3 is equivalent to columns: (auto, auto, auto).

Examples

The example below demonstrates the different track sizing options. It also shows how you can use grid.cell to make an individual cell span two grid tracks.

// We use `rect` to emphasize the
// area of cells.
#set rect(
  inset: 8pt,
  fill: rgb("e4e5ea"),
  width: 100%,
)

#grid(
  columns: (60pt, 1fr, 2fr),
  rows: (auto, 60pt),
  gutter: 3pt,
  rect[Fixed width, auto height],
  rect[1/3 of the remains],
  rect[2/3 of the remains],
  rect(height: 100%)[Fixed height],
  grid.cell(
    colspan: 2,
    image("tiger.jpg", width: 100%),
  ),
)
Preview

You can also spread an array of strings or content into a grid to populate its cells.

#grid(
  columns: 5,
  gutter: 5pt,
  ..range(25).map(str)
)
Preview

Styling the grid

The grid and table elements work similarly. For a hands-on explanation, refer to the Table Guide; for a quick overview, continue reading.

The grid's appearance can be customized through different parameters. These are the most important ones:

To meet different needs, there are various ways to set them.

If you need to override the above options for individual cells, you can use the grid.cell element. Likewise, you can override individual grid lines with the grid.hline and grid.vline elements.

To configure an overall style for a grid, you may instead specify the option in any of the following fashions:

#grid(
  columns: 5,

  // By a single value
  align: center,
  // By a single but more complicated value
  inset: (x: 2pt, y: 3pt),
  // By an array of values (cycling)
  fill: (rgb("#239dad50"), none),
  // By a function that returns a value
  stroke: (x, y) => if calc.rem(x + y, 3) == 0 { 0.5pt },

  ..range(5 * 3).map(n => numbering("A", n + 1))
)
Preview

On top of that, you may apply styling rules to grid and grid.cell. Especially, the x and y fields of grid.cell can be used in a where selector, making it possible to style cells at specific columns or rows, or individual positions.

Stroke styling precedence

As explained above, there are three ways to set the stroke of a grid cell: through grid.cell's stroke field, by using grid.hline and grid.vline, or by setting the grid's stroke field. When multiple of these settings are present and conflict, the hline and vline settings take the highest precedence, followed by the cell settings, and finally the grid settings.

Furthermore, strokes of a repeated grid header or footer will take precedence over regular cell strokes.

Accessibility

Grids do not carry any special semantics. Assistive Technology (AT) does not offer the ability to navigate two-dimensionally by cell in grids. If you want to present tabular data, use the table element instead.

AT will read the grid cells in their semantic order. Usually, this is the order in which you passed them to the grid. However, if you manually positioned them using grid.cell's x and y arguments, cells will be read row by row, from left to right (in left-to-right documents). A cell will be read when its position is first reached.

Parameters
Question mark

columns
auto or int or relative or fraction or array
Settable
Question mark

The column sizes.

Either specify a track size array or provide an integer to create a grid with that many auto-sized columns. Note that opposed to rows and gutters, providing a single track size will only ever create a single column.

See the track size section above for more details.

Default: ()

rows
auto or int or relative or fraction or array
Settable
Question mark

The row sizes.

If there are more cells than fit the defined rows, the last row is repeated until there are no more cells.

See the track size section above for more details.

Default: ()

gutter

The gaps between rows and columns. This is a shorthand to set column-gutter and row-gutter to the same value.

If there are more gutters than defined sizes, the last gutter is repeated.

See the track size section above for more details.

Default: ()

column-gutter
auto or int or relative or fraction or array
Settable
Question mark

The gaps between columns.

Default: ()

row-gutter
auto or int or relative or fraction or array
Settable
Question mark

The gaps between rows.

Default: ()

inset
relative or array or dictionary or function
Settable
Question mark

How much to pad the cells' content.

To specify a uniform inset for all cells, you can use a single length for all sides, or a dictionary of lengths for individual sides. See the box's documentation for more details.

To specify varying inset for different cells, you can:

See the styling section above for more details.

In addition, you can find an example at the table.inset parameter.

Default: (:)

align
auto or array or alignment or function
Settable
Question mark

How to align the cells' content.

If set to auto, the outer alignment is used.

You can specify the alignment in any of the following fashions:

See the styling section above for details.

In addition, you can find an example at the table.align parameter.

Default: auto

fill
none or color or gradient or array or tiling or function
Settable
Question mark

How to fill the cells.

This can be:

Most notably, arrays and functions are useful for creating striped grids. See the styling section above for more details.

View example
#grid(
  fill: (x, y) =>
    if calc.even(x + y) { luma(230) }
    else { white },
  align: center + horizon,
  columns: 4,
  inset: 2pt,
  [X], [O], [X], [O],
  [O], [X], [O], [X],
  [X], [O], [X], [O],
  [O], [X], [O], [X],
)
Preview

Default: none

stroke
none or length or color or gradient or array or stroke or tiling or dictionary or function
Settable
Question mark

How to stroke the cells.

Grids have no strokes by default, which can be changed by setting this option to the desired stroke.

If it is necessary to place lines which can cross spacing between cells produced by the gutter option, or to override the stroke between multiple specific cells, consider specifying one or more of grid.hline and grid.vline alongside your grid cells.

To specify the same stroke for all cells, you can use a single stroke for all sides, or a dictionary of strokes for individual sides. See the rectangle's documentation for more details.

To specify varying strokes for different cells, you can:

See the styling section above for more details.

View example: Passing a function to set a stroke based on position
#set page(width: 420pt)
#set text(number-type: "old-style")
#show grid.cell.where(y: 0): set text(size: 1.3em)

#grid(
  columns: (1fr, 2fr, 2fr),
  row-gutter: 1.5em,
  inset: (left: 0.5em),
  stroke: (x, y) => if x > 0 { (left: 0.5pt + gray) },
  align: horizon,

  [Winter \ 2007 \ Season],
  [Aaron Copland \ *The Tender Land* \ January 2007],
  [Eric Satie \ *Gymnopedie 1, 2* \ February 2007],

  [],
  [Jan 12 \ *Middlebury College \ Center for the Arts* \ 20:00],
  [Feb 2 \ *Johnson State College Dibden Center for the Arts* \ 19:30],

  [],
  [Skip a week \ #text(0.8em)[_Prepare your exams!_]],
  [Feb 9 \ *Castleton State College \ Fine Arts Center* \ 19:30],

  [],
  [Jan 26, 27 \ *Lyndon State College Alexander Twilight Theater* \ 20:00],
  [
    Feb 17 --- #smallcaps[Anniversary] \
    *Middlebury College \ Center for the Arts* \
    19:00 #text(0.7em)[(for a special guest)]
  ],
)
Preview
View example: Folding the stroke dictionary
#set page(height: 13em, width: 26em)

#let cv(..jobs) = grid(
  columns: 2,
  inset: 5pt,
  stroke: (x, y) => if x == 0 and y > 0 {
    (right: (
      paint: luma(180),
      thickness: 1.5pt,
      dash: "dotted",
    ))
  },
  grid.header(grid.cell(colspan: 2)[
    *Professional Experience*
    #box(width: 1fr, line(length: 100%, stroke: luma(180)))
  ]),
  ..{
    let last = none
    for job in jobs.pos() {
      (
        if job.year != last [*#job.year*],
        [
          *#job.company* - #job.role _(#job.timeframe)_ \
          #job.details
        ]
      )
      last = job.year
    }
  }
)

#cv(
  (
    year: 2012,
    company: [Pear Seed & Co.],
    role: [Lead Engineer],
    timeframe: [Jul - Dec],
    details: [
      - Raised engineers from 3x to 10x
      - Did a great job
    ],
  ),
  (
    year: 2012,
    company: [Mega Corp.],
    role: [VP of Sales],
    timeframe: [Mar - Jun],
    details: [- Closed tons of customers],
  ),
  (
    year: 2013,
    company: [Tiny Co.],
    role: [CEO],
    timeframe: [Jan - Dec],
    details: [- Delivered 4x more shareholder value],
  ),
  (
    year: 2014,
    company: [Glorbocorp Ltd],
    role: [CTO],
    timeframe: [Jan - Mar],
    details: [- Drove containerization forward],
  ),
)
Preview Preview

Default: (:)

children
content
Required Positional
Question mark
Variadic
Question mark

The contents of the grid cells, plus any extra grid lines specified with the grid.hline and grid.vline elements.

The cells are populated in row-major order.

Definitions
Question mark

cellElement
Question mark

A cell in the grid. You can use this function in the argument list of a grid to override grid style properties for an individual cell or manually positioning it within the grid. You can also use this function in show rules to apply certain styles to multiple cells at once.

For example, you can override the position and stroke for a single cell:

View example
#set text(15pt, font: "Noto Sans Symbols 2")
#show regex("[♚-♟︎]"): set text(fill: rgb("21212A"))
#show regex("[♔-♙]"): set text(fill: rgb("111015"))

#grid(
  fill: (x, y) => rgb(
    if calc.odd(x + y) { "7F8396" }
    else { "EFF0F3" }
  ),
  columns: (1em,) * 8,
  rows: 1em,
  align: center + horizon,

  [], [], [], [], [], [], [], [],
  [], [], [], [], [],  [], [], [],
  grid.cell(
    x: 4, y: 3,
    stroke: blue.transparentize(60%)
  )[],

  ..(grid.cell(y: 6)[],) * 8,
  ..([], [], [], [], [], [], [], [])
    .map(grid.cell.with(y: 7)),
)
Preview

You may also apply a show rule on grid.cell to style all cells at once, which allows you, for example, to apply styles based on a cell's position. Refer to the examples of the table.cell element to learn more about this.

body
content
Required Positional
Question mark

The cell's body.

x
auto or int
Settable
Question mark

The cell's column (zero-indexed). This field may be used in show rules to style a cell depending on its column.

You may override this field to pick in which column the cell must be placed. If no row (y) is chosen, the cell will be placed in the first row (starting at row 0) with that column available (or a new row if none). If both x and y are chosen, however, the cell will be placed in that exact position. An error is raised if that position is not available (thus, it is usually wise to specify cells with a custom position before cells with automatic positions).

View example
#let circ(c) = circle(
    fill: c, width: 5mm
)

#grid(
  columns: 4,
  rows: 7mm,
  stroke: .5pt + blue,
  align: center + horizon,
  inset: 1mm,

  grid.cell(x: 2, y: 2, circ(aqua)),
  circ(yellow),
  grid.cell(x: 3, circ(green)),
  circ(black),
)
Preview

Default: auto

y
auto or int
Settable
Question mark

The cell's row (zero-indexed). This field may be used in show rules to style a cell depending on its row.

You may override this field to pick in which row the cell must be placed. If no column (x) is chosen, the cell will be placed in the first column (starting at column 0) available in the chosen row. If all columns in the chosen row are already occupied, an error is raised.

View example
#let tri(c) = polygon.regular(
  fill: c,
  size: 5mm,
  vertices: 3,
)

#grid(
  columns: 2,
  stroke: blue,
  inset: 1mm,

  tri(black),
  grid.cell(y: 1, tri(teal)),
  grid.cell(y: 1, tri(red)),
  grid.cell(y: 2, tri(orange))
)
Preview

Default: auto

colspan
int
Settable
Question mark

The amount of columns spanned by this cell.

Default: 1

rowspan
int
Settable
Question mark

The amount of rows spanned by this cell.

Default: 1

inset
auto or relative or dictionary
Settable
Question mark

The cell's inset override.

Default: auto

align
auto or alignment
Settable
Question mark

The cell's alignment override.

Default: auto

fill
none or auto or color or gradient or tiling
Settable
Question mark

The cell's fill override.

Default: auto

stroke
none or length or color or gradient or stroke or tiling or dictionary
Settable
Question mark

The cell's stroke override.

Default: (:)

breakable
auto or bool
Settable
Question mark

Whether rows spanned by this cell can be placed in different pages. When equal to auto, a cell spanning only fixed-size rows is unbreakable, while a cell spanning at least one auto-sized row is breakable.

Default: auto

hlineElement
Question mark

A horizontal line in the grid.

Overrides any per-cell stroke, including stroke specified through the grid's stroke field. Can cross spacing between cells created through the grid's column-gutter option.

An example for this function can be found at the table.hline element.

y
auto or int
Settable
Question mark

The row above which the horizontal line is placed (zero-indexed). If the position field is set to bottom, the line is placed below the row with the given index instead (see grid.hline.position for details).

Specifying auto causes the line to be placed at the row below the last automatically positioned cell (that is, cell without coordinate overrides) before the line among the grid's children. If there is no such cell before the line, it is placed at the top of the grid (row 0). Note that specifying for this option exactly the total amount of rows in the grid causes this horizontal line to override the bottom border of the grid, while a value of 0 overrides the top border.

Default: auto

start
int
Settable
Question mark

The column at which the horizontal line starts (zero-indexed, inclusive).

Default: 0

end
none or int
Settable
Question mark

The column before which the horizontal line ends (zero-indexed, exclusive). Therefore, the horizontal line will be drawn up to and across column end - 1.

A value equal to none or to the amount of columns causes it to extend all the way towards the end of the grid.

Default: none

stroke
none or length or color or gradient or stroke or tiling or dictionary
Settable
Question mark

The line's stroke.

Specifying none removes any lines previously placed across this line's range, including hlines or per-cell stroke below it.

Default: 1pt + black

position
alignment
Settable
Question mark

The position at which the line is placed, given its row (y) - either top to draw above it or bottom to draw below it.

This setting is only relevant when row gutter is enabled (and shouldn't be used otherwise - prefer just increasing the y field by one instead), since then the position below a row becomes different from the position above the next row due to the spacing between both.

Default: top

vlineElement
Question mark

A vertical line in the grid.

Overrides any per-cell stroke, including stroke specified through the grid's stroke field. Can cross spacing between cells created through the grid's row-gutter option.

x
auto or int
Settable
Question mark

The column before which the vertical line is placed (zero-indexed). If the position field is set to end, the line is placed after the column with the given index instead (see grid.vline.position for details).

Specifying auto causes the line to be placed at the column after the last automatically positioned cell (that is, cell without coordinate overrides) before the line among the grid's children. If there is no such cell before the line, it is placed before the grid's first column (column 0). Note that specifying for this option exactly the total amount of columns in the grid causes this vertical line to override the end border of the grid (right in LTR, left in RTL), while a value of 0 overrides the start border (left in LTR, right in RTL).

Default: auto

start
int
Settable
Question mark

The row at which the vertical line starts (zero-indexed, inclusive).

Default: 0

end
none or int
Settable
Question mark

The row on top of which the vertical line ends (zero-indexed, exclusive). Therefore, the vertical line will be drawn up to and across row end - 1.

A value equal to none or to the amount of rows causes it to extend all the way towards the bottom of the grid.

Default: none

stroke
none or length or color or gradient or stroke or tiling or dictionary
Settable
Question mark

The line's stroke.

Specifying none removes any lines previously placed across this line's range, including vlines or per-cell stroke below it.

Default: 1pt + black

position
alignment
Settable
Question mark

The position at which the line is placed, given its column (x) - either start to draw before it or end to draw after it.

The values left and right are also accepted, but discouraged as they cause your grid to be inconsistent between left-to-right and right-to-left documents.

This setting is only relevant when column gutter is enabled (and shouldn't be used otherwise - prefer just increasing the x field by one instead), since then the position after a column becomes different from the position before the next column due to the spacing between both.

Default: start

headerElement
Question mark

A repeatable grid header.

If repeat is set to true, the header will be repeated across pages. For an example, refer to the table.header element and the grid.stroke parameter.

repeat
bool
Settable
Question mark

Whether this header should be repeated across pages.

Default: true

level
int
Settable
Question mark

The level of the header. Must not be zero.

This allows repeating multiple headers at once. Headers with different levels can repeat together, as long as they have ascending levels.

Notably, when a header with a lower level starts repeating, all higher or equal level headers stop repeating (they are "replaced" by the new header).

Default: 1

children
content
Required Positional
Question mark
Variadic
Question mark

The cells and lines within the header.

A repeatable grid footer.

Just like the grid.header element, the footer can repeat itself on every page of the table.

No other grid cells may be placed after the footer.

grid.footer() -> content

Whether this footer should be repeated across pages.

Default: true

The cells and lines within the footer.