{"id":303448,"date":"2020-02-26T08:04:24","date_gmt":"2020-02-26T15:04:24","guid":{"rendered":"https:\/\/css-tricks.com\/?p=303448"},"modified":"2020-02-26T09:15:22","modified_gmt":"2020-02-26T16:15:22","slug":"weaving-a-line-through-text-in-css","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/weaving-a-line-through-text-in-css\/","title":{"rendered":"Weaving a Line Through Text in CSS"},"content":{"rendered":"\n

Earlier this year, I came across this demo<\/a> by Florin Pop, which makes a line go either over or under the letters of a single line heading. I thought this was a cool idea, but there were a few little things about the implementation I felt I could simplify and improve at the same time.<\/p>\n\n\n\n\n\n\n\n

\"\"<\/figure>\n\n\n\n

First off, the original demo duplicates the headline text, which I knew could be easily avoided. Then there’s the fact that the length of the line going through the text is a magic number, which is not a very flexible approach. And finally, can’t we get rid of the JavaScript?<\/p>\n\n\n\n

So let’s take a look into where I ended up taking this.<\/p>\n\n\n

HTML structure<\/h3>\n\n\n

Florin puts the text into a heading element and then duplicates this heading, using Splitting.js<\/a> to replace the text content of the duplicated heading with spans, each containing one letter of the original text.<\/p>\n\n\n\n

Already having decided to do this without text duplication, using a library to split the text into characters and then put each into a span<\/code> feels a bit like overkill, so we’re doing it all with an HTML preprocessor.<\/p>\n\n\n\n

- let text = 'We Love to Play';\n- let arr = text.split('');\n\nh1(role='image' aria-label=text)\n  - arr.forEach(letter => {\n    span.letter #{letter}\n  - });<\/code><\/pre>\n\n\n\n

Since splitting text into multiple elements may not work nicely with screen readers, we’ve given the whole thing a role<\/code> of image<\/code> and an aria-label<\/code>.<\/p>\n\n\n\n

This generates the following HTML:<\/p>\n\n\n\n

<h1 role=\"image\" aria-label=\"We Love to Play\">\n  <span class=\"letter\">W<\/span>\n  <span class=\"letter\">e<\/span>\n  <span class=\"letter\"> <\/span>\n  <span class=\"letter\">L<\/span>\n  <span class=\"letter\">o<\/span>\n  <span class=\"letter\">v<\/span>\n  <span class=\"letter\">e<\/span>\n  <span class=\"letter\"> <\/span>\n  <span class=\"letter\">t<\/span>\n  <span class=\"letter\">o<\/span>\n  <span class=\"letter\"> <\/span>\n  <span class=\"letter\">P<\/span>\n  <span class=\"letter\">l<\/span>\n  <span class=\"letter\">a<\/span>\n  <span class=\"letter\">y<\/span>\n<\/h1><\/code><\/pre>\n\n\n

Basic styles<\/h3>\n\n\n

We place the heading in the middle of its parent (the body<\/code> in this case) by using a grid<\/code> layout:<\/p>\n\n\n\n

body {\n  display: grid;\n  place-content: center;\n}<\/code><\/pre>\n\n\n\n
\"Screenshot
The heading doesn’t stretch across its parent to cover its entire width<\/code>, but is instead placed in the middle.<\/figcaption><\/figure>\n\n\n\n

We may also add some prettifying touches, like a nice font<\/code> or a background<\/code> on the container.<\/p>\n\n\n\n

Next, we create the line with an absolutely positioned ::after<\/code> pseudo-element of thickness (height<\/code>) $h<\/code>:<\/p>\n\n\n\n

$h: .125em;\n$r: .5*$h;\n\nh1 {\n  position: relative;\n  \n  &::after {\n    position: absolute;\n    top: calc(50% - #{$r}); right: 0;\n    height: $h;\n    border-radius: 0 $r $r 0;\n    background: crimson;\n  }\n}<\/code><\/pre>\n\n\n\n

The above code takes care of the positioning and height<\/code> of the pseudo-element, but what about the width<\/code>? How do we make it stretch from the left edge of the viewport to the right edge of the heading text?<\/p>\n\n\n

Line length<\/h3>\n\n\n

Well, since we have a grid<\/code> layout where the heading is middle-aligned horizontally, this means that the vertical midline of the viewport coincides with that of the heading, splitting both into two equal-width halves:<\/p>\n\n\n\n

\"SVG
The middle-aligned heading.<\/figcaption><\/figure>\n\n\n\n

Consequently, the distance between the left edge of the viewport and the right edge of the heading is half the viewport width (50vw<\/code>) plus half the heading width, which can be expressed as a %<\/code> value when used in the computation of its pseudo-element’s width<\/code>.<\/p>\n\n\n\n

So the width<\/code> of our ::after<\/code> pseudo-element is:<\/p>\n\n\n\n

width: calc(50vw + 50%);<\/code><\/pre>\n\n\n

Making the line go over and under<\/h3>\n\n\n

So far, the result is just a crimson line crossing some black text:<\/p>\n\n\n\n