Skip to content

Story Ring Avatars with Gradient Border

Pure CSS story avatars with Instagram-style gradient rings, close friends and live variants, and a seen state that turns the ring grey on click.

Live preview

Open in new tab

Code

A row of story avatars in the style of social apps, built with pure CSS and a little vanilla JavaScript. Each round photo sits inside a gradient ring that signals a new story, a green ring for close friends, a red ring with a Live badge, and a thin grey ring once the story has been seen. Use it above a feed or in a messaging sidebar.

How it works

The root section.sra-stories holds a header with a .sra-count badge, the scrollable ul.sra-list and a .sra-footer with a legend, size samples and a reset button. Every story is a button.sra-story that contains a .sra-ring, an img.sra-avatar and a .sra-name. The ring is not a border: .sra-ring has padding equal to --sra-ring-width, and its ::before pseudo element fills the circle behind the photo with var(--sra-ring-bg, var(--sra-gradient)). A solid photo border (--sra-gap) creates the white gap between ring and face. On hover or keyboard focus the pseudo element rotates, so the conic gradient appears to spin around the avatar.

Modifiers only change --sra-ring-bg: .sra-story--close uses --sra-close, .sra-story--live uses --sra-live, and .is-seen switches to --sra-seen and a 2px ring. .sra-story--own shows a thin grey ring with a blue .sra-add plus badge.

The script remembers each story's initial state, adds .is-seen when a story is clicked, updates the hidden .sra-state text and recounts unseen stories into .sra-count-num. The .sra-reset button restores the starting state. Without JavaScript the rings and states still render from the markup.

Customization

  • --sra-gradient on .sra-stories: the ring for new stories; swap the conic gradient for your brand colors.
  • --sra-close, --sra-live and --sra-seen: ring colors for close friends, live and seen stories.
  • --sra-size: the avatar diameter (68px on phones, 72px from 640px); the classes .sra-size-sm, .sra-size-md and .sra-size-lg show how to override it per avatar.
  • --sra-ring-width and --sra-gap: thickness of the ring and of the white gap around the photo.
  • --sra-spin: duration of one rotation of the hover animation, e.g. 1.2s for a faster spin.
  • Add is-seen to any .sra-story in the markup to start it as viewed, and set its .sra-state text to ", seen".
  • Replace the comment in the click handler of script.js with code that opens your own story viewer.

Accessibility

Every story is a native button, so it is reachable with Tab and activated with Enter or Space. The visible username is part of the button label, and a visually hidden .sra-sr span adds the state ("new story", "close friends", "live now" or "seen"), so color is never the only cue. Photos use an empty alt because the name already labels the button. The unseen counter sits in an aria-live="polite" region. Focus shows a violet outline (--sra-focus), and with prefers-reduced-motion: reduce the spinning ring and press effect are disabled.

Usage notes

The portraits come from Unsplash; replace them with your users' own profile images. The script scopes everything to each .sra-stories root, so several rows can share a page.

Requirements

No dependencies. Pure HTML, CSS and JavaScript.

Story Ring Avatars with Gradient Border – Pure CSS avatars component screenshot
Story Ring Avatars with Gradient Border · Pure CSS Avatars

More Avatars

Avatars with Status Indicator – Bootstrap 5 avatars preview

Avatars with Status Indicator

Bootstrap 5 avatars in five sizes with online, away, busy and offline status dots, initials fallback for missing photos and a status picker.

Avatar Group – Tailwind CSS avatars preview

Avatar Group

Overlapping Tailwind CSS avatar stack in three sizes with initials, status dots, name tooltips and a +N counter that reveals hidden people.