Navigation

v0.0.4
Feedback

Flash

A ring that shows where a link has landed, then fades.

On this page

A flash shows where a link has landed. Put flash on anything with an id: a heading, a row, a comment, a card. When that id is the fragment of the address, a ring of ink stands around the element and fades away in two seconds, so a reader who followed a link to one thing among many sees which. Nothing is added by script: the address is the state.

Flash

<div class="card max-w(480) mx-auto">  <p class="m(0) text-sm">Jump to    <a class="link" href="#comment-ada">Ada's comment</a>or    <a class="link" href="#comment-grace">Grace's reply</a>.</p>  <ol class="thread">    <li class="thread-item flash" id="comment-ada" style="--flash-offset: 1rem">      <span class="thread-avatar">        <span class="avatar">AL</span>      </span>      <div class="thread-body">        <p class="thread-header">          <span class="thread-author">Ada Lovelace</span>          <time class="thread-meta" datetime="2026-11-12T09:12">2h</time>        </p>        <p class="thread-text">The focus ring is cut by the mask. Should it move to the group?</p>      </div>    </li>    <li class="thread-item flash" id="comment-grace" style="--flash-offset: 1rem">      <span class="thread-avatar">        <span class="avatar">GH</span>      </span>      <div class="thread-body">        <p class="thread-header">          <span class="thread-author">Grace Hopper</span>          <time class="thread-meta" datetime="2026-11-12T10:31">35m</time>        </p>        <p class="thread-text">I will make the change this afternoon.</p>      </div>    </li>  </ol></div>
Follow a link: a ring stands around the comment it points at, then fades.

#How it works

The browser marks the element whose id matches the fragment of the address as the page's :target. flash draws a ring around that element and lets it fade. Two things follow from the address being the state:

  • A link from another page, a link on the same page and a pasted address all flash the same element.
  • Following another fragment link moves the flash; a link to the same fragment again does not replay it.

The ring is an outline. It takes no room, follows the element's own corner and needs no pseudo-element, and it leaves the element's face, its shadow and its hover as they are. So the same class works on a card, a table row, a heading and a comment.

#Clear of the navbar

A fragment link scrolls its target to the very top of the viewport, which is under a sticky navbar if the page has one. flash also sets how far below the top edge the browser stops: --flash-offset, 5rem by default. Set it to the height of whatever is fixed at the top of your page, plus a little air.

#Without motion

For a reader who asks for reduced motion nothing fades. A hairline stays around the target for as long as it is the target, which says the same thing without movement.

#Flash or mark

A flash marks a place for a moment. Words that are emphasized where they stand, for as long as they are there, are a mark. News about something that just happened is a toast.

#Accessibility

  • The flash is for the eye. Give a target that is not a link or a control tabindex="-1", so focus moves there with the jump and the next Tab starts from it.
  • The ring stands 4px outside the element and covers nothing, so the text keeps its contrast.
  • In forced colors the target takes a system-highlight outline and nothing fades.

#Classes

ClassDescription
flashOn an element with an id: when it becomes the target of the address a ring stands around it and fades away, following the element's own corner

#Custom properties

PropertyDefaultDescription
--flash-offset5remHow far below the top of the viewport the browser scrolls the element to

#Source

@master/ui/components/flash.css
/* * Flash: where a link has landed. * On anything with an id. When that id is the fragment of the address, the element is the page's :target: * a ring of ink stands around it and fades away in two seconds, so a reader who followed a link to one * heading, one row or one comment among many sees which. Nothing is added by script: the address is the state. * The ring is an outline. It takes no room, follows the element's own corner, needs no pseudo-element and * leaves the element's face, shadow and hover as they are, so it works on a card, a table row and a line of * text alike. * The element also keeps clear of what is fixed at the top of the page: --flash-offset is how far below the * top edge the browser scrolls it to, 5rem unless told otherwise. * With reduced motion the ring does not fade: a hairline stays around the target instead. */@layer components {  .flash {    --flash-offset: 5rem;    --flash-color: color-mix(in oklab, var(--color-text-strong) 42%, transparent);    scroll-margin-top: var(--flash-offset);    &:target {      animation: flash-fade 2s var(--easing-glide) 200ms backwards;    }    @media (prefers-reduced-motion: reduce) {      &:target {        outline: 1px solid var(--color-line-mark);        outline-offset: .25rem;        animation: none;      }    }    @media (forced-colors: active) {      &:target {        outline: 2px solid Highlight;        outline-offset: .25rem;        animation: none;      }    }  }}@keyframes flash-fade {  0%, 30% {    outline: 2px solid var(--flash-color);    outline-offset: .25rem;  }  100% {    outline: 2px solid transparent;    outline-offset: .25rem;  }}