Buttons buttonsliderevealanimationaction

Slide Reveal Button

A dual-state button that slides primary text out and reveals hidden secondary action text or icons.

Preview

Usage

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

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

<SlideRevealButton label="Download File" revealText="Start (.zip)" variant="primary" />
--- import { SlideRevealButton } from 'astro-component-kit'; --- <SlideRevealButton label="Download File" revealText="Start (.zip)" variant="primary" />

Manual Installation

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

astro
---
/**
 * SlideRevealButton — Interactive action button with a sliding icon drawer and kinetic typography transition.
 * 
 * @param {'primary' | 'secondary' | 'accent'} variant - Visual tone variant. Default is 'primary'.
 * @param {'sm' | 'md' | 'lg'} size - Button size variant. Default is 'md'.
 * @param {string} href - Optional link destination.
 * @param {string} icon - SVG or character icon. Default is '→'.
 * @param {boolean} disabled - Whether the button is disabled.
 * @param {string} className - Additional CSS class names.
 */
interface Props {
  variant?: 'primary' | 'secondary' | 'accent';
  size?: 'sm' | 'md' | 'lg';
  href?: string;
  icon?: string;
  disabled?: boolean;
  className?: string;
}

const {
  variant = 'primary',
  size = 'md',
  href,
  icon = '→',
  disabled = false,
  className = '',
} = Astro.props;

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

<Tag
  href={href}
  disabled={!href && disabled ? true : undefined}
  aria-disabled={disabled ? 'true' : undefined}
  class:list={['slide-reveal-btn', `slide-reveal-btn--${variant}`, `slide-reveal-btn--${size}`, className]}
>
  <span class="slide-reveal-btn__text">
    <slot />
  </span>
  <span class="slide-reveal-btn__icon" aria-hidden="true">
    {icon}
  </span>
</Tag>

<style>
  .slide-reveal-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    font-family: var(--font-sans, system-ui, sans-serif);
    font-weight: 700;
    text-decoration: none;
    border-radius: var(--r-md, 12px);
    border: 1px solid rgba(255, 255, 255, 0.12);
    cursor: pointer;
    user-select: none;
    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
  }

  .slide-reveal-btn:focus-visible {
    outline: 2px solid var(--c-primary-light, #d4a017);
    outline-offset: 3px;
  }

  .slide-reveal-btn[disabled],
  .slide-reveal-btn[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
  }

  /* Variants */
  .slide-reveal-btn--primary {
    background: rgba(184, 134, 11, 0.15);
    color: var(--c-accent, #fef3c7);
    border-color: rgba(184, 134, 11, 0.35);
  }
  .slide-reveal-btn--primary:hover {
    background: linear-gradient(135deg, #b8860b, #d4a017);
    color: #050505;
    box-shadow: 0 8px 24px rgba(184, 134, 11, 0.3);
  }

  .slide-reveal-btn--secondary {
    background: rgba(255, 255, 255, 0.04);
    color: var(--c-text-1, #ffffff);
    backdrop-filter: blur(12px);
  }
  .slide-reveal-btn--secondary:hover {
    background: rgba(255, 255, 255, 0.1);
    border-color: rgba(255, 255, 255, 0.25);
  }

  .slide-reveal-btn--accent {
    background: rgba(56, 189, 248, 0.12);
    color: #7dd3fc;
    border-color: rgba(56, 189, 248, 0.3);
  }
  .slide-reveal-btn--accent:hover {
    background: #0284c7;
    color: #ffffff;
    box-shadow: 0 8px 24px rgba(56, 189, 248, 0.25);
  }

  /* Sizes */
  .slide-reveal-btn--sm {
    padding: 0.45rem 1.1rem;
    font-size: 0.8rem;
  }
  .slide-reveal-btn--sm .slide-reveal-btn__icon {
    font-size: 0.9rem;
  }

  .slide-reveal-btn--md {
    padding: 0.75rem 1.6rem;
    font-size: 0.92rem;
  }
  .slide-reveal-btn--md .slide-reveal-btn__icon {
    font-size: 1.05rem;
  }

  .slide-reveal-btn--lg {
    padding: 1rem 2.2rem;
    font-size: 1.05rem;
  }
  .slide-reveal-btn--lg .slide-reveal-btn__icon {
    font-size: 1.25rem;
  }

  /* Text & sliding icon */
  .slide-reveal-btn__text {
    display: inline-block;
    transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
  }

  .slide-reveal-btn__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    right: 1.2rem;
    opacity: 0;
    transform: translateX(-12px);
    transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.25s ease;
  }

  .slide-reveal-btn:hover .slide-reveal-btn__text {
    transform: translateX(-12px);
  }

  .slide-reveal-btn:hover .slide-reveal-btn__icon {
    opacity: 1;
    transform: translateX(0);
  }
</style>
--- /** * SlideRevealButton — Interactive action button with a sliding icon drawer and kinetic typography transition. * * @param {'primary' | 'secondary' | 'accent'} variant - Visual tone variant. Default is 'primary'. * @param {'sm' | 'md' | 'lg'} size - Button size variant. Default is 'md'. * @param {string} href - Optional link destination. * @param {string} icon - SVG or character icon. Default is '→'. * @param {boolean} disabled - Whether the button is disabled. * @param {string} className - Additional CSS class names. */ interface Props { variant?: 'primary' | 'secondary' | 'accent'; size?: 'sm' | 'md' | 'lg'; href?: string; icon?: string; disabled?: boolean; className?: string; } const { variant = 'primary', size = 'md', href, icon = '→', disabled = false, className = '', } = Astro.props; const Tag = href ? 'a' : 'button'; --- <Tag href={href} disabled={!href && disabled ? true : undefined} aria-disabled={disabled ? 'true' : undefined} class:list={['slide-reveal-btn', `slide-reveal-btn--${variant}`, `slide-reveal-btn--${size}`, className]} > <span class="slide-reveal-btn__text"> <slot /> </span> <span class="slide-reveal-btn__icon" aria-hidden="true"> {icon} </span> </Tag> <style> .slide-reveal-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; overflow: hidden; font-family: var(--font-sans, system-ui, sans-serif); font-weight: 700; text-decoration: none; border-radius: var(--r-md, 12px); border: 1px solid rgba(255, 255, 255, 0.12); cursor: pointer; user-select: none; transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } .slide-reveal-btn:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 3px; } .slide-reveal-btn[disabled], .slide-reveal-btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; pointer-events: none; } /* Variants */ .slide-reveal-btn--primary { background: rgba(184, 134, 11, 0.15); color: var(--c-accent, #fef3c7); border-color: rgba(184, 134, 11, 0.35); } .slide-reveal-btn--primary:hover { background: linear-gradient(135deg, #b8860b, #d4a017); color: #050505; box-shadow: 0 8px 24px rgba(184, 134, 11, 0.3); } .slide-reveal-btn--secondary { background: rgba(255, 255, 255, 0.04); color: var(--c-text-1, #ffffff); backdrop-filter: blur(12px); } .slide-reveal-btn--secondary:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.25); } .slide-reveal-btn--accent { background: rgba(56, 189, 248, 0.12); color: #7dd3fc; border-color: rgba(56, 189, 248, 0.3); } .slide-reveal-btn--accent:hover { background: #0284c7; color: #ffffff; box-shadow: 0 8px 24px rgba(56, 189, 248, 0.25); } /* Sizes */ .slide-reveal-btn--sm { padding: 0.45rem 1.1rem; font-size: 0.8rem; } .slide-reveal-btn--sm .slide-reveal-btn__icon { font-size: 0.9rem; } .slide-reveal-btn--md { padding: 0.75rem 1.6rem; font-size: 0.92rem; } .slide-reveal-btn--md .slide-reveal-btn__icon { font-size: 1.05rem; } .slide-reveal-btn--lg { padding: 1rem 2.2rem; font-size: 1.05rem; } .slide-reveal-btn--lg .slide-reveal-btn__icon { font-size: 1.25rem; } /* Text & sliding icon */ .slide-reveal-btn__text { display: inline-block; transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1); } .slide-reveal-btn__icon { display: inline-flex; align-items: center; justify-content: center; position: absolute; right: 1.2rem; opacity: 0; transform: translateX(-12px); transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.25s ease; } .slide-reveal-btn:hover .slide-reveal-btn__text { transform: translateX(-12px); } .slide-reveal-btn:hover .slide-reveal-btn__icon { opacity: 1; transform: translateX(0); } </style>

Quick Info

Category
Buttons
Filename
SlideRevealButton.astro
Dependencies
None — pure Astro + CSS
Tags
buttonsliderevealanimationaction