I was messing about with typst styling rules while I was trying to get a specific style. What I had in mind was to apply a certain style to all the text before the first header and a different style to all the text after the first header. Here is an example I set up with my own crude implementation.
#let color-state = state("color", false)
#show heading: it => {
set text(fill: primary-red)
}
#show par: it => {
context {
let is-action = color-state.get()
let txt-color = if is-action { black } else { red }
set text(fill: txt-color)
block(
text(it.body)
)
}
}
#show heading.where(level: 4): it => block[
#text(it.body, size: 1.3em, fill: red, weight: "light")
#v(-0.5em)
#line(length: 100%, stroke: 1pt + red)
#color-state.update(true)
]
This is some text before the first header, which needs to be red and is red
==== This is the first header that needs to be red and is red
This is the text after the first header, which needs to be black and is black
==== This is the second header that also needs to be red but is black
It all works good and well, maybe even too good. I want the headings themselves to be a different style (in this case just red) but I couldn’t get the show rule for headings to override the show rule for paragraphs. I tried putting it before, after and inside to no avail.
The problem arises because you are stripping away the heading element when you use #text(it.body, ...). This makes the show rule return a par element, which matches with the other show rule. Simply keeping the entire heading, i.e. #text(it, ...) fixes the issue.
Also, you should prefer using show-set rules whenever possible, as this allows for more customisation later (see: Is it possible to call set multiple times in a single show rule? - #8 by ensko)
In your example, this would be
#show heading.where(level: 4): set text(size: 1.3em, fill: red, weight: "light")
#show heading.where(level: 4): set block(below: 0.7em)
#show heading.where(level: 4): it => {
show divider: set line(length: 100%, stroke: 1pt + red)
show divider: box
it + divider() + color-state.update(true)
}
So that later in the document, if you wanted to e.g. change the line to asterix’s you could just write
#show heading.where(level: 4): it => {
show divider: set align(center)
show divider: block[\* \* \*]
show block: set text(fill: red)
it
}
Instead of re-writing the whole show rule.
1 Like
Changing it.body to just it causes the whole text to disappear for some reason. So having:
#let color-state = state("color", false)
#show heading: it => {
set text(fill: red)
}
#show par: it => {
context {
let is-action = color-state.get()
let txt-color = if is-action { black } else { red }
set text(fill: txt-color)
block(
text(it.body)
)
}
}
#show heading.where(level: 4): it => block[
#text(it, size: 1.3em, fill: red, weight: "light")
#v(-0.5em)
#line(length: 100%, stroke: 1pt + red)
#color-state.update(true)
]
This is some text before the first header, which needs to be red and is red
==== This is the first header that needs to be red and is red
This is the text after the first header, which needs to be black and is black
==== This is the second header that also needs to be red but is black
Results in this:
I will try to remember your advice about prioritizing show set rules, but first I need to figure this out.