S-016 Loading simple

Pulse Grid Loader パルスグリッドローダー

Cascading concentric pulse wave across a 3x3 dot matrix. 3x3ドットマトリクスを伝播する美しい同心円パルスウェーブ演出。

ライブデモ Live Demo

Loading

読み込み中... Loading...

概要・用途・特徴Overview, Usage & Features

何ができるかWhat it does

A 3x3 dot matrix loader with cascading radial pulse waves radiating outwards from center.

3x3(9個)のドットマトリクスが中央から外周へと放射状に波及するパルスアニメーションローダー。

どこで使うかWhere to use

Used in modern AI dashboards, modal states, data widgets, or centered async wrappers.

モダンなAIダッシュボード、モーダル、ウィジェット、非同期データ待機エリアなどで活用できます。

特徴Key features

Pure CSS keyframes, scoped custom properties, accessibility support with role="status", prefers-reduced-motion fallback, and pause toggle.

CSSアニメーションのみで構成、カスタムプロパティでのサイズ・速度調整、role="status"対応、prefers-reduced-motion時の静止フォールバック、一時停止機能。

調整可能パラメータ Adjustable Parameters

Parameter Default Description
size54pxTotal width and height of the grid
speed1.2sCycle duration of one full pulse wave
color#38bdf8Dot fill and glow shadow color

実装コード Implementation Code

<div class="s016-loader" role="status" aria-label="Loading">
  <span class="s016-sr-only">Loading</span>
  <div class="s016-dot" aria-hidden="true"></div>
  <div class="s016-dot" aria-hidden="true"></div>
  <div class="s016-dot" aria-hidden="true"></div>
  <div class="s016-dot" aria-hidden="true"></div>
  <div class="s016-dot" aria-hidden="true"></div>
  <div class="s016-dot" aria-hidden="true"></div>
  <div class="s016-dot" aria-hidden="true"></div>
  <div class="s016-dot" aria-hidden="true"></div>
  <div class="s016-dot" aria-hidden="true"></div>
</div>
.s016-loader {
  --s016-size: 54px;
  --s016-color: #38bdf8;
  --s016-speed: 1.2s;
  width: var(--s016-size);
  height: var(--s016-size);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: calc(var(--s016-size) * 0.14);
  box-sizing: border-box;
}

.s016-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.s016-dot {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: var(--s016-color);
  box-shadow: 0 0 8px var(--s016-color);
  animation: s016-pulse var(--s016-speed) cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

.s016-dot:nth-child(1) { animation-delay: calc(var(--s016-speed) * -0.4); }
.s016-dot:nth-child(2) { animation-delay: calc(var(--s016-speed) * -0.2); }
.s016-dot:nth-child(3) { animation-delay: calc(var(--s016-speed) * -0.4); }
.s016-dot:nth-child(4) { animation-delay: calc(var(--s016-speed) * -0.2); }
.s016-dot:nth-child(5) { animation-delay: 0s; }
.s016-dot:nth-child(6) { animation-delay: calc(var(--s016-speed) * -0.2); }
.s016-dot:nth-child(7) { animation-delay: calc(var(--s016-speed) * -0.4); }
.s016-dot:nth-child(8) { animation-delay: calc(var(--s016-speed) * -0.2); }
.s016-dot:nth-child(9) { animation-delay: calc(var(--s016-speed) * -0.4); }

@keyframes s016-pulse {
  0%, 100% {
    transform: scale(0.35);
    opacity: 0.25;
  }
  50% {
    transform: scale(1);
    opacity: 1;
    filter: brightness(1.25);
  }
}

.s016-loader.paused,
.s016-loader[data-paused="true"],
.s016-loader.paused .s016-dot,
.s016-loader[data-paused="true"] .s016-dot {
  animation-play-state: paused !important;
}

@media screen and (prefers-reduced-motion: reduce) {
  .s016-dot {
    animation: none !important;
    opacity: 0.7;
    transform: scale(0.85);
  }
}
import React from 'react';
import './S-016.css';

export default function PulseGridLoader({
  size = 54,
  color = '#38bdf8',
  speed = 1.2,
  paused = false
}) {
  const style = {
    '--s016-size': `${size}px`,
    '--s016-color': color,
    '--s016-speed': `${speed}s`,
  };

  return (
    <div
      className={`s016-loader ${paused ? 'paused' : ''}`}
      style={style}
      role="status"
      aria-label="Loading"
    >
      <span className="s016-sr-only">Loading</span>
      {[...Array(9)].map((_, i) => (
        <div key={i} className="s016-dot" aria-hidden="true" />
      ))}
    </div>
  );
}
.s016-loader {
  width: var(--s016-size, 54px);
  height: var(--s016-size, 54px);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: calc(var(--s016-size, 54px) * 0.14);
  box-sizing: border-box;
}

.s016-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.s016-dot {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: var(--s016-color, #38bdf8);
  box-shadow: 0 0 8px var(--s016-color, #38bdf8);
  animation: s016-pulse var(--s016-speed, 1.2s) cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

.s016-dot:nth-child(1) { animation-delay: calc(var(--s016-speed, 1.2s) * -0.4); }
.s016-dot:nth-child(2) { animation-delay: calc(var(--s016-speed, 1.2s) * -0.2); }
.s016-dot:nth-child(3) { animation-delay: calc(var(--s016-speed, 1.2s) * -0.4); }
.s016-dot:nth-child(4) { animation-delay: calc(var(--s016-speed, 1.2s) * -0.2); }
.s016-dot:nth-child(5) { animation-delay: 0s; }
.s016-dot:nth-child(6) { animation-delay: calc(var(--s016-speed, 1.2s) * -0.2); }
.s016-dot:nth-child(7) { animation-delay: calc(var(--s016-speed, 1.2s) * -0.4); }
.s016-dot:nth-child(8) { animation-delay: calc(var(--s016-speed, 1.2s) * -0.2); }
.s016-dot:nth-child(9) { animation-delay: calc(var(--s016-speed, 1.2s) * -0.4); }

@keyframes s016-pulse {
  0%, 100% {
    transform: scale(0.35);
    opacity: 0.25;
  }
  50% {
    transform: scale(1);
    opacity: 1;
    filter: brightness(1.25);
  }
}

.s016-loader.paused,
.s016-loader[data-paused="true"],
.s016-loader.paused .s016-dot,
.s016-loader[data-paused="true"] .s016-dot {
  animation-play-state: paused !important;
}

@media screen and (prefers-reduced-motion: reduce) {
  .s016-dot {
    animation: none !important;
    opacity: 0.7;
    transform: scale(0.85);
  }
}

仕組みとカスタマイズHow It Works & Customization

仕組みHow it works

CSS Grid aligns nine circular dots. Calculated negative animation-delays create an organic concentric ripple wave across the matrix.

CSS Gridで9つのドットを配置し、中心からの距離に応じた負のanimation-delayによって外側へ広がる波及効果を生み出します。

カスタマイズ方法Customization

Adjust --s016-size, --s016-speed, and --s016-color through CSS custom properties or React props.

CSS変数(--s016-size, --s016-speed, --s016-color)またはReact propsでサイズ、周期、色を簡単にカスタマイズできます。

注意点Caveats

Maintain aria-label and role="status" for screen reader accessibility, and verify container padding on compact screens.

スクリーンリーダー用にaria-labelとrole="status"を維持し、極小スペースで使用する際はコンテナのパディングに配慮してください。

よくある質問 Frequently Asked Questions

How can I customize the pulse rate? パルスの速度を調整するにはどうすればよいですか?

Modify the --s016-speed CSS custom property or pass the speed prop in React.

CSSカスタムプロパティ --s016-speed を変更するか、Reactの speed prop に秒数を指定してください。

Does this support screen readers? スクリーンリーダーに対応していますか?

Yes, role="status" and an accessible visually hidden label announce the loading status.

はい、role="status" と視覚的に隠された代替テキストによって読み込み状態が支援技術に通知されます。

AIへの指示テンプレート AI Prompt Template

以下をAIに貼り付けると実装を依頼できます。 Paste the following into your AI assistant to request implementation.