Unfortunately, recursive show rules are still kind of cursed, so if I try to use underline inside show rule for underline, it would be pretty bad. Even several clever tricks I know didn’t save me there.
So, there are two options: either get rid of show rule or use element other than underline inside the rule:
// creating custom function for multiunderline
#let underbrow(it) = {
underline(underline(underline(it, stroke: red), stroke: yellow, offset: 1.5pt), stroke: blue, offset: 2pt)
}
#underbrow[Hello world!]
// creating show rule with boxes' lower edges
#show underline: it => {
let cbox(color, body) = {
box(
body,
stroke: (bottom: color + 1pt),
inset: (bottom: 1pt))
}
cbox(blue, cbox(yellow, cbox(red, it.body)))
}
#underline[Hello world!]
Here is the result, of course, pretty twickable in both cases:
Drawbacks
Custom function: you will have to replace all underline in your document with this function. That includes all files you include or import. If underlining is done by a package, it can’t be tweaked this way.
Show rule: unfortunately, box is not underline. For example, it is not breakable. This underline can’t easily evade the spaces. You can do it, of course, with some magic by splitting input by words, and putting each word in a box separately, but it would still be limited.
I expected this to work better, but I’ll leave it here in the hopes that someone more familiar with gradients can get it working properly. The idea was to underline with a stroke that uses a vertical gradient with the three target colors. For some reason red is always dominant and blue is always the least present.