06Liquid fill circle
Water rises inside a rounded ring while its wavy surface comes from a big rotating square; only the rise itself moves with translateY, so the surface keeps rippling quietly.
Best forFile upload progress, survey response gauge
translate: 0 -60pxrotateoverflow: hidden

<main class="stage">
<div class="lq-tube is-demo" aria-hidden="true">
<div class="lq-water"></div>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: 2s;
$easing: $ease-spring;
$color: $subject-sky;
@include stage($stage-yellow);
// 실사용: 로딩 중에만 .is-loading
.lq-tube {
position: relative;
width: 150px;
height: 150px;
border: 4px solid $subject-ink;
border-radius: 50%;
overflow: hidden; // 원 밖의 물은 잘린다
}
.lq-water {
position: absolute;
top: 100%;
left: calc(50% - 55px);
width: 110px;
height: 110px;
border-radius: 36%;
background: $color;
}
.lq-tube.is-loading .lq-water,
.lq-tube.is-demo .lq-water {
// 두 동작의 조합: 사각형의 회전(물결 윗면) + translateY 상승
animation: lq-wave 2s linear infinite,
lq-rise $duration $easing infinite;
}
@keyframes lq-wave {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes lq-rise {
0% { translate: 0 0; }
50% { translate: 0 -60px; }
100% { translate: 0 0; }
}
@media (prefers-reduced-motion: reduce) {
.lq-tube.is-loading .lq-water,
.lq-tube.is-demo .lq-water {
animation: none !important;
translate: 0 -30px !important; // 절반쯤 찬 물에 수면이 기울어진 정지 모양
transform: rotate(12deg) !important;
}
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffd23f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.lq-tube {
position: relative;
width: 150px;
height: 150px;
border: 4px solid #17141a;
border-radius: 50%;
overflow: hidden;
}
.lq-water {
position: absolute;
top: 100%;
left: calc(50% - 55px);
width: 110px;
height: 110px;
border-radius: 36%;
background: #6ec8ff;
}
.lq-tube.is-loading .lq-water,
.lq-tube.is-demo .lq-water {
animation: lq-wave 2s linear infinite, lq-rise 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
@keyframes lq-wave {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes lq-rise {
0% {
translate: 0 0;
}
50% {
translate: 0 -60px;
}
100% {
translate: 0 0;
}
}
@media (prefers-reduced-motion: reduce) {
.lq-tube.is-loading .lq-water,
.lq-tube.is-demo .lq-water {
animation: none !important;
translate: 0 -30px !important;
transform: rotate(12deg) !important;
}
}import { CSSProperties } from "react";
import styles from "./LiquidFill.module.scss";
type Vars = CSSProperties & {
"--duration"?: string;
"--easing"?: string;
"--color"?: string;
};
export interface LiquidFillProps {
duration?: string;
easing?: string;
color?: string;
/** 원 테두리 색 */
frameColor?: string;
}
export default function LiquidFill({
duration = "2s",
easing = "cubic-bezier(.2, .8, .2, 1)",
color = "#6ec8ff",
frameColor = "#17141a",
}: LiquidFillProps) {
const style: Vars = {
"--duration": duration,
"--easing": easing,
"--color": color,
"--frame-color": frameColor,
} as Vars & { "--frame-color"?: string };
return (
<div className={styles.tube} style={style} aria-hidden="true">
<div className={styles.water} />
</div>
);
}06 Liquid fill circle — Measured area 3.84% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · 9 Loading Screen Designs — Pretty CSS Loaders
The post this demo lives in
9 Loading Screen Designs — Pretty CSS LoadersA loading screen design is the first thing your site says while the user waits. A pastel ring or a circle filling with w See all demos in this post


