How to style table.header / table.footer?

What is the recommended way to style the table.header and table.footer elements?

2 Likes

I don’t think you can at the moment, see Unable to style `table.header` and `table.footer` · Issue #3640 · typst/typst · GitHub.

Depending on the use case it’s still possible to add some styling, the table header is always the first row, the numerous parameters of table which allow passing functions can easily filter out what a header and footer is.

So this works perfectly fine:

#table(
  columns: 3,
  fill: (x, y) => if y == 0 { gray },
  table.header[A][B][C],
  ..range(9).map(x => [#x])
)

image

Table guide – Typst Documentation goes over a bunch of table formatting options and how to use them effectively.

This works well for the header, but to my knowledge it isn’t possible to style the footer because there’s no (straightforward) way to get the amount of rows of a table, or is there?
Would be handy for a result/sum row to make it stand out.

I think so, the functions don’t carry any totals, nor can they easily without making simple use cases harder.

I think I landed in a similar spot, I wanted to set a table style similar to that in the picture:

I am stuck at how to identify the last row and how to get some padding between the table cell and the stroke.

I got this so far:

#import table: cell, header
#show table.cell.where(y:0): set text(weight: 700)
#set table(
    stroke: (x, y) => 
    if y == 0 { // First row gets top and bottom stroke
      (
        top: (0.3pt),
        bottom: (0.3pt)
      )
    } else if y == calc.last(y) { // Last row gets a bottom stroke
      (
        top: none,
        bottom: (0.3pt)
      )
    },
  fill: (x, y) =>
    if y == 0 {
      gray.transparentize(80%)
    } else if calc.even(y) {
      red.transparentize(90%)
    } else {
      none
    },
  inset: (x: 0.4em, y: 0.4em),
  gutter: 0em,
  )

Any ideas/help for someone who hardly knows what he does…

The following might be useful to you. The show rule feels kinda hacky though, it would probably be best to just use a custom function directly to create the header and footer. I got the spacing by just inserting empty cells above and below :)

#let header-args(children, cols) = {
  arguments(
    table.cell(stroke: (top: 1pt), inset: 2pt, fill: none, colspan: cols, none),
    ..children.map(c => {
      let c_fields = c.fields()
      let body = c_fields.remove("body")
      table.cell(
        fill: gray.lighten(60%),
        ..c_fields,
        body,
      )
    }),
    table.cell(stroke: (bottom: 1pt), inset: 2pt, fill: none, colspan: cols, none),
  )
}

#show table: it => {
  let fields = it.fields()

  if fields.at("label", default: none) == <table-show-recursion-stop> {
    it
  } else {
    let children = fields.remove("children")
    if children.at(0).func() == table.header {
      children.at(0) = table.header(
        ..header-args(children.at(0).children, fields.columns.len()),
      )
    }
    if children.at(-1).func() == table.footer {
      children.at(-1) = table.footer(
        ..header-args(children.at(-1).children, fields.columns.len()),
      )
    }
    [#table(..fields, ..children) <table-show-recursion-stop>]
  }
}
#set table(
  stroke: none,
  fill: (x, y) => {
    if calc.odd(y) {
      none
    } else {
      blue.lighten(92%)
    }
  },
)

#table(
  columns: 6,
  align: (left,) + (center,) * 5,
  table.header(
    [Test],
    [Anomalies],
    [Warnings],
    [Correct],
    [Categories],
    [Missed],
  ),

  [Connection [3]], $2$, $2$, $1$, $C$, $1$,
  [Coordinates'03 [1]], $1$, $4$, $1$, $2B, 1C$, $0$,
  [Local Variable [1]], $1$, $2$, $1$, $A$, $0$,
  [NASA [1]], $1$, $1$, $1$, [---], $0$,
  [Coordinates'04 [2]], $1$, $4$, $1$, $3C$, $0$,
  [Buffer [2]], $0$, $7$, $0$, $2A, 1B, 2C, 2D$, $0$,
  [Double-Check [2]], $0$, $2$, $0$, $1A, 1B$, $0$,
  [StringBuffer [8]], $1$, $0$, $0$, [---], $1$,
  [Account [14]], $1$, $1$, $1$, [---], $0$,
  [Jigsaw [14]], $1$, $2$, $1$, $C$, $0$,
  [Over-reporting [14]], $0$, $2$, $0$, $1A, 1C$, $0$,
  [Under-reporting [14]], $1$, $1$, $1$, [---], $0$,
  [Allocate Vector [10]], $1$, $2$, $1$, $C$, $0$,
  [Knight Moves [3]], $1$, $3$, $1$, $2B$, $0$,

  table.footer([Total], $12$, $33$, $10$, $5A, 6B , 10C, 2D$, $2$),
)
Output

Thanks, looks great. I will check it out in detail asap.