Dashed Border March
SVG dashed frame whose dashes march around a box of any size; the stroke brightens and thickens on hover, focus and touch.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Utilities has 43 effects, including 3 free. Explore the category →
3 usage examples



How it works
An absolutely positioned <svg> inside .dsm-box holds a single <rect>, 100% × 100%, with 12 px rounded corners. The SVG is moved 1.5 px inward and made 3 px smaller each way (half of the 3 px stroke), with overflow: visible: the outline lands exactly on the box edge, whatever its width or height. Make the box bigger or fill it with text and the stroke follows, without a single line of script.
The violet #6366f1 stroke is cut by stroke-dasharray: 12, 8 (12 px dash, 8 px gap). The dsm-march animation slides stroke-dashoffset from 0 to -40 in 1.5 s, linear and looping: since -40 equals two full patterns, the restart can't be seen and the dashes appear to walk around the frame.
When the box is hovered, gets focus (itself, or a link, button or field inside it: :focus-within) or is touched, the stroke turns #a78bfa, grows to 4 px and gains a 6 px drop-shadow glow. Touch is handled by a small script (0.8 KB) that adds the dsm-on class on a finger or pen pointerdown and removes it at the next touch elsewhere on the page.
Accessibility
- Silent decoration: the SVG has
aria-hidden="true"andfocusable="false"; only the box content is read out. - Keyboard: the demo box is a
<div>, so it never gets focus. Put thedsm-boxclass on an<a href>or a<button>, or place one inside: Tab then lights the stroke, and a 2 px outline 6 px away is added on the box's own:focus-visible. On macOS Safari, Tab skips links unless the system setting allows it (Option+Tab reaches them). - Touch: a finger press lights the frame (
dsm-onclass) until the next press elsewhere, WebKit included, where hover does not exist. - Reduced motion: under
prefers-reduced-motion: reducethe dashes stop dead; the frame stays visible and still changes colour in the active state. - Limit: outside that setting the marching never stops and has no pause button. Keep it to one or two elements per screen.
Browser compatibility
SVG and CSS (dashes, animated stroke-dashoffset, custom properties), plus 0.8 KB of JavaScript for touch (Pointer Events: Chrome 55, Firefox 59, Safari 13). prefers-reduced-motion: Chrome 74, Firefox 63, Safari 10.1. Measured in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): two copies with only the second one hovered, box resized to 320×160 with a 317×157 outline, keyboard focus through a link, touch, 390 px without overflow, reduced motion with no running animation, 0 errors.
Without JavaScript: everything works except the finger-press active state on a box that is neither a link nor a button. Without prefers-reduced-motion (old browsers), the dashes always march.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0766-root">
<div class="dsm-box">
<svg class="dsm-svg" aria-hidden="true" focusable="false">
<rect class="dsm-rect" width="100%" height="100%" rx="12" ry="12"></rect>
</svg>
…
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Dash colour (CSS — --dsm-color) |
#6366f1 |
Frame colour at rest, set on .fx-0766-root or a parent. |
Active colour (CSS — --dsm-color-active and --dsm-glow) |
#a78bfa and rgba(99, 102, 241, .6) |
Stroke and glow colour on hover, focus and touch. |
Speed (CSS — --dsm-speed) |
1.5s |
Length of one 40 px cycle. The bigger the value, the slower the march. |
Box fill (CSS — --dsm-fill) |
#0a0a0f |
Fills the inside of the frame, under the white text. Set it to transparent to frame an image or part of the page (like the selection rectangle in the example), and then give the text a colour that reads on that background. |
Pattern (CSS — stroke-dasharray of .dsm-rect and @keyframes dsm-march) |
12, 8 and -40 |
Dash and gap length. Keep a multiple of their sum in the animation (e.g. 6, 6 and -24) for a seamless loop. |
Box size (CSS — width / height of .dsm-box) |
200 px wide, at least 120 px tall | Any size works: the 100% rectangle follows the box. |
Corner radius (HTML — rx / ry of the <rect>, CSS — border-radius) |
12 px | Change both together; rx="0" gives square corners. |
Forced active state (HTML — dsm-on class on .dsm-box) |
absent | Keeps the frame lit, for instance on the selected item. The script only removes the class it added itself on touch. |
FAQ
<rect> is 100% of an SVG pinned to the box in CSS, inset by half the stroke. A box that grows with its text, goes full width on mobile or animates its size keeps an exact outline. If you change the stroke width, set the inset (top, left and both calc() of .dsm-svg) to half the new value.pathLength="400" to the <rect> (Chrome 57, Firefox 61, Safari 14): the pattern is then spread over 20 equal steps at any size (dashes get longer on a big box).dsm-box class straight on the card's <a> (or on a <button>), keep the SVG as first child and add your content after it. Hover, keyboard focus and a finger press then light the same state, and the card stays a real link for screen readers. One .fx-0766-root per card: the touch script only takes the first box of each root.