Buttons buttonanimatedattention

Pulse Button

A button with a persistent circular pulse effect to draw attention.

Preview

Usage

Copy the full block below to use this component with its imports.

astro
---
import { PulseButton } from 'astro-component-kit';
---

<PulseButton 
  color="#6366f1" 
  variant="primary" 
  size="lg"
>
  Start Process
</PulseButton>
--- import { PulseButton } from 'astro-component-kit'; --- <PulseButton color="#6366f1" variant="primary" size="lg" > Start Process </PulseButton>

Manual Installation

If you are not using the npm package, create a new file src/components/lib/PulseButton.astro and paste the following code:

astro
---
interface Props {
  href?: string;
  type?: 'button' | 'submit' | 'reset';
  variant?: 'primary' | 'secondary' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  color?: string;
  disabled?: boolean;
}

const { 
  href, 
  type = 'button', 
  variant = 'primary', 
  size = 'md',
  color = '#8b5cf6',
  disabled = false 
} = Astro.props;

const Tag = href ? 'a' : 'button';
---

<Tag 
  href={href} 
  type={href ? undefined : type}
  disabled={disabled}
  class={`pulse-btn pulse-btn--${variant} pulse-btn--${size}`}
  style={`--pulse-color: ${color}`}
>
  <slot />
</Tag>

<style>
  .pulse-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.7rem 1.6rem;
    background: var(--pulse-color);
    color: white;
    border: none;
    border-radius: var(--r-full, 9999px);
    cursor: pointer;
    text-decoration: none;
    font-weight: 600;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    user-select: none;
    white-space: nowrap;
    font-family: var(--font-sans, inherit);
    isolation: isolate;
    box-shadow: 0 10px 26px -18px color-mix(in srgb, var(--pulse-color), black 10%);
  }

  .pulse-btn::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    box-shadow: 0 0 0 0 var(--pulse-color);
    animation: pulse-ring 2s infinite;
    z-index: -1;
    pointer-events: none;
  }

  @keyframes pulse-ring {
    0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--pulse-color), transparent 30%); }
    70% { box-shadow: 0 0 0 15px color-mix(in srgb, var(--pulse-color), transparent 100%); }
    100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--pulse-color), transparent 100%); }
  }

  .pulse-btn:hover:not(:disabled) {
    transform: scale(1.05);
    filter: brightness(110%);
  }

  .pulse-btn:active:not(:disabled) {
    transform: scale(0.98);
  }

  .pulse-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    animation: none;
    filter: grayscale(1);
  }

  .pulse-btn:disabled::after {
    animation: none;
    box-shadow: none;
  }

  /* Variants */
  .pulse-btn--secondary {
    background: #0f172a;
    border: 1px solid rgba(255, 255, 255, 0.1);
    box-shadow: 0 10px 26px -20px rgba(15, 23, 42, 0.95);
  }

  .pulse-btn--ghost {
    background: transparent;
    border: 1px solid var(--pulse-color);
    color: var(--pulse-color);
    box-shadow: none;
  }

  /* Sizes */
  .pulse-btn--sm { padding: 0.4rem 1.1rem; font-size: 0.8rem; }
  .pulse-btn--md { padding: 0.7rem 1.6rem; font-size: 0.95rem; }
  .pulse-btn--lg { padding: 1rem 2.2rem; font-size: 1.1rem; }
</style>
--- interface Props { href?: string; type?: 'button' | 'submit' | 'reset'; variant?: 'primary' | 'secondary' | 'ghost'; size?: 'sm' | 'md' | 'lg'; color?: string; disabled?: boolean; } const { href, type = 'button', variant = 'primary', size = 'md', color = '#8b5cf6', disabled = false } = Astro.props; const Tag = href ? 'a' : 'button'; --- <Tag href={href} type={href ? undefined : type} disabled={disabled} class={`pulse-btn pulse-btn--${variant} pulse-btn--${size}`} style={`--pulse-color: ${color}`} > <slot /> </Tag> <style> .pulse-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; padding: 0.7rem 1.6rem; background: var(--pulse-color); color: white; border: none; border-radius: var(--r-full, 9999px); cursor: pointer; text-decoration: none; font-weight: 600; transition: transform 0.2s ease, box-shadow 0.2s ease; user-select: none; white-space: nowrap; font-family: var(--font-sans, inherit); isolation: isolate; box-shadow: 0 10px 26px -18px color-mix(in srgb, var(--pulse-color), black 10%); } .pulse-btn::after { content: ''; position: absolute; inset: 0; border-radius: inherit; box-shadow: 0 0 0 0 var(--pulse-color); animation: pulse-ring 2s infinite; z-index: -1; pointer-events: none; } @keyframes pulse-ring { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--pulse-color), transparent 30%); } 70% { box-shadow: 0 0 0 15px color-mix(in srgb, var(--pulse-color), transparent 100%); } 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--pulse-color), transparent 100%); } } .pulse-btn:hover:not(:disabled) { transform: scale(1.05); filter: brightness(110%); } .pulse-btn:active:not(:disabled) { transform: scale(0.98); } .pulse-btn:disabled { opacity: 0.5; cursor: not-allowed; animation: none; filter: grayscale(1); } .pulse-btn:disabled::after { animation: none; box-shadow: none; } /* Variants */ .pulse-btn--secondary { background: #0f172a; border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: 0 10px 26px -20px rgba(15, 23, 42, 0.95); } .pulse-btn--ghost { background: transparent; border: 1px solid var(--pulse-color); color: var(--pulse-color); box-shadow: none; } /* Sizes */ .pulse-btn--sm { padding: 0.4rem 1.1rem; font-size: 0.8rem; } .pulse-btn--md { padding: 0.7rem 1.6rem; font-size: 0.95rem; } .pulse-btn--lg { padding: 1rem 2.2rem; font-size: 1.1rem; } </style>

Quick Info

Category
Buttons
Filename
PulseButton.astro
Dependencies
None — pure Astro + CSS
Tags
buttonanimatedattention