Can you add a tag/label/class to an element/function?

Hello,

I’ve been trying very hard to create a separate functions.typ file for stuff that just computes things. A components.typ file for basically laying out stuff, so just determining the structure, and at this moment a testing.typ where I want to use those components and style them. My goal is to inject the least amount of styling stuff into the components, but there seems to be no way around it at this moment, is there?

In some cases you are able to set a style “globally”, like these kinds of things.

#set text(font: "Aptos", size: 12pt)
#set par(leading: 0.65em, spacing: 1.2em, justify: true, first-line-indent: 1em, hanging-indent: 0em)
#set grid(gutter: 0.65em)
#set table(gutter: 0em, inset: (x: 0.65em / 2, y: 0.65em / 2), stroke: none)
#set table(fill: (x, y) => if y >= 2 and calc.even(y) { luma(90%) })

#show heading: it => block[#text(font: "Aptos Display", fill: navy, size: 16pt, weight: "bold")[#underline(text(it.body))]]
#show table.cell.where(y: 0): it => {
  set text(fill: navy, weight: "bold")
  underline(it)
}

Now comes my real question, which portrays just as an example by the way: can I target only table.header instead of “hoping” that the first row is the header? Maybe I could target the header cells directly, or a table that has some specific attribute. With CSS this is easy: you’d add a class to it and go. In other words: could I do something similar with Typst?

I’ve searched and I’m not entirely sure if I missed something. I’ve seen yesterday (late) night there is an open ticket about something related on GitHub. But I figured I’d ask more directly just to be sure I didn’t overlook yet again something trivial :nerd_face:.


Another case where I’d like to use it, is this

#let action-point-def(fill: maroon, id) = {
  let value = str(id)
  while value.len() < 3 { value = "0" + value }

  set text(fill: fill)
  super[#value #label("action-point-" + value)]
}

#let action-point-ref(fill: maroon, id) = {
  let value = str(id)
  while value.len() < 3 { value = "0" + value }

  set text(fill: fill)
  super[#link(label("action-point-" + value))[#value]]
}

#action-point-def(1)
#action-point-ref(1)

I don’t want to define the default color in my components.typ here. But I definitely don’t want to provide the fill for each usage either, so the default is my best option right now. But if I would want them to be green and red, I can no longer really reuse this no more. With a CSS class like construct I would be able to I think. I’ve asked my LLM “partner” and it simply comes up with dozens of fake stuff that doesn’t work :sweat_smile:


The closest I found is something like this, but it can’t specifically select something

#let appendix-heading(it) = {
  set align(center)
  it
  line(length: 100%, stroke: 1pt)
  v(1em)
}

#show heading: appendix-heading
= An appendix chapter

Sadly, no. The issue you probably found (#3640) has current status and some workarounds. If you don’t want to use the table-header function form the linked comments, you can also do something like this:

#show table: it => {
  let has-header = it.children.first(default: []).func() == table.header
  show table.cell: it => {
    set text(fill: red) if has-header and it.y == 0
    it
  }
  it
}

The answer kind of depends how you want to be able to redefine the colors. If you want to be able to easily redefine the function for a new color you can use the .with method to change the color, e.g.

#let AP-def = action-point-def.with(fill: green)

#AP-def(2)

(you can also override the default variable name if you so wish)

If you instead want the color to change depending on where the user called the function in the document, e.g. if you want to make a template which automatically changes the color in the appendix but nowhere else such that the user doesn’t need to refefine the color, you’ll need to use some kind of stateful function:

#let AP-color = state("AP-color", maroon)

#let action-point-def(id) = context {
  let value = str(id)
  while value.len() < 3 { value = "0" + value }

  set text(fill: AP-color.get())
  super[#value #label("action-point-" + value)]
}

#let action-point-ref(id) = context {
  let value = str(id)
  while value.len() < 3 { value = "0" + value }

  set text(fill: AP-color.get())
  super[#link(label("action-point-" + value))[#value]]
}

#action-point-def(1)
#action-point-ref(1)

#AP-color.update(green)

#action-point-def(2)

While the syntax here is pretty similar to the above, the color can be updated independently of user interaction as mentioned before. For example you could have a main.typ file where you update the color depending on the chapter:

// main.typ
#include "chapter1.typ"

#AP-color.update(green)

#include "chapter2.typ"

#AP-color.update(blue)

#include "appendix.typ"

This seems unrelated to the above, but I can’t exactly parse what you are asking :sweat_smile: