Hey all,
this is my first question here and I would appreciate your assistance.
I’m trying to write a template, where:
- Count all level 1 headings and store the total in a variable called
X
.
- Sample
X
different colors from a gradient and store them in an array, A
, with X
entries.
- Assign each of the
X
headings one of the entries from array A
.
I’ve tried quite a few approaches, but I seem to be struggling with the last part. It prints each heading X
times for each color instead of just once.
Also everything has to remain in the context
which is not very elegant.
#set heading(numbering: "1")
#context[
//get the number of level-one headings
#let counter = counter(heading).final().at(0)
#counter
//get array of ratios
#let ratios = ()
#for i in range(1, counter+1) {
ratios.push(i*(100/counter)*1%)
}
#ratios
//generate colors
#let grad = gradient.linear(..color.map.rainbow).samples(..ratios)
#grad
#show heading: it => for i in range(counter){
//if i+1 == counter(heading).at(i){
align(center, text(font: "Poppins", fill: grad.at(i), it.body))
}
]
= one
= two
= three
Thank you in advance!
Are you trying to make each heading a different color? if so, the best way would be to use a show rule that applies to headings:
#show heading: it => context [
//get the number of level-one headings
#let finalcounter = counter(heading).final().at(0)
#let currentcounter = counter(heading).at(here()).at(0)
#let ratio = currentcounter / finalcounter * 100%
//generate color
#let grad = gradient.linear(..color.map.rainbow).sample(ratio)
#align(center, text(font: "Poppins", fill: grad, it.body))
]
= one
= two
= three
If instead you are trying to get a sort of outline of all headings at the beginning, you can do that like so
#context [
//get all level-one headings
#let allheadings = query(heading.where(level: 1))
#for (i, h) in allheadings.enumerate() {
let ratio = i / allheadings.len() * 100%
//generate color
let grad = gradient.linear(..color.map.rainbow).sample(ratio)
align(center, text(font: "Poppins", fill: grad, h.body))
}
]
= one
= two
= three
If you are curious about why your code didn’t work, it is because you are doing a for loop for each heading, so each heading gets displayed as the output of the for loop, leaving you with X copies.
2 Likes
Oh my gosh, it works! Instead of dealing with multiple values and attempting to apply the show rule to each one individually, you can simply consolidate everything into a single show rule that will be applied to each heading separately. It’s such a clever solution!
No problem. Though do note that taking the value from the counter can lead to unwanted behavior if it’s manually set in the document:
= one
= two
#counter(heading).update(1)
= three
Summary
So if this is a problem, you should use queries instead of relying on counters :)
I haven’t looked at queries yet. Would you mind elaborating a bit?
Sure. All you need to do is replace the lines up to calculating the ratio with
//get all level-one headings
#let allheadings = query(heading.where(level: 1))
#let currentheading = query(heading.where(level: 1).before(here()))
#let ratio = currentheading.len() / allheadings.len() * 100%
This will calculate the ratio using the number of headings in the document, instead of relying on the counter whose value can be changed arbitrarily