Domphy

shinyButton

A Community block/component from Magic UI — clean-room reimplemented for Domphy (see methodology). Call shinyButton() with no arguments for a working demo, or edit the code below live.

Implementation notes

Pill button with a continuous (not hover-gated) diagonal shimmer, built as a single <button> whose own layered background (solid dataTone-anchored surface + a color-mix() gradient streak swept via background-position keyframe) produces the sheen, matching the domSketch's 'no separate overlay needed' guidance. Duration/shimmer-width are both JS props AND mirrored into --shiny-button-duration/--shiny-button-shimmer-width CSS custom properties per the spec's own suggestion. Hover adds scale+brightness; disabled dims the button, pauses the shimmer (a judgment call — the spec doesn't address the disabled+loop interaction explicitly) and sets the native disabled attribute. Exact shimmer color/duration aren't published upstream (spec's own researchNote flags this), so timing (3000ms) and streak width (35%) mirror this package's already-ported animatedShinyText/glareHover techniques.

Status: ported · Reference: Magic UI original

// Magic UI "Shiny Button" — clean-room reimplementation.
//
// A pill-shaped button whose surface is continuously swept, on an endless
// loop, by a soft diagonal light streak — a glossy/metallic sheen rather than
// a hard highlight. Implemented purely from the block's public
// functional/visual spec — no upstream Magic UI source was viewed or copied.
//
// The upstream docs page only documents `className`/`children` — duration and
// streak width aren't published, so this mirrors the sibling "shiny text"
// shimmer technique already ported in this package (background-position
// keyframe over a few seconds, infinite loop) and exposes both as CSS custom
// properties (with sane JS-prop defaults) so a caller can retune them without
// a new prop surface, per the spec's own suggestion.

import type { DomphyElement, Listener, StyleObject } from "@domphy/core";
import { hashString } from "@domphy/core";
import { themeColor, themeDensity, themeSpacing } from "@domphy/theme";

export interface ShinyButtonProps {
  /** Label content. Plain text or a full element (e.g. text + icon). Defaults to `"Shiny Button"`. */
  children?: string | DomphyElement | DomphyElement[];
  /** Click handler. */
  onClick?: (event: MouseEvent) => void;
  /** Disables the button (dims it and stops the pointer cursor; the shimmer keeps looping). */
  disabled?: boolean;
  /** One full shimmer sweep, in ms. Also exposed as `--shiny-button-duration` for CSS-side tuning. Defaults to `3000`. */
  duration?: number;
  /** Streak band width, as a percent of the button's own box. Also exposed as `--shiny-button-shimmer-width`. Defaults to `35`. */
  shimmerWidth?: number;
  /** Passthrough style merged onto the button. */
  style?: StyleObject;
}

let shinyButtonInstanceCounter = 0;

/**
 * A fully-rounded pill button with a continuous, non-hover-gated diagonal
 * light shimmer sweeping across its surface. Hover adds a mild scale/
 * brightness bump; click behaves like an ordinary button. Call with no
 * arguments for a working demo.
 */
function shinyButton(props: ShinyButtonProps = {}): DomphyElement<"button"> {
  const label = props.children ?? "Shiny Button";
  const disabled = props.disabled ?? false;
  const duration = props.duration ?? 3000;
  const shimmerWidth = props.shimmerWidth ?? 35;

  const instanceId = ++shinyButtonInstanceCounter;
  const halfBand = shimmerWidth / 2;
  const keyframes = {
    from: { backgroundPosition: "-150% 0" },
    to: { backgroundPosition: "150% 0" },
  };
  const animationName = `shiny-button-sweep-${hashString(
    JSON.stringify({ instanceId, shimmerWidth }),
  )}`;

  const labelChildren: DomphyElement[] =
    typeof label === "string"
      ? [{ span: label } as DomphyElement]
      : Array.isArray(label)
        ? label
        : [label];

  const buttonElement: DomphyElement<"button"> = {
    button: labelChildren,
    type: "button",
    disabled,
    // `dataTone` anchors a self-contained edge surface (light-in-light-theme,
    // dark-in-dark-theme per the spec) — `inherit` then paints exactly that
    // context instead of a hardcoded shift, per the doctor's tone contract.
    dataTone: "shift-1",
    style: {
      position: "relative",
      appearance: "none",
      border: "none",
      cursor: disabled ? "not-allowed" : "pointer",
      display: "inline-flex",
      alignItems: "center",
      justifyContent: "center",
      width: "fit-content",
      userSelect: "none",
      borderRadius: themeSpacing(999),
      paddingBlock: (listener: Listener) => themeSpacing(themeDensity(listener) * 1),
      paddingInline: (listener: Listener) => themeSpacing(themeDensity(listener) * 4),
      color: (listener: Listener) => themeColor(listener, "shift-9", "neutral"),
      backgroundColor: (listener: Listener) => themeColor(listener, "inherit", "neutral"),
      // The streak: a transparent-flanked band swept via `background-position`
      // over an oversized `background-size`, layered on top of the solid
      // `backgroundColor` above (same technique as `animatedShinyText`/
      // `glareHover`). `shift-11` (not a small shift) so it reads as a
      // distinguishable highlight against the button's own near-edge surface.
      backgroundImage: (listener: Listener) =>
        `linear-gradient(105deg, transparent ${50 - halfBand - 10}%, color-mix(in srgb, ${themeColor(listener, "shift-11", "neutral")} 55%, transparent) 50%, transparent ${50 + halfBand + 10}%)`,
      backgroundSize: "220% 100%",
      backgroundRepeat: "no-repeat",
      outline: (listener: Listener) => `1px solid ${themeColor(listener, "shift-4", "neutral")}`,
      outlineOffset: "-1px",
      opacity: disabled ? 0.6 : 1,
      transition: "transform 150ms ease, filter 150ms ease, opacity 150ms ease",
      animation: `${animationName} var(--shiny-button-duration, ${duration}ms) linear infinite`,
      "--shiny-button-duration": `${duration}ms`,
      "--shiny-button-shimmer-width": `${shimmerWidth}%`,
      [`@keyframes ${animationName}`]: keyframes,
      "&:hover:not([disabled])": {
        transform: "scale(1.02)",
        filter: "brightness(1.06)",
      },
      "&:focus-visible": {
        boxShadow: (listener: Listener) =>
          `0 0 0 ${themeSpacing(0.5)} ${themeColor(listener, "shift-6", "primary")}`,
      },
      "&[disabled]": {
        cursor: "not-allowed",
        animationPlayState: "paused",
      },
      ...(props.style ?? {}),
    } as StyleObject,
  };

  if (props.onClick) buttonElement.onClick = props.onClick;

  return buttonElement;
}

export { shinyButton };

← Back to Magic UI catalog