
Button with Inline Spinner Swap
Published
20 hand-coded CSS loading buttons for checkout flows, dashboards, upload forms and any action that waits on a server. Covers the inline spinner swap, the width jump when a label changes, double-submit guarding, the aria-busy contract screen readers need, and the states most builds skip — retry after failure, optimistic save, and an undo window instead of a confirm dialog. Scoped under .lb-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.
Related51 CSS Buttons20 CSS Loaders20 CSS Form Validation30 CSS Notifications

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published
Published

Published

Published

Published
grid-area: 1 / 1 and crossfade opacity between them — the button sizes to the wider of the two and never moves. Or set a min-inline-size in ch units, sized to the longest label the button will ever hold, which is simplest when the states are known up front. Or reserve the spinner's box with visibility: hidden rather than display: none, so the space exists before the spinner appears. What does not work is swapping textContent and hoping, which is the default approach and the reason so many production buttons visibly jump. The shift is not only ugly — it is a Cumulative Layout Shift event, and a button near other controls can push a mis-click onto the wrong target.disabled property on the element itself, not a class that only changes appearance. A class with pointer-events: none stops the mouse but not the keyboard — the button still activates on Enter or Space, so a determined double-tap still fires twice. disabled makes the browser refuse activation from every input method, which is the whole point. There is a real tradeoff, though: a disabled element is removed from the tab order and silently drops focus, so a keyboard user who just pressed the button is dumped back at the top of the document with no idea what happened. When focus must persist, use aria-disabled="true" plus a guard clause that returns early from the handler — the button stays focusable and announces as unavailable, and your own code blocks the second call. Belt and braces is to also guard on the state itself: if data-state is already pending, return immediately.aria-busy="true" on the button while the request is in flight and clear it on resolution, which is the standard way to say "this control is working". Announce the outcome through a live region: an element with aria-live="polite" that you write "Changes saved" into on success. Use role="alert" for the failure path instead, because a failed save is urgent and polite announcements can be queued behind other speech. Beyond that, never rely on colour alone for the error state — red plus a label, or red plus an icon. And if the button shows determinate progress, it needs role="progressbar" with aria-valuenow, aria-valuemin and aria-valuemax kept in sync, or the percentage is invisible to assistive technology.button.style.setProperty('--progress', pct), with the background reading that value through a gradient stop or a scaled pseudo-element. Register the property with @property and a <percentage> syntax so it can transition smoothly between updates instead of stepping between them — an unregistered custom property is just a string to the engine and will not interpolate. If you show a number alongside the bar, set font-variant-numeric: tabular-nums so the digits stay the same width and the label does not jitter on every update. One honesty point worth making: only show a percentage when you actually know the total. If the response is streamed or the size is unknown, a percentage is invented and users notice when it stalls at 90% — an indeterminate spinner is the more truthful control..lb-NN selector and custom property is framework-agnostic, and the five pure-CSS demos need no JavaScript anywhere. The state machine is where framework idiom matters, and modern React has purpose-built tools for it: useTransition gives you an isPending flag without managing a boolean yourself, and inside a form useFormStatus exposes the parent form's pending state to a nested submit button — which is exactly the case that used to require prop-drilling. Vue reaches for a ref plus onBeforeUnmount; Svelte a plain let and the cleanup returned from onMount. One trap is universal: clear the previous timeout before setting a new one, or a rapid re-click leaves a stale timer that flips the button back to idle while a second request is still running. For Tailwind the states map to data-[state=pending]: variants, which keeps the whole machine in markup.15 mouse-aware 3D tilt hover cards — e-commerce product spotlight, glassmorphism parallax team card, interactive pricing tier, holographic NFT collectible, pop-out mascot, dark tech grid with border glow, media player album art, blog article preview, cyberpunk neon glow, dashboard KPI widget, minimalist real estate, flip-to-back tilt, mobile app showcase, course learning card, and a Pure CSS touch-friendly tilt. Vanilla JS writes --rx/--ry/--mx/--my; all rendering stays in CSS on the GPU.
24 hand-coded CSS animated card patterns organised by what triggers the motion, not by card style. Scroll and entrance triggers: staggered grid reveals, @starting-style mount-in, scroll-driven scale and fade on animation-timeline: view(), View Transitions API card-to-detail morphs, blur-to-sharp lazy image loading, and FLIP re-layout when a filter changes. State-change triggers: add-to-cart success morph, like and save particle burst, expand and collapse to intrinsic height with calc-size(), swipe-to-dismiss on Pointer Events, and skeleton-to-loaded crossfade. Data-driven triggers: count-up KPI metrics, SVG sparkline draw-on-enter, and progress ring fill on load. Ambient idle loops: breathing glow, floating drift, animated gradient mesh backgrounds, conic rotating borders, auto-cycling testimonial decks, and live status pulses. Plus four hover patterns where the effect is the topic itself: cursor spotlight, holographic foil glare, corner ribbon slide, and horizontal accordion expand. Every card is scoped under a .ac-NN prefix for no-collision pasting, guards prefers-reduced-motion, animates compositor-only properties, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.
22 hand-coded CSS avatars for chat apps, team dashboards, comment threads, account menus, and social profiles. Covers circular and squircle shapes, gradient and conic story rings, hexagon clip-path crops, online status dots, notification count badges, verified checkmarks, stacked facepiles with plus-N overflow, initials fallbacks for users without photos, broken-image recovery, avatar pickers, and a size scale system where the ring, badge, and status dot all scale from a single custom property.