Effect buttons
BetaA shimmer button, a shiny announcement pill and a glowing border, from the launch-page screens.
Three pieces from the Magic UI and Aceternity style screens, rebuilt in plain CSS. ShimmerButton runs a beam of light once round its 1px border; ShinyPill sweeps a shine across its words twice, then rests; fx-hover-border is a class for a pill link with a glow at its base. Use one per page, for the moment that matters.
Updated View source
On a launch page
import { ShimmerButton, ShinyPill } from '@rchursin/screens/magic-ui/parts'
/** * The three effects as they sit on a launch page: an announcement pill, a shimmer button and a * pill with a glowing border. Each light runs once on arrival (and the beam again on hover); * nothing moves with reduced motion. These links jump to sections of this page. */export default function EffectButtonsShowcase() { return ( <div className="flex flex-col items-center gap-7 text-center"> <ShinyPill href="#examples">See each effect on its own</ShinyPill> <div className="flex flex-wrap items-center justify-center gap-4"> <ShimmerButton href="#usage">Get started</ShimmerButton> <a href="#api" className="fx-hover-border flex focus-ring transition-transform active:scale-[0.975]"> <span className="flex h-10 items-center rounded-full bg-surface px-5.5 text-sm font-medium text-fg">Read the API</span> </a> </div> </div> )}Usage
import { ShimmerButton, ShinyPill } from '@rchursin/screens/magic-ui/parts'<ShinyPill href="#changelog">Version 2.4 is out</ShinyPill><ShimmerButton href="#start">Get started</ShimmerButton>Examples
As a submit button
With no onClick, the button form of ShimmerButton is a form’s submit button. This form only shows a toast.
import { useState, type FormEvent } from 'react'import { Field, TextInput, useToast } from '@rchursin/product/ui'import { ShimmerButton } from '@rchursin/screens/magic-ui/parts'
/** * ShimmerButton as a form's submit button (type="submit"); it also takes onClick and the other * <button> attributes. Pass the outer radius in className and one pixel less in innerClassName * for a squarer shape. */export default function ShimmerSubmit() { const toast = useToast() const [email, setEmail] = useState('')
function join(e: FormEvent<HTMLFormElement>) { e.preventDefault() toast({ title: 'You are on the list', description: 'Demo only: nothing was sent.', icon: 'check' }) setEmail('') }
return ( <form onSubmit={join} className="flex w-full max-w-sm flex-col gap-4"> <Field label="Work email" hint="One email when early access opens."> {(p) => <TextInput {...p} type="email" required autoComplete="email" value={email} onChange={(e) => setEmail(e.target.value)} />} </Field> <ShimmerButton type="submit" className="rounded-[10px]" innerClassName="rounded-[9px]"> Join the waitlist </ShimmerButton> </form> )}API
ShimmerButton props
| Prop | Type | Default | Description |
|---|---|---|---|
childrenrequired | ReactNode | – | The label. White on a near-black face in both themes. |
href | string | – | Renders an <a> instead of a <button>. |
type | 'button' | 'submit' | 'button' | For the <button> form: "submit" inside a form, or "button" with an onClick. |
size | 'sm' | 'md' | 'md' | sm is 36 pixels tall overall, md 42 (34 and 40 plus the 1px border). |
className | string | 'rounded-full' | Outer classes. Pass the radius here; it replaces the default rounded-full. |
innerClassName | string | 'rounded-full' | The face’s radius, 1 pixel smaller than the outer one. |
…rest | ButtonHTMLAttributes<HTMLButtonElement> | – | Any other <button> attribute: onClick, disabled, aria-*, data-*. With href they go on the <a> instead. |
ShinyPill props
| Prop | Type | Default | Description |
|---|---|---|---|
childrenrequired | ReactNode | – | One short line of news. An arrow follows it. |
href | string | '#' | Where the news is. |
CSS
| Class or variable | What it does |
|---|---|
fx-hover-border | A 1px pill ring that glows at the bottom centre. Put it on the outer <a> or <button> and the face on an inner element; bring your own focus style (the example adds focus-ring). |
Accessibility
Keyboard
| Key | What it does |
|---|---|
| Enter | Follows the link, or submits the form when ShimmerButton is a submit button. |
| Space | Presses ShimmerButton in its <button> form. |
- The beam and the shine are decoration: the beam is aria-hidden, and both stop entirely when the visitor asks for reduced motion. Neither loops on its own.
- ShimmerButton’s focus outline is 2px in the theme’s focus colour (--lux-focus: near-black on a light page, near-white on a dark one), 2px outside the edge, so it stands out far beyond the 3:1 a focus indicator needs.
- ShinyPill’s words are a grey gradient clipped to the letters. Resting, they are #6b6b6b (about 4.9:1 on the pill in light mode) and #9c9c9c in dark mode (over 6:1), so the small text keeps 4.5:1. Keep the news short.
- ShinyPill has no focus style of its own and takes no className, so it shows the browser’s default focus outline. fx-hover-border brings none either: add focus-ring to the element that carries it, as the example does.
Guidelines
Do
- Use one effect per screen, on the main call to action or the one piece of news.
- Repeat the pill’s news in plain text nearby.
Don’t
- Don’t put effect buttons in app UI or forms people fill in every day.
- Don’t stack a shimmer button next to a lit-metal primary button; pick one.
In use
Related
- Button linkA link cut from the same lit metal as Button, for calls to action that go somewhere.
- Animated accentsShiny and gradient text, a beam round a border, a shine border, ripples, lines that type in and a scanning column.
- Magic UI screensFive screens with Magic UI’s effects: retro grid, border beam, shiny and gradient text, shimmer buttons, ripples and a dock.
Source
The component’s own files, exactly as they run on this site.
/** * Magic UI-style building blocks, recreated with plain CSS from ../../styles/effects.css. * If you install the real Magic UI components (each page on magicui.design has the shadcn CLI * install command), you can swap these for <ShimmerButton>, <BorderBeam>, <AnimatedShinyText>. */import type { AnchorHTMLAttributes, ButtonHTMLAttributes, ReactNode } from "react"import { cx } from "../../components/cx"import { ArrowRightIcon } from "../../components/icons"
export interface ShimmerButtonProps extends Omit< ButtonHTMLAttributes<HTMLButtonElement>, "type" | "className" | "children"> { children: ReactNode /** Renders an <a> instead of a <button> */ href?: string type?: "button" | "submit" size?: "sm" | "md" /** Outer classes — pass the radius here (default rounded-full) */ className?: string