S-020 Loading simple

Liquid Bubble Loader リキッドバブルローダー

Translucent capsule with organic rising and popping liquid bubbles. 微細な気泡の浮上と弾けるアニメーションによる親しみやすい液体ローダー。

ライブデモ Live Demo

Loading

抽出中... Filtering...

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

何ができるかWhat it does

An organic loader depicting rising liquid bubbles inside a rounded translucent capsule container.

丸みを帯びた半透明カプセルの中で、微小な気泡が湧き上がって頂部で弾ける液体風ローダーです。

どこで使うかWhere to use

Great for search filter processing, chemistry/science dashboards, consumer brand apps, and clean loading screens.

検索フィルターの絞り込み処理、サイエンス・バイオ系UI、リフレッシュ待機画面などに最適です。

特徴Key features

Staggered keyframe emergence, radial specular highlight gradients, role="status" accessibility, prefers-reduced-motion fallback.

時差をつけたバブルの上昇アニメーション、ハイライト光沢グラデーション、role="status"対応、prefers-reduced-motion対応。

調整可能パラメータ Adjustable Parameters

Parameter Default Description
width52pxWidth of the capsule container
speed2.2sDuration of one full bubble rise cycle
color#38bdf8Primary bubble glow color

実装コード Implementation Code

<div class="s020-loader" role="status" aria-label="Loading">
  <span class="s020-sr-only">Loading</span>
  <div class="s020-capsule" aria-hidden="true">
    <div class="s020-bubble"></div>
    <div class="s020-bubble"></div>
    <div class="s020-bubble"></div>
    <div class="s020-bubble"></div>
  </div>
</div>
.s020-loader {
  --s020-width: 52px;
  --s020-height: 72px;
  --s020-speed: 2.2s;
  --s020-bubble-color: #38bdf8;
  position: relative;
  width: var(--s020-width);
  height: var(--s020-height);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
}

.s020-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;
}

.s020-capsule {
  position: absolute;
  inset: 0;
  border-radius: 26px;
  border: 2px solid rgba(148, 163, 184, 0.25);
  background: rgba(15, 23, 42, 0.4);
  box-shadow: inset 0 0 12px rgba(56, 189, 248, 0.1);
  overflow: hidden;
}

.s020-bubble {
  position: absolute;
  bottom: 6px;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #ffffff, var(--s020-bubble-color));
  box-shadow: 0 0 8px var(--s020-bubble-color);
  animation: s020-rise var(--s020-speed) cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

.s020-bubble:nth-child(1) { left: 28%; width: 14px; height: 14px; animation-delay: 0s; }
.s020-bubble:nth-child(2) { left: 52%; width: 10px; height: 10px; animation-delay: calc(var(--s020-speed) * -0.33); }
.s020-bubble:nth-child(3) { left: 36%; width: 8px;  height: 8px;  animation-delay: calc(var(--s020-speed) * -0.66); }
.s020-bubble:nth-child(4) { left: 18%; width: 12px; height: 12px; animation-delay: calc(var(--s020-speed) * -0.85); }

@keyframes s020-rise {
  0% { transform: translateY(0) scale(0.3); opacity: 0; }
  15% { opacity: 0.9; transform: translateY(-8px) scale(1); }
  50% { transform: translateY(-32px) translateX(3px) scale(1.05); }
  85% { opacity: 0.85; transform: translateY(-52px) translateX(-2px) scale(0.95); }
  100% { opacity: 0; transform: translateY(-60px) scale(1.35); }
}

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

@media screen and (prefers-reduced-motion: reduce) {
  .s020-bubble {
    animation: none !important;
    opacity: 0.6;
    transform: translateY(-24px);
  }
}
import React from 'react';
import './S-020.css';

export default function LiquidBubbleLoader({
  width = 52,
  height = 72,
  speed = 2.2,
  color = '#38bdf8',
  paused = false
}) {
  const style = {
    '--s020-width': `${width}px`,
    '--s020-height': `${height}px`,
    '--s020-speed': `${speed}s`,
    '--s020-bubble-color': color,
  };

  return (
    <div
      className={`s020-loader ${paused ? 'paused' : ''}`}
      style={style}
      role="status"
      aria-label="Loading"
    >
      <span className="s020-sr-only">Loading</span>
      <div className="s020-capsule" aria-hidden="true">
        <div className="s020-bubble" />
        <div className="s020-bubble" />
        <div className="s020-bubble" />
        <div className="s020-bubble" />
      </div>
    </div>
  );
}
.s020-loader {
  width: var(--s020-width, 52px);
  height: var(--s020-height, 72px);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
}

.s020-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;
}

.s020-capsule {
  position: absolute;
  inset: 0;
  border-radius: 26px;
  border: 2px solid rgba(148, 163, 184, 0.25);
  background: rgba(15, 23, 42, 0.4);
  box-shadow: inset 0 0 12px rgba(56, 189, 248, 0.1);
  overflow: hidden;
}

.s020-bubble {
  position: absolute;
  bottom: 6px;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #ffffff, var(--s020-bubble-color, #38bdf8));
  box-shadow: 0 0 8px var(--s020-bubble-color, #38bdf8);
  animation: s020-rise var(--s020-speed, 2.2s) cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

.s020-bubble:nth-child(1) { left: 28%; width: 14px; height: 14px; animation-delay: 0s; }
.s020-bubble:nth-child(2) { left: 52%; width: 10px; height: 10px; animation-delay: calc(var(--s020-speed, 2.2s) * -0.33); }
.s020-bubble:nth-child(3) { left: 36%; width: 8px;  height: 8px;  animation-delay: calc(var(--s020-speed, 2.2s) * -0.66); }
.s020-bubble:nth-child(4) { left: 18%; width: 12px; height: 12px; animation-delay: calc(var(--s020-speed, 2.2s) * -0.85); }

@keyframes s020-rise {
  0% { transform: translateY(0) scale(0.3); opacity: 0; }
  15% { opacity: 0.9; transform: translateY(-8px) scale(1); }
  50% { transform: translateY(-32px) translateX(3px) scale(1.05); }
  85% { opacity: 0.85; transform: translateY(-52px) translateX(-2px) scale(0.95); }
  100% { opacity: 0; transform: translateY(-60px) scale(1.35); }
}

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

@media screen and (prefers-reduced-motion: reduce) {
  .s020-bubble {
    animation: none !important;
    opacity: 0.6;
    transform: translateY(-24px);
  }
}

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

仕組みHow it works

Four individual bubble nodes are assigned varying horizontal offsets and negative animation delays, ascending with subtle horizontal drift.

4つのバブル要素に異なる水平位置と負の遅延を設定し、左右の揺らぎを伴う上昇キーフレームを適用しています。

カスタマイズ方法Customization

Tweak duration, bubble color, and capsule dimensions via CSS variables or React component props.

CSSカスタムプロパティ(--s020-width, --s020-height, --s020-speed, --s020-bubble-color)またはReact propsで自在に色や速度を変更できます。

注意点Caveats

Provide a meaningful text alternative in aria-label or surrounding DOM for assistive technology.

支援技術ユーザー向けに、aria-labelまたは周囲のテキストでローディング中であることを明示してください。

よくある質問 Frequently Asked Questions

How can I change the bubble ascent rate? バブルの上昇速度を変更するにはどうすればよいですか?

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

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

Can the outer capsule boundary be hidden? 外側のカプセルの枠線を非表示にできますか?

Yes, set border: none and background: transparent on .s020-capsule for a borderless bubble stream.

はい、.s020-capsule の border を none に設定することで枠線なしのシームレスなバブル上昇にできます。

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

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