How to specify custom underline strokes based on default stroke thickness?

The underline() function uses a stroke thickness that is based on the font by default, e.g.

#underline(stroke: "loosely-dotted")[#lorem(10)]

I can define a custom stroke like so:

#underline(stroke: stroke(dash: (1pt, 2pt)))[#lorem(10)]

But there is no obvious way to define a custom stroke that also uses the font-derived thickness, i.e. something along the lines of

#underline(
  stroke: stroke(
    dash: (default-thickness, 0.7 * default-thickness),
  ),
)[#lorem(10)]

Is there any existing method to achieve this? Or would this rather be a feature request?

I’m unsure what you mean by “thickness”, but if you want to make the spacing be relative to the current font size you should use “em” length units instead of “pt”.

So something like

#underline(stroke: stroke(
  thickness: 0.1em,
  dash: (1em, 0.7em)
),)[#lorem(10)]

If you mean relative to font weight instead of font size, it’d probably be easiest to do with a context expression

What I refer to by thickness is really just that: The default value for stroke.thickness that Typst will choose when using, say, stroke(dash: "loosely-dotted").

So, in other words, what I’m asking is the default value for stroke.thickness in the context of an underline. Maybe, that’s just a documentation issue (if it turns out to be something like 0.1em as you suggest). But if it’s somehow derived differently from other font metrics, I’d be curious if and how the default value can be obtained in code.

Your second link points to the definition of the dash pattern, which doesn’t adjust the thickness. All it does is adjust the length and spacing of the underlined dashes.

From the first link, it states that the thickness is derived as follows: “If set to auto, the value is inherited, defaulting to 1pt.”

Sorry, it seems that I haven’t described sufficiently precise what my goal is:

For underlines, the default is different, namely it is based on the font, not simply 1pt. This becomes obvious when changing font size: The line thickness will scale as well in this situation.

You’re right that the dash pattern is in principle independent of stroke thickness. However, as the link to the Typst source code shows, it is in fact based on the thickness for dotted patterns (by setting dash length = thickness to obtain dots).

What I want to achieve is

  • keep the default thickness of the underline, which depends on the font in some undocumented way
  • have a dash pattern with dots, i.e. set the first element of the pattern array to the same value as the thickness
  • adjust the dot spacing, i.e. the second value of the pattern array, to be different from both the dotted, loosely-dotted and densely-dotted defaults (because I don’t like their looks)

Ah I understand now, sorry for the confusion.

Indeed if you pass a troke to the underline function, the thickness default is based on the font thickness, or defaulted to 0.06em.

Because this value is based on the actual font you are using (i.e. some fonts will set it to different values), the pt/em value is not exposed through typst.

You can, however, use the string “dot” for a length equal to the line thickness in stroke, so something like this would work:

#underline(stroke: stroke(
  // works
  dash: ("dot", 7pt)
),)[#lorem(10)]

Downside with this is that you can’t set it to 0.7 times the thickness, so this wouldn’t work

#underline(stroke: stroke(
  // does not work
  dash: ("dot",  0.7 * "dot")
),)[#lorem(10)]

It seems the best way to achieve “relative thickness” would be through trial and error, or by exploring the .otf themselves. Maybe somebody else knows a better solution.

1 Like

Nice, thanks, this is actually very helpful already: I’m pretty happy with dash: ("dot", "dot") in this case, which is even denser than densely-dotted for the font in question.

I completely missed the info on "dot" when reading the documentation. I’ll probably open a GitHub issue, asking to amend the docs slightly: I feel like it would be helpful if the actual values for the shorthands would be specified. (e.g. densely-dotted is ("dot", 1pt) but if I wanted to slightly tweak that, I currently need to first read the Typst source to figure out what densely-dotted corresponds to.)

No problem. I agree that the docs could do with some improvement, and it would also be nice if instead of "dot" we had something concrete like underline.thickness which we could use set rules on, and that we could e.g. multiply (it wouldn’t quite work like this because of the way stroke is implemented, but still). Of course, this would be a separate issue

see `stroke` documentation does not provide definitions for dash pattern shorthands · Issue #5982 · typst/typst · GitHub