Understanding the
Element and Where It Actually Belongs
Horizontal rules are one of those HTML elements that most people throw into a page without really thinking about why, then wonder why their content looks like a series of disconnected islands. The tag is simple —
— but getting it right involves understanding what browsers expect, what CSS can and can't do, and when you should reach for a border versus a thematic break instead. The
tag creates a horizontal line that separates content thematically. It's not a visual border. It's a semantic divider meaning "this section is done, moving on." Browsers render it as a line by default, but that's purely coincidental styling. The actual purpose is structural. I spent probably six months arguing with developers who insisted
was just a fancy way to draw a line across the page. They'd stack three of them in a row with different colors and widths and call it "design." It was terrible. A proper
sits at the end of a logical content block — a paragraph, a list, a section — and signals to both humans and screen readers that a shift is happening.
Here's the thing most tutorials skip:
is a void element in HTML5. It requires no closing tag. Some people still write </hr> out of habit from XHTML days. Browsers handle it fine, but it's unnecessary clutter and it will trigger validation warnings if you're running any kind of linting pipeline.
How It Actually Works in Practice
CSS can style
freely now. The old-school approach of dropping a table row or a div with a border-bottom is obsolete. You control height, color, margin, and even create gradients on
elements without fighting the browser. Here's a practical example that works reliably across everything from Chrome to Safari: <hr style="border: none; height: 2px; background: linear-gradient(to right, #333, transparent); margin: 2rem 0;" /> This creates a line that starts dark and fades to nothing on the right side. Useful for card separators or section breaks in dashboards. Takes about two seconds to write. Looks significantly better than a standard gray rule.
Get the Full Details

But here's where people get tripped up. The default browser styling varies wildly between Chrome, Firefox, and Safari. If you need pixel-perfect consistency — and most production apps do — you should always override every property. Height, border, background, margin, and even min-height. One of these unoverridden will silently revert to browser defaults and your QA reviewer will flag it as an inconsistency.
Edge Cases and What I Learned the Hard Way
I once worked on a documentation site where
was being used inside a nested grid layout with auto-generated table of contents. Every time the TOC updated dynamically, the
elements would lose their custom CSS because the JavaScript was cloning DOM nodes and the inline styles weren't being preserved. The lines reverted to default browser rendering mid-page-load. It looked broken for about half a second every time someone navigated. The fix was straightforward but took me three hours to figure out because the issue was intermittent and I was initially blaming a CSS specificity conflict. The real solution was switching to data attributes instead of inline styles and using a CSS selector to target them, so the styles were reapplied cleanly after each DOM mutation. Something like [data-hr-style="fade"] { border: none; /* ... */ } with the attribute set in the template. Done. Never had the issue again. Another problem worth noting:
elements in a flex container with align-items: stretch can behave unpredictably depending on the browser. Chrome might give them the full container width while Firefox renders them at content width. If your layout depends on consistent horizontal spanning, wrap the
in a div or explicitly set width: 100%.
When Not to Use It
This is the part nobody emphasizes enough.
is semantically heavy. It carries meaning. Using it as a decorative separator between two unrelated UI elements — say, a navigation link and a button — is misuse. In those cases, a simple div with a border-bottom or a CSS pseudo-element is the correct choice. Screen reader users will announce "horizontal rule" when they encounter
, which adds noise when there's no actual thematic break happening. I've seen analytics pages use
to separate every chart row. That's roughly 47 announcements per page for a screen reader user. Not useful. Those should be structural dividers with proper aria-hidden attributes if you must use the element, or better yet, just use a bottom border on the chart container itself.

What Is Horizontal Rule Beyond Basic HTML
In Markdown,
translates to three or more hyphens, asterisks, or underscores on a line by themselves. In tools like Tailwind CSS, there's no built-in class for
— you write custom CSS or use arbitrary values. In framework-specific ecosystems like React or Vue,
works identically to plain HTML but you'll often see it wrapped in components for styling consistency across a design system. The common pitfall across all of these is treating it as a visual component when it's a semantic one. Style it to match your design system, yes, but don't change its function. It divides content. That's the job. Everything else is decoration on top of that foundation.