ストーリーズ風リング

未読のアバターだけ回転するグラデーションリングが輝き、既読になるとリングは静かなグレーへ変わります。クリックすると即座に既読状態へ切り替わります。

#css#avatar

ライブデモ

コード

HTML
<!-- ストーリーズ風リング: 未読は回転するグラデーションリングが輝き、既読はグレーに。クリックで既読化 -->
<ul class="stories" aria-label="ストーリーズ一覧">
  <li>
    <button type="button" class="story" data-read="false">
      <span class="story__ring"><img class="story__avatar" src="https://picsum.photos/id/64/200/200" alt="つむぎのストーリー" width="58" height="58"></span>
      <span class="story__name">つむぎ</span>
    </button>
  </li>
  <li>
    <button type="button" class="story" data-read="false">
      <span class="story__ring"><img class="story__avatar" src="https://picsum.photos/id/91/200/200" alt="陽菜のストーリー" width="58" height="58"></span>
      <span class="story__name">陽菜</span>
    </button>
  </li>
  <li>
    <button type="button" class="story" data-read="true">
      <span class="story__ring"><img class="story__avatar" src="https://picsum.photos/id/1027/200/200" alt="蓮のストーリー" width="58" height="58"></span>
      <span class="story__name">蓮</span>
    </button>
  </li>
  <li>
    <button type="button" class="story" data-read="false">
      <span class="story__ring"><span class="story__avatar story__avatar--letter" aria-hidden="true">碧</span></span>
      <span class="story__name">碧</span>
    </button>
  </li>
  <li>
    <button type="button" class="story" data-read="true">
      <span class="story__ring"><span class="story__avatar story__avatar--letter" aria-hidden="true">澪</span></span>
      <span class="story__name">澪</span>
    </button>
  </li>
</ul>
CSS
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 28px;
  font-family: "Hiragino Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  background:
    radial-gradient(circle at 82% 18%, rgba(236, 72, 153, .14) 0%, transparent 42%),
    radial-gradient(circle at 12% 88%, rgba(250, 204, 21, .1) 0%, transparent 45%),
    #0c0c10;
  color: #f3f0eb;
}

.stories {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 13px;
  margin: 0;
  padding: 24px 18px;
  max-width: 480px;
  border-radius: 22px;
  background: rgba(255, 255, 255, .04);
  border: 1px solid rgba(255, 255, 255, .08);
  box-shadow: 0 26px 60px -28px rgba(0, 0, 0, .75);
}

/* ボタンをリセットしつつ、内側の要素はgridで縦積み */
.story {
  all: unset;
  cursor: pointer;
  display: grid;
  justify-items: center;
  gap: 8px;
  padding: 4px;
  border-radius: 14px;
}
.story:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

.story__ring {
  position: relative;
  display: inline-flex;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  padding: 3px;
}

/* 未読: 虹色グラデーションのリングがゆっくり回転して「生きている」ことを示す */
.story__ring::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(from 0deg, #f59e0b, #ec4899, #8b5cf6, #38bdf8, #f59e0b);
  animation: story-spin 3.2s linear infinite;
}

/* 既読: リングは静かなグレーに変わり、回転も止まる */
.story[data-read="true"] .story__ring::before {
  background: #4b5563;
  animation: none;
}

.story__avatar {
  position: relative;
  z-index: 1;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  border: 3px solid #131317;
}

.story__avatar--letter {
  display: grid;
  place-items: center;
  background: linear-gradient(155deg, #38bdf8, #6366f1);
  color: #fff;
  font-weight: 700;
  font-size: 18px;
}

.story:hover .story__avatar {
  filter: brightness(1.08);
}

.story__name {
  font-size: 11.5px;
  letter-spacing: .02em;
  color: rgba(243, 240, 235, .65);
}
.story[data-read="true"] .story__name {
  color: rgba(243, 240, 235, .4);
}

@keyframes story-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .story__ring::before { animation: none; }
}

@media (max-width: 480px) {
  .stories { gap: 14px; padding: 20px 16px; }
  .story__ring { width: 56px; height: 56px; }
  .story__avatar { width: 50px; height: 50px; }
}
JavaScript
// ストーリーズ風リング: クリックしたアバターを既読状態にする(data-read属性を切り替えるだけでCSSが見た目を変える)
(() => {
  const stories = document.querySelectorAll('.story');
  if (!stories.length) return; // null安全

  stories.forEach((btn) => {
    btn.addEventListener('click', () => {
      btn.dataset.read = 'true';
    });
  });
})();

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「ストーリーズ風リング」の効果を追加してください。

# 追加してほしい効果
ストーリーズ風リング(バッジ & アバター)
未読のアバターだけ回転するグラデーションリングが輝き、既読になるとリングは静かなグレーへ変わります。クリックすると即座に既読状態へ切り替わります。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- ストーリーズ風リング: 未読は回転するグラデーションリングが輝き、既読はグレーに。クリックで既読化 -->
<ul class="stories" aria-label="ストーリーズ一覧">
  <li>
    <button type="button" class="story" data-read="false">
      <span class="story__ring"><img class="story__avatar" src="https://picsum.photos/id/64/200/200" alt="つむぎのストーリー" width="58" height="58"></span>
      <span class="story__name">つむぎ</span>
    </button>
  </li>
  <li>
    <button type="button" class="story" data-read="false">
      <span class="story__ring"><img class="story__avatar" src="https://picsum.photos/id/91/200/200" alt="陽菜のストーリー" width="58" height="58"></span>
      <span class="story__name">陽菜</span>
    </button>
  </li>
  <li>
    <button type="button" class="story" data-read="true">
      <span class="story__ring"><img class="story__avatar" src="https://picsum.photos/id/1027/200/200" alt="蓮のストーリー" width="58" height="58"></span>
      <span class="story__name">蓮</span>
    </button>
  </li>
  <li>
    <button type="button" class="story" data-read="false">
      <span class="story__ring"><span class="story__avatar story__avatar--letter" aria-hidden="true">碧</span></span>
      <span class="story__name">碧</span>
    </button>
  </li>
  <li>
    <button type="button" class="story" data-read="true">
      <span class="story__ring"><span class="story__avatar story__avatar--letter" aria-hidden="true">澪</span></span>
      <span class="story__name">澪</span>
    </button>
  </li>
</ul>

【CSS】
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 28px;
  font-family: "Hiragino Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  background:
    radial-gradient(circle at 82% 18%, rgba(236, 72, 153, .14) 0%, transparent 42%),
    radial-gradient(circle at 12% 88%, rgba(250, 204, 21, .1) 0%, transparent 45%),
    #0c0c10;
  color: #f3f0eb;
}

.stories {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 13px;
  margin: 0;
  padding: 24px 18px;
  max-width: 480px;
  border-radius: 22px;
  background: rgba(255, 255, 255, .04);
  border: 1px solid rgba(255, 255, 255, .08);
  box-shadow: 0 26px 60px -28px rgba(0, 0, 0, .75);
}

/* ボタンをリセットしつつ、内側の要素はgridで縦積み */
.story {
  all: unset;
  cursor: pointer;
  display: grid;
  justify-items: center;
  gap: 8px;
  padding: 4px;
  border-radius: 14px;
}
.story:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

.story__ring {
  position: relative;
  display: inline-flex;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  padding: 3px;
}

/* 未読: 虹色グラデーションのリングがゆっくり回転して「生きている」ことを示す */
.story__ring::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(from 0deg, #f59e0b, #ec4899, #8b5cf6, #38bdf8, #f59e0b);
  animation: story-spin 3.2s linear infinite;
}

/* 既読: リングは静かなグレーに変わり、回転も止まる */
.story[data-read="true"] .story__ring::before {
  background: #4b5563;
  animation: none;
}

.story__avatar {
  position: relative;
  z-index: 1;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  border: 3px solid #131317;
}

.story__avatar--letter {
  display: grid;
  place-items: center;
  background: linear-gradient(155deg, #38bdf8, #6366f1);
  color: #fff;
  font-weight: 700;
  font-size: 18px;
}

.story:hover .story__avatar {
  filter: brightness(1.08);
}

.story__name {
  font-size: 11.5px;
  letter-spacing: .02em;
  color: rgba(243, 240, 235, .65);
}
.story[data-read="true"] .story__name {
  color: rgba(243, 240, 235, .4);
}

@keyframes story-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .story__ring::before { animation: none; }
}

@media (max-width: 480px) {
  .stories { gap: 14px; padding: 20px 16px; }
  .story__ring { width: 56px; height: 56px; }
  .story__avatar { width: 50px; height: 50px; }
}

【JavaScript】
// ストーリーズ風リング: クリックしたアバターを既読状態にする(data-read属性を切り替えるだけでCSSが見た目を変える)
(() => {
  const stories = document.querySelectorAll('.story');
  if (!stories.length) return; // null安全

  stories.forEach((btn) => {
    btn.addEventListener('click', () => {
      btn.dataset.read = 'true';
    });
  });
})();

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。