Grow your CSS skills. Land your dream job.

Line-On-Sides Headers

Published by Chris Coyier

Forums user McAsh wondered how they might code up a design in which there was a large header and a smaller subheader beneath it which featured double horizontal lines jutting out after the text to both the left and right of the centered text. An easy thing to mock up as image, but a much more difficult thing to pull off in CSS because of the variable nature of text (length, size, etc).

The post mockup was this:

I coded up one way to do it which works, but isn't quite perfect. I figured I'd post it here and you all could chime in with better ways if you can think of them.

If the background was a solid color, this would be fairly easy. Apply the lined background to the subhead and center a span in the middle with a bit of padding and a background color to match the solid background. We don't have a solid background color here. Perhaps some trickery using the same background image but fixing the background position would work, but I didn't go there.

Instead, I used the ::before and ::after pseudo elements to create the left and right set of lines. The text is still in a span, which is relatively positioned. The right set is a pseudo element on that span which starts 100% from the left with a bit of margin to push it away, and vice versa for the left set. Both are of a fixed height and use border-top and border-bottom to create the lines. Thus no backgrounds are used and the insides of everything is transparent.

The length of the lines is long enough to always break out of the parent container, and they are cut off by hidden overflow on that parent.

.fancy {
  line-height: 0.5;
  text-align: center;
}
.fancy span {
  display: inline-block;
  position: relative;  
}
.fancy span:before,
.fancy span:after {
  content: "";
  position: absolute;
  height: 5px;
  border-bottom: 1px solid white;
  border-top: 1px solid white;
  top: 0;
  width: 600px;
}
.fancy span:before {
  right: 100%;
  margin-right: 15px;
}
.fancy span:after {
  left: 100%;
  margin-left: 15px;
}

Example:

Check out this Pen!

There are two things I don't love about this.

  1. The line-height: 0.5 thing in there to center the lines around the text feels like a "magic number" to me which might not be right depending on the font.
  2. The width of the pseudo elements lines isn't exactly what they need to be, but way longer than they need to be and cut off.

Neither are that huge of a big deal but little things that probably could be cleverly fixed.

Comments

  1. Awsome! Will be using on my next project!

  2. One clean way would be to use CSS gradients. Either with no fallbacks or a dataURL.

  3. Change the color.

  4. “fancy span” haha cleaver!

  5. I just finished pulling one of these off using a span to center the text and add the solid background color to act as the transparency with some padding.

    I never really gave much thought to if I wanted to have it on a background that doesn’t have a solid color.

    Thanks for this Chris.

  6. Chris, to address your “magic number” concern, instead of doing line-height: 0.5, you can use percentage-based positioning (top: 50%) on the pseudo-element. This also lets you put overflow: hidden on a .fancy element instead of the <article>, which is nice, in case you need to have other pseudo-elements escape the <article>.

    Here’s the code

    Since you always know the height of the double-line flourish, you can set a negative margin-top equal to half that height. Sass isn’t required, but I used it to show how this method doesn’t require guesstimate numbers.

    • I attempted to test the code you provided. I tried it off of Codepen… without using a custom font. My result was one solid line. Whereas the original method worked. I didn’t change the “magic number” when I was testing; so I don’t know if that will change.

    • Trevor
      Permalink to comment#

      This method is especially helpful when offsetting the lines to either the left or right

  7. I faced the same problem with a website I was building & I coded it like this

    The site has two variations of this one that spans the full width of the container & another one which doesn’t fill 100%

    The only thing I don’t like here is that whenever I want to use it on different background I have to change the span background color.

    • zoorock

      thx for your code, refactored it for our usecase and works like a charm. …

    • ulygun
      Permalink to comment#

      Nice solution , but i have a question, how can I apply this to an image (like a logo), instead of a text?

  8. thurston blodgett
    Permalink to comment#

    You may want to check out this:

    http://pepsized.com/demo/?id=614

    This is how she handled it:

    h1:after {
    content: “”;
    height: 2px;
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    top: .5em;
    z-index: -1;
    border-top: 1px solid #504331;
    border-bottom: 1px solid #504331;
    }

  9. Josiah
    Permalink to comment#

    Nice use of “embiggen” as a class name! It is a perfectly cromulent word. :)

  10. Can you use flexbox on pseudo-elements? That seems like it would make it easier to do this kind of thing.

  11. Chuck Barlow
    Permalink to comment#

    Here’s how to do it with flexbox (Chrome only), as suggested by Aaron: http://codepen.io/anon/pen/IKbFx

    And here’s another way to do it without flexbox, which removes the line-height “magic number” issue: http://codepen.io/anon/pen/crEJa

    The second solution sets the width of p.fancy to 200%, then shifts the text back using text-indent of -50% (50% of 200% = 100%). That allows for using inline-block pseudo elements, instead of absolutely positioned ones.

  12. Chuck Barlow
    Permalink to comment#

    Here’s how to do it with flexbox (Chrome only), as suggested by Aaron: http://codepen.io/anon/pen/IKbFx

    And here’s another way to do it without flexbox, which removes the line-height “magic number” issue: http://codepen.io/anon/pen/crEJa

    The second solution sets the width of p.fancy to 200%, then shifts the text back using text-indent of -50% (50% of 200% = 100%). That allows for using inline-block pseudo elements, instead of absolutely positioned ones.

  13. jimniceguy
    Permalink to comment#

    how about this?

    span:before {content: "======";letter-spacing: -1px;}
    

    is there anyway it would be safe with resizing and zooming?

    • jimniceguy
      Permalink to comment#

      Changing the size doesn’t seem to do anything, but I guess it can’t be just that simple.
      What’s the flaw in it?

    • jimniceguy
      Permalink to comment#

      The only problem is that you can’t change the distance between two lines other than using different font, that is.

  14. I made a quick demo on this, let me know your thoughts: http://codepen.io/catalinred/pen/bwqta

  15. Permalink to comment#

    I decided to use the pseudo table-cell trick. I combined it with transforms to bring the top and bottom borders closer together. This means I don’t have overflow: hidden;, there are no “magic numbers” and the lines are exactly the width they need to be.

    Because this trick does not rely on multiple backgrounds or linear-gradient it also works in a wider range of browsers. Even IE8 is acceptable :)

    • Excellent! This is a great solution – way to think outside the box with your transform idea.

      The only issue is that this won’t work for responsive sites in which headings might have to wrap (white-space: nowrap, and it breaks if this rule is removed).

      I’d love to find the solution that works for wrapping as well.

    • PS – caniuse.com also indicates that transforms aren’t supported in IE8. That won’t be a problem for too much longer, but I just wanted to add that tidbit.

  16. Excellent – Thanks for posting.

  17. Permalink to comment#

    Smart. How is the support for Internet Explorer?

  18. Permalink to comment#

    Hey Chris, I found that if I give the .fancy :after and :before pseudo lines a width of 100% the lines will be the same with of the fancy heading, or if a smaller percentage such as 80% is used it would be that percentage of the total width of the heading.

    If the heading were to gain or be added more character the lines will adjust accordingly, which is where the overflow hidden could be an interesting solution to hide any excess lines that would extend beyond or outside the content container, or at that point it might be better to just use a smaller fixed pixel width value.

    Awesome example!

  19. Permalink to comment#

    Also I find that if you use 100pts or point values instead of pixels or percentages could be a better solution than the one I just posted above.

  20. Hey Chris, I read the top part of the article and decided to try it out on my own first before seeing your solution.

    This is what I came up with: http://codepen.io/rzea/pen/ecrfL

    Some notes on this solution:

    1.- It doesn’t use extra markup

    2.- CSS is a bit shorter

    3.- The width of the side lines are not “container” dependent, meaning they are the same width regardless the element is applied to.

    4.- Depending on how something like this would get implemented, I may/may-not be using “magic numbers”, but hey, I had to assign values to this thing, eh? lol.

    Let me know what you think.

    • Looks good to me. Just slightly different requirements with the lines-on-sides being a static width not the “remaining” width (which you make clear in your demo).

  21. Permalink to comment#

    I recently had to the exact same thing. Firstly I did a brief search on how to do it but could not find anything (mostly because I didn’t know how this thing is called technically). So I started thinking. Initially I did with good old table with three td and the center one with white-space: nowrap and width: 1px. This worked fine but then I wanted to remove the tables and came up with this solution http://cdpn.io/BcnaE Hope it helps.

  22. liza
    Permalink to comment#

    :)

  23. A nice approach to this seemingly simple, and of course very nice, design feature.

    The discrepancy here between the ease that designers’ enjoy and the complications that front-end folks deal with is quite significant!

  24. Денис

    Center the pseudo-elements vertically:


    top: 0;
    bottom: 0;
    margin: auto;

    http://codepen.io/anon/pen/vaGoe

  25. Mattia

    Thanks Chris, I used something similar in one of my projects… ;)

  26. Tihomir
    Permalink to comment#

    Thanks Chris!!! I used that in my project that I’m working on right now. You saved me a lot of time : ) I used the @Денис method to center the pseudo-elements vertically and it all worked perfectly.

  27. The additional markup (span element inside h1) is kinda nasty. So I tried to get rid off the span, but unlike Ricardo’s solution fill the whole width with the lines-on-sides.

    I went through some iterations that relied on a background color, i.e. they didn’t work with background images. Cf. this
    forum thread (in German, but you’ll find links to samples).

    Finally, I came up with this solution.

    It works with just <h1>Heading</h1> markup. The basic idea is to use display: table-cell like Marush did, but for pseudoelements.

  28. wycks
    Permalink to comment#

    The Fieldset and <legend> html tags is the easiest way to accomplish this and it’s not even a hack though you have to change your markup.

    Simple use:

    <fieldset class="title">
        <legend>Some text</legend>
    </fieldset>
    

    Demo here: http://jsfiddle.net/F3UqW/
    Found on StackOverflow

    • Yes, but that’s a completely non-semantic way of accomplishing it. Let’s try to respect the markup we all love :)

      From the MDN:

      The HTML <fieldset> element is used to group several controls as well as labels (`<label>`) within a web form.

      Better ways of doing it with :before and :after.

  29. I was the one asking for this solution and I have to thank you again for this one, Chris. If you’re interested in seeing the outcome you can check it out here:

    http://malenemailand.dk

  30. Ajay Karwal
    Permalink to comment#

    I’m trying to achieve a similar effect but with a single underline which extends out from the end of the heading and fills the remaining space available on the line…

    However, I’m having problems containing the ‘underline’ inside the parent container.

    Any thoughts?

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".