Loaders✨ Premium

Bouncing Squares

Three 15 px squares, #6366f1 fill, no border radius — rising 20 px with 80% compression at the peak, in a 100 ms cascade. Pure CSS, no JavaScript.

CSSPlayful

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Loaders has 29 effects, including 4 free. Explore the category →

3 usage examples

Processing a batch of photos — export in progress — Bouncing Squares example 1

① Processing a batch of photos — export in progress

WhenPhoto editing or publishing tool: an export of 40 images has started (resize to 2048 px, JPEG). Files go through one after another; the counter gives the progress, the loader gives the rhythm of the queue.
WhyA square is a thumbnail: three squares side by side read as a queue of images, something no circle suggests. The left-to-right cascade — 100 ms between squares — states the order of passage: the first image is done, the next one is coming. A spinning ring would only say “please wait”. The counter “12 of 40 processed” carries the measure, the loader carries the queue. Scaled to 24 px, the squares stay crisp next to 44 px thumbnails.
SettingsSquares 24 × 24 px, gap: 12px, translateY(-32px) (20 px × 24/15: the rise follows the size), default #6366f1 color, .8s duration (slightly brisker rhythm — the queue keeps moving); loader sitting at the bottom of a 56 px box so the bounce has room, left of the counter, role="status" on the status bar.
Children's learning app — between two exercises — Bouncing Squares example 2

② Children's learning app — between two exercises

WhenLearning app for 6-to-9-year-olds: the exercise is passed, the congratulations screen shows and the next one loads. Two or three seconds that must stay inside the game.
WhyThree blocks jumping one after another and squashing at the top: that is the vocabulary of cartoon animation (the squash-and-stretch of scale(.8), pushed here to .6), not that of business software. A ring or a bar would pull the child back into an adult world right on the reward screen. The 0.7 s duration makes the jump lively, and one color per square — allowed by the :nth-child rules — turns the three squares into play cubes. At 24 px on a light background they read on a tablet held at arm's length.
SettingsSquares 24 × 24 px, gap: 12px, dedicated keyframe translateY(-32px) scale(.6), duration .7s, three colors via :nth-child(1) #2563eb, (2) #ea580c, (3) #16a34a on a cream #fff7ed background (contrast ≥ 3.1:1); visible label “Next exercise coming up…” under the loader, role="status" on the waiting block.
Kanban board opening — the columns fill up — Bouncing Squares example 3

③ Kanban board opening — the columns fill up

WhenProject management tool: the board opens, the column headers are there, the cards arrive column after column from the API. The card skeletons stay visible behind the indicator.
WhyThree squares for three columns: each square takes its column's color (to do, in progress, done) and the left-to-right bounce follows the reading direction of the board — the one in which the cards will appear. The square shape echoes the cards themselves; a circle would have nothing to do with what shows up next. The duration raised to 1.4 s gives a settled rhythm, that of a screen building itself rather than a computation spinning.
SettingsSquares 24 × 24 px, gap: 12px, translateY(-32px), duration 1.4s, per-square colors :nth-child(1) #818cf8, (2) #f59e0b, (3) #34d399 — the same as the square markers in the column headers; centered in a floating box above the skeleton columns (opacity 0.5), role="status" on the box.

How it works

The .loader-squares container is a single-row flex with gap: 8px and no explicit height. Its three span children are each 15 × 15 px, filled #6366f1, border-radius: 0 — sharp-cornered squares, not circles. Measured in Chromium: 14.77 to 15.00 px depending on the animation phase at capture time. No CSS variables, no JavaScript.

The squareBounce keyframe applies two transforms simultaneously at 50% of the cycle: translateY(-20px) and scale(.8), returning to translateY(0) scale(1) at both ends. Measured at peak in Chromium, the matrix shows scale(0.800) and a vertical offset of −20 px: the square moves up exactly 20 px and occupies 12 × 12 px at that point. The scale(.8) is not a depth effect — it is the squash-and-stretch technique: an elastic object decelerating at the top of its arc compresses slightly, and this single value reproduces that without shadows or secondary elements.

The positive delays 0 s, 0.1 s and 0.2 s stagger the three squares by 100 ms each. Because positive CSS delays only apply on the first cycle, subsequent cycles overlap in a permanent cascade: at steady state, the first square is at its peak when the second is halfway up and the third is leaving the ground. The eye reads a left-to-right wave at 1 Hz (one full cycle per second).

Accessibility

  • prefers-reduced-motion not handled: measured with reducedMotion: 'reduce' emulation, all three animations remain in the running state. The code contains no @media (prefers-reduced-motion: reduce) rule. To respect the preference, add @media (prefers-reduced-motion: reduce) { .loader-squares span { animation: none; } } — the squares become static, an honest fallback for a loading indicator.
  • role, aria-live and alternative text are absent: for a functional loading indicator, add role="status" aria-live="polite" to .loader-squares and a <span class="sr-only">Loading…</span> inside it; remove it when loading ends. If the loader is purely decorative (visible text already indicates the state), set aria-hidden="true" on the container.
  • Contrast: #6366f1 on #0a0a0f (site background) = 4.5:1 — above the 3:1 threshold for non-text graphical components (WCAG 1.4.11). Verify with your own background if you change the color.
  • Integration: the three span elements form a 61 × 15 px box at rest (measured: 3 × 15 px + 2 × 8 px = 61 px). During the bounce, squares extend 20 px upward — allow at least 35 px of height in the parent container and avoid overflow: hidden on it.

Browser compatibility

CSS animations and 2D transforms: core properties available in all major browsers since 2012. No JavaScript, no CSS variables, no dependencies.

Chrome 43+✓ Full
Firefox 16+✓ Full
Safari 9+✓ Full
Edge 12+✓ Full
Mobile iOS✓ Full
Android Chrome✓ Full

Without CSS animation support (@keyframes), the three squares remain still at their resting position. No content is lost, but the indicator stops signaling — add alternative text if the loading state is functional.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="loader-squares">
  <span></span>
  <span></span>
  <span></span>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Color (CSS — background: #6366f1) #6366f1 Fill color for all three squares. Change the value on .loader-squares span to update them all at once. For different colors per square, add :nth-child(1), (2), (3) rules with their own background.
Size (CSS — width: 15px; height: 15px) 15 × 15 px Square dimensions. Change both together to keep the square shape. 8 px for a button; 24 px for a large indicator. The bounce height is absolute — scale translateY proportionally.
Rise height (CSS — translateY(-20px)) −20 px Maximum height at 50% of the cycle, in absolute pixels. With 8 px squares, -10px keeps the proportions. -30px with 15 px squares lengthens the arc without changing the duration, making the motion read as faster.
Peak compression (CSS — scale(.8)) 0.8 (80% of full size) Scale factor at mid-animation. scale(1) removes the squash-and-stretch: squares rise without shrinking — more mechanical. scale(.6) exaggerates the compression and makes the animation more expressive.
Cycle duration (CSS — animation: 1s) 1 s Duration of one full bounce per square. .7s gives a brisk rhythm; 1.4s a slow, steady motion. The relative stagger (100 ms out of 1 s = 10% of the cycle) stays proportional if you also update the delays.
Stagger between squares (CSS — animation-delay: .1s and .2s) 0 s / 0.1 s / 0.2 s 100 ms gap between each square, i.e. 10% of the cycle. .15s and .3s widen the cascade (15%); 0s for all three synchronizes the bounces into a single simultaneous jump.
Spacing (CSS — gap: 8px) 8 px Space between squares. 4px for a compact group (button); 12px to open up the indicator. Total width at rest: 3 × size + 2 × gap — with 15 px and 8 px, that is 61 px.

FAQ

The scale(.8) at 50% reproduces squash-and-stretch: an elastic object decelerating at the top of its arc compresses slightly. Here, the 80% reduction creates that illusion without any shadow or extra layer. Remove the scale and the squares rise in a straight line: visually crisper, less lively.
Three technical differences. Shape: fx-0422 uses circles (border-radius: 50%), fx-0433 uses squares (border-radius: 0). Transform: fx-0422 runs from scale(0) to scale(1) — dots appear and disappear — while fx-0433 combines translateY with scale(.8) and never reduces to zero. Timing: fx-0422 uses negative delays (−0.32 s, −0.16 s) that enter steady state on the first frame; fx-0433 uses positive delays (0.1 s, 0.2 s) that produce a visible staggered start on first load.
Add a <span></span> to the HTML and a rule .loader-squares span:nth-child(4) { animation-delay: .3s } (and :nth-child(5) { animation-delay: .4s } for five). The base CSS does not need changing: every span inherits from .loader-squares span. Total width: 4 × 15 px + 3 × 8 px = 84 px; 5 × 15 px + 4 × 8 px = 107 px.