SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/buttons

8 CSS Buttons

6 Pure CSS2 with JSPublished 2026-10-03Free · no sign-up

Hand-written CSS buttons you can copy into any project: primary and secondary actions, a 3D press button, a loading state, a segmented group and a copy-to-clipboard button. Every one has visible keyboard focus, and the animated ones calm down for visitors who ask for reduced motion.

01. Gradient Glow Button

Pure CSS

A primary call-to-action with a two-stop gradient and a soft coloured glow that grows on hover. The glow is a blurred pseudo-element behind the button, so the button itself never changes size and nothing around it shifts. Keyboard focus gets the same lift as hover.

View code
<button class="ttb-glow">Start free trial</button>
.ttb-glow {
  position: relative;
  padding: 14px 28px;
  border: 0;
  border-radius: 12px;
  font: 700 16px/1 Inter, system-ui, sans-serif;
  color: #fff;
  background: linear-gradient(120deg, #7DA3FF, #5B8CFF);
  cursor: pointer;
  isolation: isolate;
  transition: transform .2s ease;
}
.ttb-glow::after {
  content: "";
  position: absolute;
  inset: 6px;
  z-index: -1;
  border-radius: inherit;
  background: inherit;
  filter: blur(18px);
  opacity: .55;
  transition: opacity .2s ease, inset .2s ease;
}
.ttb-glow:hover,
.ttb-glow:focus-visible { transform: translateY(-2px); }
.ttb-glow:hover::after,
.ttb-glow:focus-visible::after { opacity: .9; inset: 0; }
.ttb-glow:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .ttb-glow, .ttb-glow::after { transition: none; }
}

02. Ghost Button with Sweep Fill

Pure CSS

An outline button for secondary actions. On hover a solid fill sweeps in from the left while the label switches to the dark ink colour, so the text keeps its contrast at every point of the animation. The sweep is a scaled background layer, which keeps it on the compositor and smooth on slow phones.

View code
<button class="ttb-ghost">See pricing</button>
.ttb-ghost {
  position: relative;
  overflow: hidden;
  padding: 13px 26px;
  border: 1.5px solid #7DA3FF;
  border-radius: 999px;
  background: transparent;
  color: #7DA3FF;
  font: 600 15px/1 Inter, system-ui, sans-serif;
  cursor: pointer;
  isolation: isolate;
  transition: color .25s ease;
}
.ttb-ghost::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: #7DA3FF;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .3s cubic-bezier(.2,.7,.2,1);
}
.ttb-ghost:hover,
.ttb-ghost:focus-visible { color: #080A0F; }
.ttb-ghost:hover::before,
.ttb-ghost:focus-visible::before { transform: scaleX(1); }
.ttb-ghost:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

03. 3D Press Button

Pure CSS

A chunky button with a solid bottom edge that sinks when you press it, like a physical key. The depth comes from a hard box-shadow, and the active state moves the button down by exactly the shadow height, so the base stays put. Good for games, quizzes and playful sign-up flows.

View code
<button class="ttb-press">Play now</button>
.ttb-press {
  padding: 14px 30px;
  border: 0;
  border-radius: 14px;
  background: #5B8CFF;
  color: #fff;
  font: 800 16px/1 Inter, system-ui, sans-serif;
  letter-spacing: .02em;
  cursor: pointer;
  box-shadow: 0 6px 0 #2f5fd6, 0 12px 18px -6px rgba(47, 95, 214, .5);
  transform: translateY(0);
  transition: transform .08s ease, box-shadow .08s ease;
}
.ttb-press:hover { background: #6b97ff; }
.ttb-press:active {
  transform: translateY(6px);
  box-shadow: 0 0 0 #2f5fd6, 0 4px 8px -4px rgba(47, 95, 214, .5);
}
.ttb-press:focus-visible { outline: 3px solid #080A0F; outline-offset: 4px; }

04. Button with Loading State

CSS + JS

A submit button that swaps its label for a spinner while work is in progress, then shows a confirmation. The width is locked during loading so the layout does not jump, the button is disabled to stop double submits, and aria-busy tells screen readers what is happening.

View code
<button class="ttb-load" type="button">
  <span class="ttb-load-label">Save changes</span>
  <span class="ttb-load-spin" aria-hidden="true"></span>
</button>
.ttb-load {
  position: relative;
  display: inline-grid;
  place-items: center;
  min-width: 160px;
  padding: 14px 24px;
  border: 0;
  border-radius: 10px;
  background: #5B8CFF;
  color: #fff;
  font: 700 15px/1 Inter, system-ui, sans-serif;
  cursor: pointer;
}
.ttb-load > * { grid-area: 1 / 1; }
.ttb-load-spin {
  width: 18px;
  height: 18px;
  border: 2.5px solid rgba(255, 255, 255, .35);
  border-top-color: #fff;
  border-radius: 50%;
  opacity: 0;
  animation: ttb-spin .7s linear infinite;
}
.ttb-load[aria-busy="true"] { cursor: progress; opacity: .9; }
.ttb-load[aria-busy="true"] .ttb-load-label { opacity: 0; }
.ttb-load[aria-busy="true"] .ttb-load-spin { opacity: 1; }
.ttb-load.is-done { background: #22C55E; }
.ttb-load:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
@keyframes ttb-spin { to { transform: rotate(360deg); } }
document.querySelectorAll('.ttb-load').forEach(function (btn) {
  var label = btn.querySelector('.ttb-load-label');
  btn.addEventListener('click', function () {
    if (btn.getAttribute('aria-busy') === 'true') return;
    btn.setAttribute('aria-busy', 'true');
    btn.disabled = true;
    setTimeout(function () {
      btn.removeAttribute('aria-busy');
      btn.classList.add('is-done');
      label.textContent = 'Saved';
      setTimeout(function () {
        btn.classList.remove('is-done');
        label.textContent = 'Save changes';
        btn.disabled = false;
      }, 1500);
    }, 1400);
  });
});

05. Icon Button with Sliding Arrow

Pure CSS

A text button with an inline SVG arrow that slides forward on hover. The arrow is decorative, so it carries aria-hidden and the label alone names the action. Use it for 'Read more', 'Continue' and 'Next step' links where you want a hint of motion without a loud animation.

View code
<a class="ttb-arrow" href="#">
  Continue
  <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
</a>
.ttb-arrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px 12px 22px;
  border-radius: 10px;
  background: #121722;
  border: 1px solid rgba(255, 255, 255, .14);
  color: #F5F7FA;
  font: 600 15px/1 Inter, system-ui, sans-serif;
  text-decoration: none;
  transition: border-color .2s ease, background .2s ease;
}
.ttb-arrow svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: #7DA3FF;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform .25s cubic-bezier(.2,.7,.2,1);
}
.ttb-arrow:hover,
.ttb-arrow:focus-visible { border-color: #7DA3FF; background: #161c29; }
.ttb-arrow:hover svg,
.ttb-arrow:focus-visible svg { transform: translateX(4px); }
.ttb-arrow:focus-visible { outline: 2px solid #7DA3FF; outline-offset: 3px; }

06. Segmented Button Group

Pure CSS

Three buttons joined into one pill for switching views, such as Day, Week and Month. It is built on radio inputs, so only one option can be on, arrow keys move between options for free, and it works with JavaScript turned off. The selected option uses the accent fill.

View code
<fieldset class="ttb-seg">
  <legend>Range</legend>
  <label><input type="radio" name="ttb-range" checked><span>Day</span></label>
  <label><input type="radio" name="ttb-range"><span>Week</span></label>
  <label><input type="radio" name="ttb-range"><span>Month</span></label>
</fieldset>
.ttb-seg {
  display: inline-flex;
  gap: 2px;
  margin: 0;
  padding: 4px;
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 999px;
  background: #0E1118;
}
.ttb-seg legend {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}
.ttb-seg input { position: absolute; opacity: 0; pointer-events: none; }
.ttb-seg span {
  display: block;
  padding: 9px 18px;
  border-radius: 999px;
  color: #A7AFBD;
  font: 600 14px/1 Inter, system-ui, sans-serif;
  cursor: pointer;
  transition: background .2s ease, color .2s ease;
}
.ttb-seg label:hover span { color: #F5F7FA; }
.ttb-seg input:checked + span { background: #5B8CFF; color: #080A0F; }
.ttb-seg input:focus-visible + span { outline: 2px solid #7DA3FF; outline-offset: 2px; }

07. Animated Border Beam Button

Pure CSS

A dark button whose border has a moving highlight that travels around the edge. The beam is a rotating conic gradient clipped to the border with a padding-box and border-box background pair, so there are no extra elements. The animation stops for visitors who ask for reduced motion.

View code
<button class="ttb-beam">Join the beta</button>
@property --ttb-angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}
.ttb-beam {
  padding: 14px 28px;
  border: 2px solid transparent;
  border-radius: 12px;
  color: #F5F7FA;
  font: 700 15px/1 Inter, system-ui, sans-serif;
  cursor: pointer;
  background:
    linear-gradient(#0E1118, #0E1118) padding-box,
    conic-gradient(from var(--ttb-angle), transparent 70%, #7DA3FF 85%, #5B8CFF 92%, transparent) border-box;
  animation: ttb-beam 3s linear infinite;
}
.ttb-beam:hover {
  color: #fff;
  background:
    linear-gradient(#121722, #121722) padding-box,
    conic-gradient(from var(--ttb-angle), transparent 60%, #7DA3FF 80%, #5B8CFF 92%, transparent) border-box;
}
.ttb-beam:focus-visible { outline: 2px solid #7DA3FF; outline-offset: 3px; }
@keyframes ttb-beam { to { --ttb-angle: 360deg; } }
@media (prefers-reduced-motion: reduce) { .ttb-beam { animation: none; } }

08. Copy Button with Feedback

CSS + JS

A small button that copies text to the clipboard and confirms it with a tick and a 'Copied' label for a moment. It uses the Clipboard API, announces the result through a polite live region for screen readers, and tells the visitor to press Ctrl+C when the browser blocks clipboard access.

View code
<div class="ttb-copy">
  <code>npm install taimur-ui</code>
  <button type="button" data-copy="npm install taimur-ui">Copy</button>
  <span class="ttb-copy-sr" aria-live="polite"></span>
</div>
.ttb-copy {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 8px 8px 8px 16px;
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 12px;
  background: #0E1118;
}
.ttb-copy code { color: #D7DCE6; font: 500 14px/1 "JetBrains Mono", ui-monospace, monospace; }
.ttb-copy button {
  min-width: 82px;
  padding: 9px 14px;
  border: 0;
  border-radius: 8px;
  background: #1c2333;
  color: #F5F7FA;
  font: 600 13px/1 Inter, system-ui, sans-serif;
  cursor: pointer;
  transition: background .2s ease, color .2s ease;
}
.ttb-copy button:hover { background: #26304a; }
.ttb-copy button.is-copied { background: #22C55E; color: #052e16; }
.ttb-copy button:focus-visible { outline: 2px solid #7DA3FF; outline-offset: 2px; }
.ttb-copy-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
document.querySelectorAll('.ttb-copy button').forEach(function (btn) {
  var sr = btn.parentNode.querySelector('.ttb-copy-sr');
  btn.addEventListener('click', function () {
    var text = btn.getAttribute('data-copy');
    var done = function (ok) {
      btn.textContent = ok ? '✓ Copied' : 'Press Ctrl+C';
      btn.classList.toggle('is-copied', ok);
      sr.textContent = ok ? 'Copied to clipboard' : 'Copy failed';
      setTimeout(function () { btn.textContent = 'Copy'; btn.classList.remove('is-copied'); }, 1600);
    };
    if (navigator.clipboard) navigator.clipboard.writeText(text).then(function () { done(true); }, function () { done(false); });
    else done(false);
  });
});

About these CSS Buttons

What makes a good CSS button

A button has one job: to look pressable and say what happens next. Give it a label that names the action ("Save changes", not "Submit"), at least 44 by 44 pixels of tap area on touch screens, and a focus style you can actually see. Hover effects are a bonus for mouse users; they should never be the only way to tell that something is clickable.

Button or link?

Use a <button> when the click does something on the page, such as submitting a form, opening a menu or copying text. Use an <a href> when it takes the visitor to another page. Styling an anchor to look like a button is fine; using a div with a click handler is not, because keyboards and screen readers cannot reach it.

Animating buttons without jank

Animate transform and opacity, not width, height or margins. The demos here move glows, fills and arrows with transforms or on pseudo-elements, so the button never changes size and the content around it never shifts. Keep hover transitions short, between 150 and 300 milliseconds, so the button feels responsive.

How to use these demos

Each demo is scoped with its own class prefix (.ttb-), so you can paste several into one page without collisions. Click Open in editor to change colours and text with a live preview, then copy the result. The colours use the same blue palette as this site; swap the hex values for your brand.

Frequently asked questions

Are these CSS buttons free to use?

Yes. You can copy them into personal and commercial projects. No sign-up and no attribution required.

Do they need a framework?

No. They are plain HTML and CSS. Two demos add a few lines of vanilla JavaScript for the loading and copy states.

Will they work on mobile?

Yes. They use relative layout, readable font sizes and tap areas large enough for a finger. Hover effects simply do not trigger on touch screens; the buttons still work.