Utilities✨ Premium

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.

SVGAnimationDash

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

Selection rectangle in a photo editor — Dashed Border March example 1

① Selection rectangle in a photo editor

WhenIn the Clairvane editor the Selection tool is on: a rectangle of marching dashes marks out part of a photo of a cliff at sunset, and the right-hand panel shows its width and height in image pixels.
WhyMarching ants are the visual code every photo editor uses: people instantly read it as a chosen area, not an object. Because the outline follows the box size, the selection can be resized without recomputing the SVG.
SettingsEmpty box in position: absolute over the image, width and height changed by the scene every 650 ms with a 0.45 s CSS transition (the outline follows every frame); almost square corners with rx="2" ry="2" on the <rect>. --dsm-fill: transparent so the photo shows through; default colours and speed.
Seats still free on a wedding seating plan — Dashed Border March example 2

② Seats still free on a wedding seating plan

WhenOn Lise and Aurèle's seating plan at Domaine de Sarvillac, taken seats show a first name; the four free seats are "+ Free seat" buttons ringed with dashes that light up on hover or when reached with the keyboard.
WhyThe dashed line says "fill me in" with no extra words, and its movement draws the eye to what is left to do among identical cells. Since every seat is a real <button>, the planner can move through them with Tab.
Settingsdsm-box on a 36 px tall <button> in a grid, one root per seat, --dsm-fill: transparent; --dsm-color: #8b85f0, --dsm-color-active: #4f46e5, stroke reduced to 2 px by the scene. The capture focuses one seat after another, as Tab would.
Welcome coupon for a grocer delivering by bike — Dashed Border March example 3

③ Welcome coupon for a grocer delivering by bike

WhenAt the top of Pommeraude's home page, next to the promise "Your first basket, at your door tomorrow morning", a large -20% coupon with its WELCOME20 code and a "Copy the code" button is framed by marching dashes.
WhyThe dashed edge recalls the cut-out voucher from a newspaper, and its slow movement is enough to make the coupon the most visible thing on the page, with no blinking or loud colour.
Settings250 px wide box, content in a column, white background; --dsm-color: #2f5d2a (brand green) and --dsm-color-active: #b45f2b (terracotta) when the mouse is over it or the button has focus.

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" and focusable="false"; only the box content is read out.
  • Keyboard: the demo box is a <div>, so it never gets focus. Put the dsm-box class 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-on class) until the next press elsewhere, WebKit included, where hover does not exist.
  • Reduced motion: under prefers-reduced-motion: reduce the 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.

Chrome 74+✓ Full (measured)
Firefox 63+✓ Full (measured)
Safari 13+✓ Full (measured in WebKit)
Edge 79+✓ Full (Chromium engine)
Mobile iOS 13+✓ WebKit engine, not checked on a device
Android Chrome 74+✓ Full (Chromium engine)

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):

index.html — structure
<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 / propertyDefaultEffect
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

Yes, without JavaScript: the <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.
That is where the rectangle's outline starts. The 20 px pattern does not divide every perimeter evenly, so the last dash is cut there. It goes unnoticed while moving; to remove it, add 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).
Put the 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.