TL;DR
For a meaningful break between two sections, place an <hr> between them and style it as a vertical line in a grid. Give the vertical separator aria-orientation="vertical"; when the layout stacks, show a horizontal <hr> instead. If the line only decorates adjacent panels, put a CSS border on a panel and skip separator markup. Neither static option needs keyboard focus or a drag handle.
A thin line is easy to draw. The harder part is deciding what it means and what should happen when a dashboard or form becomes one column. We will build a semantic HTML vertical divider first, then show the smaller border-only alternative for everyday application chrome. The examples use plain HTML and CSS, so you can adapt them to a Vue view, a Web Component, or a server-rendered page without a library-specific API.
Before you start: You need two real content regions, a stylesheet you can edit, and a browser in which you can resize the layout. Replace the example headings and text with your own. The 48rem breakpoint below is a starting point for this sample, not a device rule; adjust it when your actual content stops fitting.
Table of contents
- 1. Decide whether the line has meaning
- 2. Add semantic HTML between distinct sections
- 3. Style the vertical rule and switch it when columns stack
- 4. Use a border when separation is only visual
- 5. Test the divider under real content and accessibility settings
- FAQ
- Can I make an
vertical without JavaScript? - Should I use role=“separator” on an empty ?
- Why is my vertical divider not full height?
- Put the divider in your next real screen
- Sources
- Recommended Reads
1. Decide whether the line has meaning
Choose
<hr>when the boundary marks a genuine change of topic or group in the document. The HTML element represents a thematic break, not a generic drawing command. If the two sides are simply adjacent cards, a sidebar and its workspace, or columns of the same form, a border is usually the more faithful choice. MDN’s<hr>reference makes that semantic distinction explicit.There is a third possibility: users need to drag the boundary to resize panes. That is an interactive splitter, not a static HTML vertical divider. A splitter has keyboard and state requirements that the examples in this guide intentionally do not implement. The W3C Window Splitter pattern explains the separate widget contract.

Here is a quick way to decide: if removing the line would make the document’s change of subject harder to understand, start with
<hr>. If removing it would only change the visual spacing, start with a border. If moving it is a user action, build a splitter instead.Expected result: you can state why the boundary exists without describing its color or thickness. If that is difficult, try spacing and headings before adding any line.
2. Add semantic HTML between distinct sections
This example separates an overview from a different topic, the review queue. Give each region its own heading. Put the desktop
<hr>between them in DOM order, so reading order remains overview, break, review queue. A second<hr>is used only at the stacked breakpoint: CSS ensures that just one of the two is displayed at a time.<div class="panel-pair"> <section aria-labelledby="overview-heading"> <h2 id="overview-heading">Account overview</h2> <p>Summary metrics and the current reporting period.</p> </section> <hr class="divider-desktop" aria-orientation="vertical"> <hr class="divider-mobile"> <section aria-labelledby="queue-heading"> <h2 id="queue-heading">Review queue</h2> <p>Accounts that need a separate follow-up decision.</p> </section> </div>An
<hr>has an implicit separator role. Separator orientation defaults to horizontal, so the desktop rule explicitly declaresaria-orientation="vertical"; the mobile rule can retain that default. Neither rule getstabindex,aria-valuenow, or a label. A static separator is not a control, and a reading cursor can encounter it without placing it in the Tab sequence. MDN’s separator-role guide distinguishes static separators from focusable resizing widgets.Expected result: the headings remain intelligible even with CSS turned off, although both rules will then appear in their native horizontal presentation. If two breaks seem redundant without CSS, revisit whether your sections truly need a thematic separator, or use the decorative-border pattern in step 4.
3. Style the vertical rule and switch it when columns stack
Add the following CSS to the stylesheet loaded by the page. Grid allocates a narrow middle track for the line; the default stretched grid row lets the rule follow whichever section has more content. There is no
100vhheight to overshoot a card or fall short of a growing panel..panel-pair { --divider-color: #64748b; display: grid; grid-template-columns: minmax(0, 1fr) 1px minmax(0, 1fr); gap: clamp(1rem, 2vw, 1.5rem); } .panel-pair > section { min-width: 0; } .panel-pair > hr { margin: 0; border: 0; } .panel-pair > .divider-desktop { width: 0; align-self: stretch; border-inline-start: 1px solid var(--divider-color); } .panel-pair > .divider-mobile { display: none; } @media (max-width: 48rem) { .panel-pair { grid-template-columns: minmax(0, 1fr); } .panel-pair > .divider-desktop { display: none; } .panel-pair > .divider-mobile { display: block; width: 100%; border-block-start: 1px solid var(--divider-color); } }border-inline-startandborder-block-startare CSS logical border properties. They follow writing direction and writing mode rather than hard-coding left and top. If your product uses a nonstandard writing mode, verify both the line’s physical direction and the layout’s column behavior before shipping.The
1pxmiddle track is the line’s thickness; change that track and the matching border width together. Adjust--divider-colorfor the surface behind it, andgapfor breathing room. The rule’s length comes from the grid row, not an arbitrary height.min-width: 0on the sections helps flexible content shrink, though a long unbroken value, table, or chart may still need its own wrapping or scrolling strategy. MDN documents grid and flex items’ automatic minimum sizes.
Resize the window below and above
48rem. On a wide view, expect one vertical rule between two columns. On a narrow view, expect one horizontal rule between stacked sections; the displayed rule’s orientation now matches its semantics. If a third empty column appears, check that the desktop divider is set todisplay: noneat the breakpoint. If the line appears too short, remove a fixed height or an overridingalign-selfon the rule and inspect the actual grid row.4. Use a border when separation is only visual
Most dashboard panel boundaries are not thematic breaks. For a left navigation area beside a work area, or two related form columns, put the line on the second region instead. This keeps decorative markup out of the accessibility tree. It also means the line grows with the region it borders; unlike an independent grid rule, it will not automatically reach the height of a taller neighboring region.
<div class="panel-pair--decorative"> <section> <h2>Reporting</h2> <p>Filters and the current report.</p> </section> <section class="panel-secondary"> <h2>Details</h2> <p>Context for the selected reporting item.</p> </section> </div>.panel-pair--decorative { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem; } .panel-pair--decorative > * { min-width: 0; } .panel-pair--decorative > .panel-secondary { border-inline-start: 1px solid #64748b; padding-inline-start: 1.5rem; } @media (max-width: 48rem) { .panel-pair--decorative { grid-template-columns: minmax(0, 1fr); } .panel-pair--decorative > .panel-secondary { border-inline-start: 0; padding-inline-start: 0; border-block-start: 1px solid #64748b; padding-block-start: 1.5rem; } }For related fields inside one form group, maintain real
<label>and<fieldset>relationships first; the border should not be the only cue to what each field does. Likewise, a navigation rail and main work area need meaningful navigation and content structure, not an ARIA label on a decorative rule. Our Application Layout example shows the wider shell context: a persistent left area beside an independently scrolling main area.For a short demonstration of drawing a vertical line in CSS, watch the video below. It is a visual-technique companion; use the semantic choice and responsive checks in this guide for the full implementation.
Expected result: there is no additional stop in keyboard navigation or announced separator for this border-only layout. If the line stays vertical after stacking, confirm the mobile rule clears
border-inline-startbefore addingborder-block-start.5. Test the divider under real content and accessibility settings
A separator is finished when the relationship between sections remains clear without depending on a perfect desktop screenshot. Check the two variants in the contexts where they will actually appear:
- Keyboard and reading order: Tab through controls in both panels. A static rule should not take focus; screen-reader reading order should follow the heading and content order. At the breakpoint, confirm that the visible semantic rule reports the matching orientation in your supported browser and screen-reader combination.
- Narrow space and zoom: Resize through the breakpoint and test browser zoom. Look for sideways page scrolling, clipped headings, a stray one-pixel column, or a divider left hanging after a panel moves below the other.
- Changing content: Add a long error message, translated label, extra dashboard card, or expanded form hint. The semantic grid rule should stretch with the row; the decorative border should follow its own panel. Decide whether you actually need a full-height visual line when panel heights differ.
- Forced colors: Test with a forced-colors or high-contrast setting. Browsers can replace border colors with system colors and remove non-URL background images, so a real border is more dependable than a background-only line. If the border is still hard to see in your supported environment, try a targeted system color such as
CanvasTextrather than disabling users’ color preferences. See MDN’s forced-colors behavior reference.
If the line conveys essential grouping but disappears visually, make the sections’ headings and spacing clear too. Do not attempt to repair a static divider with
tabindex="0", a button role, or interactive resize attributes. If the requirement really is adjustable pane width, use a proper splitter instead of adding pointer events to either snippet above.FAQ
Can I make an
<hr>vertical without JavaScript?Yes. CSS can style its border as a vertical rule and let grid or flex layout determine its length. If the rule changes to horizontal at a breakpoint, keep its accessible orientation in sync. The two-rule pattern above does that with CSS visibility and no JavaScript.
Should I use
role="separator"on an empty<div>?Prefer the native
<hr>when there is a real thematic break. Use a border on an existing region for decoration. An empty<div role="separator">is usually unnecessary, and assigning that role to a decorative line can announce structure that is not actually there.Why is my vertical divider not full height?
A percentage height often has no useful reference when the parent has no defined height. In the semantic grid example, the rule stretches with the row, so inspect the parent’s grid sizing and any overriding alignment. In the decorative example, the border belongs to the second panel and ends when that panel ends; use a separate rule if matching the taller neighbor is essential.
Put the divider in your next real screen
You now have a responsive semantic separator for genuine changes of subject and a simpler border for purely visual panel edges. Apply the appropriate version to one dashboard or settings screen, replace the example content with real data, then run the narrow-width and keyboard checks before you reuse the pattern. For a fixed sidebar and work area, start with our Application Layout block. If users must resize panes, switch to our guide to accessible, responsive splitter UI rather than treating a static line as a handle.
Sources
- MDN: The
<hr>element - MDN: ARIA separator role
- MDN:
border-inline-start - MDN:
min-width - MDN:
forced-colorsmedia feature - W3C WAI: Window Splitter Pattern
- DOM Studio: Application Layout
Recommended Reads
- Splitter UI: How to Build Accessible, Responsive Resizable Panes, for projects that need user-controlled resizing.
- Vue App Layout: Build a Responsive Dashboard Shell in Vue 3, for placing panel patterns inside an application shell.
- Can I make an
