Pulse Grid Loader パルスグリッドローダー
Cascading concentric pulse wave across a 3x3 dot matrix. 3x3ドットマトリクスを伝播する美しい同心円パルスウェーブ演出。
ライブデモ Live Demo
読み込み中... 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 |
|---|---|---|
size | 54px | Total width and height of the grid |
speed | 1.2s | Cycle duration of one full pulse wave |
color | #38bdf8 | Dot 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.