Home / Snippets / Typography /

Heading + decorative rule

Add decorative lines and rules to headings using ::before/::after pseudo-elements, borders, and oklch() accent colors — no markup changes needed.

Underline accent bar

Section Title

Left vertical bar

Section Title

Centered rule with text

Section Title

Top + bottom double rules

Section Title

Widely Supported
typographyno-js

Quick implementation

/* 1. Underline accent bar */
.heading-underline-bar {
  display: inline-block;
  padding-bottom: 0.4rem;
  position: relative;
}
.heading-underline-bar::after {
  content: '';
  position: absolute;
  bottom: 0; left: 0;
  width: 2.5rem; height: 3px;
  background: oklch(0.72 0.19 265);
  border-radius: 2px;
}

/* 2. Left vertical bar */
.heading-left-bar {
  padding-left: 0.85rem;
  border-left: 4px solid oklch(0.72 0.19 265);
  line-height: 1.2;
}

/* 3. Centered rule with text */
.heading-centered-rule {
  display: flex;
  align-items: center;
  gap: 0.85rem;
}
.heading-centered-rule::before,
.heading-centered-rule::after {
  content: '';
  flex: 1;
  height: 1px;
  background: oklch(0.72 0.19 265 / 0.4);
}

/* 4. Top + bottom double rules */
.heading-double-rule {
  padding-block: 0.6rem;
  text-align: center;
  position: relative;
}
.heading-double-rule::before,
.heading-double-rule::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: 2px;
  background: oklch(0.72 0.19 265);
}
.heading-double-rule::before { top: 0; }
.heading-double-rule::after  { bottom: 0; }

Prompt this to your LLM

Includes role, four pattern variants, constraints, and edge cases to handle.

You are a senior frontend engineer styling headings for a design system.

Goal: Add decorative rules to headings using only CSS — no extra markup,
no <hr> elements, and no JavaScript.

Technical constraints:
- Use ::before and ::after pseudo-elements with content: '' for all
  decorative lines; never use border-bottom on the heading itself for
  decorative rules (it cannot be independently sized or offset).
- Use oklch() for all colors — no hex or rgba values.
- Use CSS custom properties (var(--text), var(--accent), etc.) for theming
  wherever possible.
- Do not add extra wrapper elements to the HTML.

Produce four variants:

A) Underline accent bar — a short colored bar (not full-width) below the
   heading text. Use position: relative on the heading and position: absolute
   on ::after, anchored to bottom: 0, left: 0, with an explicit width (e.g.
   2.5rem) and height (e.g. 3px). The heading needs display: inline-block
   so the ::after bar stays under the text, not the full container width.

B) Left vertical bar — a colored left border using border-left on the heading
   element itself with padding-left for offset. This is the one case where
   using border directly on the heading is appropriate because the bar spans
   the full text height automatically.

C) Centered rule with text — the heading sits between two horizontal lines
   that stretch to fill available space. Use display: flex with align-items:
   center and gap on the heading. Both ::before and ::after use flex: 1 and
   height: 1px to form the lines. This requires the heading element to be
   block-level (display: flex).

D) Top + bottom double rules — a thin line above and below the heading text.
   Use position: relative on the heading with padding-block. ::before is
   anchored to top: 0 and ::after to bottom: 0; both span the full width
   with left: 0; right: 0.

Edge cases to handle:
- Variant A requires display: inline-block on the heading — document that
  block headings will need the wrapper pattern if full-width layout is needed.
- Variant C turns the heading into a flex container, so any child elements
  (e.g. a nested <span>) will become flex items — keep heading content as
  plain text.
- For all position: absolute variants, confirm the heading has
  position: relative set so the pseudo-element is contained correctly.
- In dark mode, semi-transparent rule colors (e.g. oklch(0.72 0.19 265 / 0.4))
  work better than full opacity lines to avoid visual heaviness.

Return CSS only, one block per variant, with a comment label for each.

Why pseudo-elements beat border-bottom and <hr>

The obvious shortcuts for decorative heading rules are border-bottom on the heading or an <hr> element. Both fall short in important ways. border-bottom always spans the full element width — you cannot give it an independent width of, say, 2.5rem to create a short accent bar. <hr> adds semantic content to the document: screen readers announce it as a thematic break, which is not the intent of a decorative rule. It also requires an extra element in the HTML, coupling presentation to markup.

Pseudo-elements — ::before and ::after with content: '' — solve both problems. They produce no accessible content (the browser correctly ignores them in the accessibility tree) and they exist entirely in CSS, so the HTML remains clean. Because pseudo-elements are independent boxes, you can set their own width, height, position, and background without affecting the parent heading's dimensions or text flow.

The flexbox gap technique for centered rules

The centered rule pattern — a heading flanked by lines on both sides — is a classic design treatment. The naive approach places <span> elements around the text and uses absolute positioning for the lines. The modern approach turns the heading itself into a flex container:

display: flex; align-items: center; gap: 0.85rem;

Both ::before and ::after then use flex: 1, which instructs the flex algorithm to give each pseudo-element an equal share of the remaining space after the heading text is sized. The lines grow and shrink automatically with the container — no fixed widths, no percentage math, no media query overrides. Setting height: 1px on the pseudo-elements and giving them a background color (rather than border) allows sub-pixel thickness and better anti-aliasing on high-DPI screens.

The gap property on the flex container provides breathing room between the text and the lines. Adjust it together with font-size to maintain visual balance: tighter headings want a smaller gap, larger display headings can take 1rem or more.

Controlling rule thickness and color

Rule thickness is almost always better specified in px rather than em or rem. Decorative rules are screen furniture — they should stay visually thin regardless of the heading's font size. 1px for a subtle divider, 2–3px for an accent bar, and 4px for a bold left-bar treatment covers most design needs.

For color, oklch() is the right choice in 2026. The chroma and hue axes map directly to perceptual brightness, so an accent rule at oklch(0.72 0.19 265) sits at the same perceptual lightness regardless of whether you change the hue for theming. Alpha transparency — oklch(0.72 0.19 265 / 0.4) — is particularly useful for the centered rule pattern: a semi-transparent line integrates with both light and dark backgrounds without needing separate theme overrides.

When using background instead of border for rule lines (as in the pseudo-element patterns), you also gain access to border-radius on the line itself. A border-radius: 2px on the short underline accent bar rounds the endpoints and gives the rule a polished, intentional finish.