search
  • Sign In
  • Sign Up
Password reset successful

Follow the proiects vou are interested in andi aet the latestnews about them taster

Table of Contents
What CSS properties actually make a spinner spin
How to build a minimal spinner with ::after
Why transform: rotate() beats animation: rotation
When to avoid pure-CSS spinners entirely
Home Web Front-end CSS Tutorial How to create a loading spinner with pure CSS?

How to create a loading spinner with pure CSS?

Jan 25, 2026 am 03:07 AM

The CSS properties that make a spinner spin are animation (with @keyframes rotating transform: rotate()) and border (with asymmetric coloring); animation-timing-function: linear ensures smooth motion, and animation-iteration-count: infinite enables continuous rotation.

How to create a loading spinner with pure CSS?

What CSS properties actually make a spinner spin

A spinner isn’t just a circle — it’s an element that rotates continuously using animation and transform: rotate(). The key is combining @keyframes that go from 0deg to 360deg, applied to an inline or block-level element with a defined size and border (or pseudo-elements). Without animation-timing-function: linear, the motion feels jerky; without animation-iteration-count: infinite, it stops after one cycle.

Do this:

  • Use border with transparent sides to form a “C” shape — most lightweight spinners rely on this trick
  • Set border-radius: 50% on the element to keep proportions round
  • Always include animation-duration (e.g., 0.8s) — too fast feels frantic, too slow feels stuck
  • Avoid width/height alone — use aspect-ratio: 1 or explicit same-value dimensions for consistency

How to build a minimal spinner with ::after

You don’t need extra HTML markup. A single element like a <div class="spinner"></div> can host the spinner via its ::after pseudo-element — keeps DOM clean and avoids wrapper bloat.

Example CSS:

.spinner {
  position: relative;
  width: 24px;
  height: 24px;
}
.spinner::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 2px solid #ccc;
  border-top-color: #007bff;
  border-radius: 50%;
  animation: spin 0.8s ease-in-out infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

Why this works: border-top-color overrides only the top edge, creating visual asymmetry needed for rotation perception. ease-in-out softens start/stop — but if you want perfectly uniform speed, switch to linear.

Why transform: rotate() beats animation: rotation

There’s no rotation CSS property. People sometimes mistakenly try animation: rotation 1s infinite — that fails because rotation isn’t a valid property. The correct path is always transform: rotate() inside @keyframes.

Common pitfalls:

  • Forgetting transform-origin: center — causes wobble if the element isn’t perfectly square or has padding
  • Using rotateX() or rotateY() — those create 3D tilts, not flat spinning
  • Applying animation to a parent with overflow: hidden but no border-radius — clipped corners break the illusion of smoothness

When to avoid pure-CSS spinners entirely

CSS spinners are great for simple states, but they’re inert — no programmatic control over start/stop without toggling classes. If your app needs to coordinate loading with API calls, error fallbacks, or timeout handling, you’ll still need JS to add/remove a loading class.

Also watch for:

  • Accessibility: Screen readers ignore decorative spinners. Always pair with aria-busy="true" and aria-live="polite" on the relevant container
  • Performance: Animating transform is GPU-accelerated, but animating border-color or box-shadow alongside it can trigger layout thrashing
  • Dark mode: Hardcoded colors like #ccc may vanish on dark backgrounds — prefer currentcolor or CSS custom properties

The subtle part isn’t making it spin — it’s making sure it spins *only when needed*, stays accessible, and doesn’t fight your layout or theme.

The above is the detailed content of How to create a loading spinner with pure CSS?. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undress AI Tool

Undress AI Tool

Undress images for free

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

ArtGPT

ArtGPT

AI image generator for creative art from text prompts.

Stock Market GPT

Stock Market GPT

AI powered investment research for smarter decisions

Popular tool

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

CSS mobile performance optimization_Use will-change to inform transition attributes in advance CSS mobile performance optimization_Use will-change to inform transition attributes in advance Mar 12, 2026 am 11:15 AM

Will-change should only be declared for transform and opacity attributes that will change frequently and can trigger synthesis; avoid abusing invalid attributes such as all, left, top, etc., which must be added/removed dynamically, and be used with caution in scrolling containers. Mobile terminals need to take into account compatibility and memory limitations.

How to make a simple fixed bottom toolbar with CSS_Set bottom:0 through position:fixed How to make a simple fixed bottom toolbar with CSS_Set bottom:0 through position:fixed Mar 10, 2026 pm 02:12 PM

The main reason why bottom:0 does not take effect is that the ancestor element triggers transform/will-change/filter to create a new containing block, so that the fixed element is positioned relative to it rather than the viewport; dynamic changes in the iOS Safari address bar cause occlusion; fixed elements need to be given way with padding-bottom; z-index failure is often caused by the parent creating a cascading context.

How to avoid content overflow when using CSS with float_Set the box-sizing of the box model and ensure that the sum of the percentages does not exceed 100% How to avoid content overflow when using CSS with float_Set the box-sizing of the box model and ensure that the sum of the percentages does not exceed 100% Mar 12, 2026 pm 12:00 PM

If the floating element cannot open the parent container, BFC needs to be triggered. Overflow:hidden is commonly used; box-sizing:border-box must be set on the floating element itself; the percentage exceeds 100% due to whitespace characters, border/padding and pixel rounding; in IE, pay attention to the box-sizing prefix and margin parsing bugs.

How to set a transparent frosted sidebar with CSS_using rgba color through background-filter How to set a transparent frosted sidebar with CSS_using rgba color through background-filter Apr 03, 2026 pm 03:57 PM

In Safari, the background-filter needs to be prefixed with -webkit- and the background is opaque to take effect; the blur value cannot be 0; the size of the blurred area affects performance more than the numerical value; text needs to be enhanced with contrast and strokes to ensure readability.

How to make a simple jitter tip with CSS_using keyframes and rotation How to make a simple jitter tip with CSS_using keyframes and rotation Apr 03, 2026 pm 03:42 PM

Shake animation should use translateX() to achieve left and right displacement instead of rotate(); it needs to be combined with @keyframes to define a 0%/25%/50%/75%/100% offset sequence, and the offset is controlled within ±2px~±6px; animation-fill-m must be added ode:forwards (or use both), select cubic-bezier(.36,.07,.19,.97) for easing; the old animation must be cleared and forced to rearrange before JS is triggered; use will-change with caution on the mobile terminal, and only add translateZ(0) to ensure hardware acceleration.

How CSS solves the absolute positioning offset bug under IE_Fixed by setting the hasLayout attribute How CSS solves the absolute positioning offset bug under IE_Fixed by setting the hasLayout attribute Apr 03, 2026 pm 03:48 PM

The essence of the inaccurate offset of position:absolute in IE6–8 is that the parent container does not trigger hasLayout, causing the absolute positioning reference to the wrong containing block; it needs to be actively triggered by reliable methods such as zoom:1, rather than relying solely on position:relative.

How to add styles to Bootstrap switch components with CSS_Transformation through checkbox pseudo-element How to add styles to Bootstrap switch components with CSS_Transformation through checkbox pseudo-element Apr 03, 2026 pm 03:54 PM

The default style of the Bootstrap switch cannot be changed because it draws visual effects through pseudo elements::before/::after, which needs to cover .form-switch.form-check-input .form-check-label::before (track) and ::after (slider) and handle all states such as: checked, :disabled and :checked:disabled.

How to implement Bootstrap fixed header scrolling with CSS_Set the position attribute of thead How to implement Bootstrap fixed header scrolling with CSS_Set the position attribute of thead Apr 03, 2026 pm 04:00 PM

The fundamental reason is that the browser does not support the direct application of position:sticky to the thead, because the display:table of the parent container table limits the sticky behavior; the tbody must be moved into an independent scrolling container and a display:block split structure must be set up to unify the column width with colgroup to achieve a stable fixed header.

Related articles