The "marching ants" effect — a dashed border whose dashes animate along the perimeter — looks like it requires SVG or canvas. It doesn't. Four CSS background-image gradients and one @keyframes rule are all you need.
How it works
CSS background-image accepts multiple values. Each gradient paints one side of the border:
- Two
linear-gradient(90deg, ...)gradients for top and bottom edges - Two
linear-gradient(180deg, ...)gradients for left and right edges
Each gradient alternates between the border color and transparent at a fixed interval, creating a dashed pattern:
background-image:
linear-gradient(90deg, #000 50%, transparent 0), /* top */
linear-gradient(90deg, #000 50%, transparent 0), /* bottom */
linear-gradient(180deg, #000 50%, transparent 0), /* left */
linear-gradient(180deg, #000 50%, transparent 0); /* right */
background-size: 30px 1px, 30px 1px, 1px 30px, 1px 30px;
background-position: 0 0, 0 100%, 0 0, 100% 0;
background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
Animating the march
Shift the background-position by exactly one dash interval and the dashes appear to march:
@keyframes marchingAnts {
to {
background-position: 30px 0, -30px 100%, 0 -30px, 100% 30px;
}
}
Note the directions: top moves right (+30px), bottom moves left (-30px), left moves up, right moves down. This makes the ants appear to travel clockwise around the perimeter.
Pause on idle, run on hover
animation: marchingAnts 0.4s linear infinite;
animation-play-state: paused;
&:hover {
animation-play-state: running;
}
Dark mode
Because the gradient color is hardcoded in the CSS, it won't respond to a data-theme attribute. The fix is to use a CSS custom property instead of a literal color:
linear-gradient(90deg, var(--color-border) 50%, transparent 0)
Now the border color is controlled by the same token system as the rest of the design.