How to draw a vertically centered horizontal line?

I want to draw a vertically centered horizontal line that would fill all the empty space on the line. It should look like this:


Now I’m using this code to generate it:

#grid(
    columns: (auto, 1fr, auto),
    align: horizon + center,
    column-gutter: 5pt,
    text(fill: red)[abc],
    line(length: 100%, stroke: 0.5pt + red),
    text(fill: red)[def],
)

But using grid feels like an overkill. Also grid seems to add margins at the top and at the bottom. Is there any better solution?

line is a block-level element, this means that it basically introduces a parbreak if necessary. Usually, to put a block level element inline you wrap it in box, which is the defacto inline container. However, this will actually require you to adjust the baseline of the line manually because it will be bottom aligned. You’re solution is already spot on.

The margins at the top and bottom are introduced by block.spacing as the grid itself is a block-level element, and the solution for this is to introduce a box to make it inline:

#box(grid(
    columns: (auto, 1fr, auto),
    align: horizon + center,
    column-gutter: 5pt,
    text(fill: red)[abc],
    line(length: 100%, stroke: 0.5pt + red),
    text(fill: red)[def],
))

This also has the effect of being able to put this within text if you add other fractionally spaced elements beside it.

Here’s an image showing the left side with the the box-wrapping and the right without it:

If that’s a common separator you can of course define a function that does it for you:

#let sep(left, right) = box(grid(
    columns: (auto, 1fr, auto),
    align: horizon + center,
    column-gutter: 5pt,
    left,
    line(length: 100%, stroke: 0.5pt),
    right,
))

Keep in mind that grid is subject to set rules and something like grid.inset may be something you want to explicitly set to nothing for this to avoid any other spacing issues.

1 Like
#set par(justify: true)
#lorem(20)

// Simplified example:
#grid(
  columns: (auto, 1fr, auto),
  align: horizon,
  column-gutter: 5pt,
  text(red)[abc],
  line(length: 100%, stroke: 0.5pt + red),
  text(red)[def],
)

// Reusable wrapper:
#let hline = {
  let color = red
  set text(color)
  set line(stroke: color)
  grid(
    columns: (auto, 1fr, auto),
    align: horizon,
    column-gutter: 5pt,
    [abc],
    line(length: 100%, stroke: 0.5pt),
    [def],
  )
}
#hline

// Quick and dirty one-liner:
#let hline = text(red)[abc #box(width: 1fr, baseline: -0.25em, stroke: (bottom: 0.5pt + red)) def]
#hline

// Same as above, but more structured:
#let hline = {
  let color = red
  set text(color)
  set box(stroke: (bottom: 0.5pt + color))
  "abc " + box(width: 1fr, baseline: -0.25em) + " def"
}
#hline

#lorem(20)

Note that output line thickness depends on PDF viewer and its settings/zoom level. Also, I used -0.25em just because it looks centered, not sure if this is the exact value that always will work.

Since I don’t know if you need to change the text on the sides of the horizontal line, I didn’t make them settable via function arguments. But if you need something like that, then you can take a look at @Tinger’s solution.

1 Like