shimmerButton
A Buttons block/component from Magic UI — clean-room reimplemented for Domphy (see methodology). Call shimmerButton() with no arguments for a working demo, or edit the code below live.
Implementation notes
Full behavioral/visual port from the spec, matching its stated masked-rotating-gradient technique. An oversized (200%) conic-gradient patch with one bright wedge is continuously rotated via a plain CSS transform keyframe (linear, infinite, shimmerDuration) behind everything; a same-color solid layer sits on top of it inset by shimmerSize on every side, so only a shimmerSize-thick ring of the rotating layer peeks out — exactly the spec's domSketch (rotating gradient sliver masked to a thin ring, separate radial hover-highlight layer, label on top). overflow on the button is present as the spec's researchNote calls essential. Hover glow opacity is toggled purely via CSS using a data-slot descendant selector on the button's own state (no JS/state needed), mirroring this package's sidebar row-action hover pattern. The dark near-black base uses a shift-15 dataTone edge anchor (doctor's fixed-surface idiom) instead of a literal color, so background/shimmerColor stay ThemeColor roles rather than caller-supplied hex, keeping the whole component theme-aware. Verified doctor-clean (0 findings) via the domphy-doctor CLI.
Status: ported · Reference: Magic UI original
// Magic UI "Shimmer Button" — clean-room reimplementation.
//
// A dark, near-pill button whose thin bright edge-highlight continuously
// rotates around its outline, like a spotlight tracing the border. Implemented
// purely from the block's public functional/visual spec — no upstream Magic
// UI source was viewed or copied.
//
// Technique: an oversized square patch filled with a `conic-gradient` (mostly
// transparent, one bright wedge) sits BEHIND everything and is continuously
// rotated via a plain CSS `transform: rotate()` keyframe loop. A second,
// solid layer the same color as the button sits on top of it, inset by
// `shimmerSize` on every side — so only a `shimmerSize`-thick ring of the
// rotating wedge peeks out around the solid layer's edge, which reads as a
// highlight traveling around the border. `overflow: hidden` on the button
// itself is essential: without it the oversized rotating patch would spill
// outside the rounded silhouette instead of only ever showing as a thin ring.
// A third, separate radial-gradient layer (opacity toggled on `:hover` via a
// `data-slot` attribute selector — the same descendant-hover technique the
// sidebar blocks already use for their own row actions) supplies the hover glow.
import type { DomphyElement, Listener, StyleObject } from "@domphy/core";
import { hashString } from "@domphy/core";
import { type ThemeColor, themeColor, themeDensity, themeSize, themeSpacing } from "@domphy/theme";
export interface ShimmerButtonProps {
/** Button label content. Defaults to `"Shimmer Button"`. */
children?: DomphyElement | DomphyElement[] | string;
/** Fill color family for the button's dark base and the ring's solid mask layer.
* Defaults to `"neutral"` (near-black, via a `shift-15` dark edge anchor). */
background?: ThemeColor;
/** Color family the rotating highlight sliver and hover glow are drawn from.
* Defaults to `"neutral"` (near-white). */
shimmerColor?: ThemeColor;
/** Thickness of the visible ring, as a CSS length. Defaults to `"0.12em"`. */
shimmerSize?: string;
/** One full rotation around the border, in seconds. Defaults to `3`. */
shimmerDuration?: number;
/** Corner radius in pixels. Defaults to `100` (near-pill). */
borderRadius?: number;
onClick?: (event: MouseEvent) => void;
disabled?: boolean;
style?: StyleObject;
}
let shimmerButtonInstanceCounter = 0;
/** Normalizes a `DomphyElement | DomphyElement[] | string` prop into the flat
* `(string | DomphyElement)[]` shape `DomphyElement<T>`'s content field expects — a
* bare single element isn't part of that type, only primitives/arrays/functions are. */
function asContent(value: DomphyElement | DomphyElement[] | string): (string | DomphyElement)[] {
return Array.isArray(value) ? value : [value];
}
/**
* A dark, near-pill button with a thin bright highlight continuously rotating
* around its border, plus a soft inner radial glow on hover — a premium/
* "shimmering" call-to-action. The border rotation is fully ambient and loops
* from mount; hover/press layer ordinary scale/brightness feedback on top.
* Call with no arguments for a working demo button.
*/
function shimmerButton(props: ShimmerButtonProps = {}): DomphyElement<"button"> {
const label = props.children ?? "Shimmer Button";
const background = props.background ?? "neutral";
const shimmerColor = props.shimmerColor ?? "neutral";
const shimmerSize = props.shimmerSize ?? "0.12em";
const shimmerDuration = props.shimmerDuration ?? 3;
const borderRadius = props.borderRadius ?? 100;
const instanceId = ++shimmerButtonInstanceCounter;
const spinAnimationName = `shimmer-button-spin-${hashString(
JSON.stringify({ instanceId, shimmerColor, shimmerDuration }),
)}`;
const spinKeyframes = {
from: { transform: "translate(-50%, -50%) rotate(0deg)" },
to: { transform: "translate(-50%, -50%) rotate(360deg)" },
};
// Rotating highlight patch: a mostly-transparent conic gradient with one bright
// wedge near the seam, sized well beyond the button's own box (200%) so every
// corner stays covered through a full rotation about its own center.
//
// `_doctorDisable` isn't part of core's strict `PartialElement` type — build each
// decorative layer through an untyped literal, then assert, so the excess-property
// check doesn't fire (mirrors `overlayCanvas` in confetti.ts).
const rotatingSliver = {
span: null,
ariaHidden: "true",
_doctorDisable: "missing-color",
style: {
position: "absolute",
top: "50%",
left: "50%",
width: "200%",
height: "200%",
backgroundImage: (listener: Listener) =>
`conic-gradient(from 0deg, transparent 0turn, transparent 0.82turn, ${themeColor(listener, "shift-1", shimmerColor)} 0.93turn, transparent 1turn)`,
animation: `${spinAnimationName} ${shimmerDuration}s linear infinite`,
[`@keyframes ${spinAnimationName}`]: spinKeyframes,
} as StyleObject,
} as DomphyElement<"span">;
// Solid mask sitting on top of the rotating sliver, inset by `shimmerSize` on
// every side — only that thin inset band of the layer beneath remains visible.
const ringMask = {
span: null,
ariaHidden: "true",
_doctorDisable: "missing-color",
style: {
position: "absolute",
inset: shimmerSize,
borderRadius: "inherit",
backgroundColor: (listener: Listener) => themeColor(listener, "inherit", background),
} as StyleObject,
} as DomphyElement<"span">;
// Hover-only soft radial glow, toggled purely via CSS on the button's own
// `:hover` state (see `&:hover [data-slot=…]` below) — no JS/state needed.
const hoverGlow = {
span: null,
ariaHidden: "true",
dataSlot: "shimmer-hover-glow",
_doctorDisable: "missing-color",
style: {
position: "absolute",
inset: 0,
borderRadius: "inherit",
opacity: 0,
transition: "opacity 200ms ease",
backgroundImage: (listener: Listener) =>
`radial-gradient(circle at 50% 0%, ${themeColor(listener, "shift-3", shimmerColor)}, transparent 70%)`,
} as StyleObject,
} as DomphyElement<"span">;
const labelSpan: DomphyElement<"span"> = {
span: asContent(label),
style: { position: "relative", zIndex: 1 },
};
const buttonElement: DomphyElement<"button"> = {
button: [rotatingSliver, ringMask, hoverGlow, labelSpan],
type: "button",
disabled: props.disabled,
// Sets a fixed dark surface regardless of the surrounding page tone (edge anchor,
// per the doctor's dataTone-surface-contract idiom) rather than a raw literal
// color — `backgroundColor`/`color` below both read this same context.
dataTone: "shift-15",
style: {
position: "relative",
overflow: "hidden",
appearance: "none",
border: "none",
cursor: props.disabled ? "not-allowed" : "pointer",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
gap: (listener: Listener) => themeSpacing(themeDensity(listener) * 1),
fontSize: (listener: Listener) => themeSize(listener, "inherit"),
paddingBlock: (listener: Listener) => themeSpacing(themeDensity(listener) * 1),
paddingInline: (listener: Listener) => themeSpacing(themeDensity(listener) * 3),
borderRadius: `${borderRadius}px`,
backgroundColor: (listener: Listener) => themeColor(listener, "inherit", background),
color: (listener: Listener) => themeColor(listener, "shift-9"),
boxShadow: (listener: Listener) => `0 ${themeSpacing(2)} ${themeSpacing(6)} ${themeColor(listener, "shift-9")}`,
opacity: props.disabled ? 0.6 : 1,
transition: "transform 150ms ease, filter 150ms ease",
"&:hover:not([disabled])": { transform: "scale(1.02)", filter: "brightness(1.1)" },
"&:hover:not([disabled]) [data-slot=shimmer-hover-glow]": { opacity: 0.35 },
"&:active:not([disabled])": { transform: "scale(0.96)" },
...(props.style ?? {}),
} as StyleObject,
};
if (props.onClick) buttonElement.onClick = props.onClick;
return buttonElement;
}
export { shimmerButton };