{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sliding-play-button",
  "title": "Sliding Play Button",
  "description": "A circular media toggle built from three overlapping SVG elements that compress, slide, expand, and settle together with an optional duration override.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/base/ui/sliding-play-button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n  animate,\n  cubicBezier,\n  useMotionValue,\n  useReducedMotion,\n  type Transition,\n} from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\n// The icon is three overlapping pieces, not one path morphing between states:\n//\n//   1. the play triangle stays in the DOM and scales into the right pause bar;\n//   2. a narrow rounded rectangle starts at the triangle's left edge, then\n//      widens and travels right underneath it to become the right pause bar;\n//   3. a second rounded rectangle grows vertically in the space it leaves.\n//\n// Drawing the triangle last is deliberate. In a debug palette it remains visible\n// inside the right bar, like the reference; in production all three use\n// `currentColor`, so the overlap resolves into a clean pause glyph.\n\nconst ICON_VIEW_BOX = 36;\nconst ICON_CENTER = ICON_VIEW_BOX / 2;\nconst EPSILON = 1e-4;\n\ntype Point = readonly [number, number];\ntype Corner = { readonly point: Point; readonly radius: number };\ntype Shape = readonly Corner[];\n\nconst BAR_TOP = 4.5;\nconst BAR_BOTTOM = 31.5;\nconst BAR_WIDTH = 7;\nconst BAR_HEIGHT = BAR_BOTTOM - BAR_TOP;\nconst BAR_RADIUS = 2.5;\nconst LEFT_SLOT_X = 8.5;\nconst RIGHT_SLOT_X = 21.5;\nconst SHIFT_DISTANCE = RIGHT_SLOT_X - LEFT_SLOT_X;\n\nconst TRIANGLE_LEFT = 7.5;\nconst TRIANGLE_RIGHT = 33;\nconst TRIANGLE_TOP = 3;\nconst TRIANGLE_BOTTOM = 33;\n\nconst TRIANGLE_BODY: Shape = [\n  { point: [TRIANGLE_LEFT, TRIANGLE_TOP], radius: 3.93 },\n  { point: [TRIANGLE_LEFT, TRIANGLE_BOTTOM], radius: 3.93 },\n  { point: [17, 27.4], radius: 0 },\n  { point: [17, 8.6], radius: 0 },\n];\n\nconst TRIANGLE_TIP: Shape = [\n  { point: [17, 8.6], radius: 0 },\n  { point: [17, 27.4], radius: 0 },\n  { point: [TRIANGLE_RIGHT, ICON_CENTER], radius: 0 },\n  { point: [TRIANGLE_RIGHT, ICON_CENTER], radius: 0 },\n];\n\ntype Fillet = {\n  entry: Point;\n  control1: Point;\n  control2: Point;\n  exit: Point;\n};\n\nfunction arcControlFraction(interior: number) {\n  return (\n    1 - (4 / 3) * Math.tan((Math.PI - interior) / 4) * Math.tan(interior / 2)\n  );\n}\n\nfunction filletCorner(previous: Point, corner: Corner, next: Point): Fillet {\n  const [vx, vy] = corner.point;\n  const inX = previous[0] - vx;\n  const inY = previous[1] - vy;\n  const outX = next[0] - vx;\n  const outY = next[1] - vy;\n  const inLength = Math.hypot(inX, inY);\n  const outLength = Math.hypot(outX, outY);\n  const tangent = Math.min(corner.radius, inLength / 2, outLength / 2);\n  const degenerate: Fillet = {\n    entry: corner.point,\n    control1: corner.point,\n    control2: corner.point,\n    exit: corner.point,\n  };\n\n  if (tangent <= EPSILON) return degenerate;\n\n  const ux = inX / inLength;\n  const uy = inY / inLength;\n  const wx = outX / outLength;\n  const wy = outY / outLength;\n  const interior = Math.acos(Math.min(1, Math.max(-1, ux * wx + uy * wy)));\n\n  if (interior <= EPSILON || interior >= Math.PI - EPSILON) return degenerate;\n\n  const pull = tangent * arcControlFraction(interior);\n\n  return {\n    entry: [vx + ux * tangent, vy + uy * tangent],\n    control1: [vx + ux * pull, vy + uy * pull],\n    control2: [vx + wx * pull, vy + wy * pull],\n    exit: [vx + wx * tangent, vy + wy * tangent],\n  };\n}\n\nfunction round(value: number) {\n  return Math.round(value * 10000) / 10000;\n}\n\nfunction coords([x, y]: Point) {\n  return `${round(x)} ${round(y)}`;\n}\n\nfunction shapeToPath(shape: Shape) {\n  const count = shape.length;\n  const fillets = shape.map((corner, index) =>\n    filletCorner(\n      shape[(index + count - 1) % count].point,\n      corner,\n      shape[(index + 1) % count].point,\n    ),\n  );\n  let path = `M ${coords(fillets[count - 1].exit)}`;\n\n  for (const fillet of fillets) {\n    path += ` L ${coords(fillet.entry)} C ${coords(fillet.control1)} ${coords(\n      fillet.control2,\n    )} ${coords(fillet.exit)}`;\n  }\n\n  return `${path} Z`;\n}\n\nconst TRIANGLE_PATH = [TRIANGLE_BODY, TRIANGLE_TIP]\n  .map(shapeToPath)\n  .join(\" \");\n\n// Scaling the triangle around this offset origin maps its bounding box exactly\n// onto the right pause slot. It therefore visibly remains the same object while\n// it compresses and slides right, instead of being replaced by a new path.\nconst TRIANGLE_PAUSE_SCALE_X =\n  BAR_WIDTH / (TRIANGLE_RIGHT - TRIANGLE_LEFT);\nconst TRIANGLE_PAUSE_SCALE_Y =\n  BAR_HEIGHT / (TRIANGLE_BOTTOM - TRIANGLE_TOP);\nconst TRIANGLE_CENTER = (TRIANGLE_LEFT + TRIANGLE_RIGHT) / 2;\nconst RIGHT_SLOT_CENTER = RIGHT_SLOT_X + BAR_WIDTH / 2;\nconst TRIANGLE_ORIGIN_X =\n  (RIGHT_SLOT_CENTER - TRIANGLE_PAUSE_SCALE_X * TRIANGLE_CENTER) /\n  (1 - TRIANGLE_PAUSE_SCALE_X);\nconst TRIANGLE_ORIGIN = `${round(\n  (TRIANGLE_ORIGIN_X / ICON_VIEW_BOX) * 100,\n)}% 50%`;\nconst LEFT_BAR_ORIGIN = `${round(\n  ((LEFT_SLOT_X + BAR_WIDTH / 2) / ICON_VIEW_BOX) * 100,\n)}% 50%`;\nconst RIGHT_BAR_ORIGIN = `${round((RIGHT_SLOT_X / ICON_VIEW_BOX) * 100)}% 50%`;\n\n// The two bars share one physical timeline. During the approach, the right bar\n// travels from the left slot while the new left bar grows behind it. Their\n// transforms share one rebound rhythm and settle on the same final frame. The\n// left bar uses a slightly softer vertical overshoot than the moving right bar.\nconst HIDDEN_BAR_SCALE = 0.5;\nconst REBOUND_PEAK_SCALE = 1.25;\nconst LEFT_BAR_REBOUND_PEAK_SCALE = 1.2;\nconst REBOUND_PEAK_AT = 0.6;\nconst DEFAULT_MORPH_DURATION = 0.24;\nconst MASTER_EASE = cubicBezier(0.23, 1, 0.32, 1);\nconst SETTLE_EASE = cubicBezier(0.42, 0, 0.58, 1);\nconst MIN_MORPH_DURATION = 0.1;\n\nfunction clamp01(value: number) {\n  return Math.min(1, Math.max(0, value));\n}\n\nfunction subProgress(progress: number, [start, end]: readonly [number, number]) {\n  return clamp01((progress - start) / (end - start));\n}\n\nfunction lerp(from: number, to: number, progress: number) {\n  return from + (to - from) * progress;\n}\n\nfunction reboundProgress(progress: number) {\n  if (progress <= REBOUND_PEAK_AT) {\n    return (\n      REBOUND_PEAK_SCALE * MASTER_EASE(progress / REBOUND_PEAK_AT)\n    );\n  }\n\n  return lerp(\n    REBOUND_PEAK_SCALE,\n    1,\n    SETTLE_EASE((progress - REBOUND_PEAK_AT) / (1 - REBOUND_PEAK_AT)),\n  );\n}\n\nfunction reboundScale(\n  from: number,\n  progress: number,\n  peak = REBOUND_PEAK_SCALE,\n) {\n  if (progress <= REBOUND_PEAK_AT) {\n    return lerp(\n      from,\n      peak,\n      MASTER_EASE(progress / REBOUND_PEAK_AT),\n    );\n  }\n\n  return lerp(\n    peak,\n    1,\n    SETTLE_EASE((progress - REBOUND_PEAK_AT) / (1 - REBOUND_PEAK_AT)),\n  );\n}\n\nfunction scaleY(value: number) {\n  return `scaleY(${round(value)})`;\n}\n\nfunction translateXScaleX(x: number, scale: number) {\n  return `translateX(${round((x / ICON_VIEW_BOX) * 100)}%) scaleX(${round(scale)})`;\n}\n\nfunction triangleScale(x: number, y: number) {\n  return `scaleX(${round(x)}) scaleY(${round(y)})`;\n}\n\nexport type SlidingPlayPauseIconStyles = {\n  leftBar: { transform: string; opacity: number };\n  rightBar: { transform: string; opacity: number };\n  triangle: { transform: string; opacity: number };\n};\n\ntype SlidingPlayPauseIconValues = {\n  leftBar: { scaleY: number; opacity: number };\n  rightBar: { translateX: number; scaleX: number; opacity: number };\n  triangle: { scaleX: number; scaleY: number; opacity: number };\n};\n\nconst PLAY_ICON_VALUES: SlidingPlayPauseIconValues = {\n  leftBar: { scaleY: HIDDEN_BAR_SCALE, opacity: 0 },\n  rightBar: {\n    translateX: -SHIFT_DISTANCE,\n    scaleX: HIDDEN_BAR_SCALE,\n    opacity: 0,\n  },\n  triangle: { scaleX: 1, scaleY: 1, opacity: 1 },\n};\n\nconst PAUSE_ICON_VALUES: SlidingPlayPauseIconValues = {\n  leftBar: { scaleY: 1, opacity: 1 },\n  rightBar: { translateX: 0, scaleX: 1, opacity: 1 },\n  triangle: {\n    scaleX: TRIANGLE_PAUSE_SCALE_X,\n    scaleY: TRIANGLE_PAUSE_SCALE_Y,\n    opacity: 1,\n  },\n};\n\nfunction copyIconValues(\n  values: SlidingPlayPauseIconValues,\n): SlidingPlayPauseIconValues {\n  return {\n    leftBar: { ...values.leftBar },\n    rightBar: { ...values.rightBar },\n    triangle: { ...values.triangle },\n  };\n}\n\nfunction iconValuesToStyles(\n  values: SlidingPlayPauseIconValues,\n): SlidingPlayPauseIconStyles {\n  return {\n    leftBar: {\n      transform: scaleY(values.leftBar.scaleY),\n      opacity: round(values.leftBar.opacity),\n    },\n    rightBar: {\n      transform: translateXScaleX(\n        values.rightBar.translateX,\n        values.rightBar.scaleX,\n      ),\n      opacity: round(values.rightBar.opacity),\n    },\n    triangle: {\n      transform: triangleScale(\n        values.triangle.scaleX,\n        values.triangle.scaleY,\n      ),\n      opacity: round(values.triangle.opacity),\n    },\n  };\n}\n\nfunction transitionIconValues(\n  progress: number,\n  playing: boolean,\n  from: SlidingPlayPauseIconValues,\n): SlidingPlayPauseIconValues {\n  const elapsed = clamp01(progress);\n  const target = playing ? PAUSE_ICON_VALUES : PLAY_ICON_VALUES;\n\n  if (playing) {\n    const settle = reboundProgress(elapsed);\n    const visibility = MASTER_EASE(subProgress(elapsed, [0, 0.12]));\n\n    return {\n      leftBar: {\n        scaleY: reboundScale(\n          from.leftBar.scaleY,\n          elapsed,\n          LEFT_BAR_REBOUND_PEAK_SCALE,\n        ),\n        opacity: lerp(\n          from.leftBar.opacity,\n          target.leftBar.opacity,\n          visibility,\n        ),\n      },\n      rightBar: {\n        translateX: lerp(\n          from.rightBar.translateX,\n          target.rightBar.translateX,\n          settle,\n        ),\n        scaleX: reboundScale(from.rightBar.scaleX, elapsed),\n        opacity: lerp(\n          from.rightBar.opacity,\n          target.rightBar.opacity,\n          visibility,\n        ),\n      },\n      triangle: {\n        scaleX: lerp(from.triangle.scaleX, target.triangle.scaleX, settle),\n        scaleY: lerp(from.triangle.scaleY, target.triangle.scaleY, settle),\n        opacity: 1,\n      },\n    };\n  }\n\n  // Mirror the horizontal rebound on the way back to play: the right bar and\n  // triangle travel left past the play position, then settle right into it.\n  const settle = reboundProgress(elapsed);\n  const exit = MASTER_EASE(elapsed);\n\n  return {\n    leftBar: {\n      scaleY: lerp(from.leftBar.scaleY, target.leftBar.scaleY, exit),\n      opacity: lerp(from.leftBar.opacity, target.leftBar.opacity, exit),\n    },\n    rightBar: {\n      translateX: lerp(\n        from.rightBar.translateX,\n        target.rightBar.translateX,\n        settle,\n      ),\n      scaleX: lerp(from.rightBar.scaleX, target.rightBar.scaleX, settle),\n      opacity: lerp(from.rightBar.opacity, target.rightBar.opacity, exit),\n    },\n    triangle: {\n      scaleX: lerp(from.triangle.scaleX, target.triangle.scaleX, settle),\n      scaleY: lerp(from.triangle.scaleY, target.triangle.scaleY, settle),\n      opacity: 1,\n    },\n  };\n}\n\n/** Returns one full direction's styles, useful for inspection and testing. */\nexport function slidingPlayPauseIconTransitionStyles(\n  progress: number,\n  playing: boolean,\n): SlidingPlayPauseIconStyles {\n  const from = playing ? PLAY_ICON_VALUES : PAUSE_ICON_VALUES;\n\n  return iconValuesToStyles(transitionIconValues(progress, playing, from));\n}\n\n/** Backward-compatible shorthand for the play → pause direction. */\nexport function slidingPlayPauseIconStyles(\n  progress: number,\n): SlidingPlayPauseIconStyles {\n  return slidingPlayPauseIconTransitionStyles(progress, true);\n}\n\nfunction transitionDistance(\n  values: SlidingPlayPauseIconValues,\n  target: SlidingPlayPauseIconValues,\n) {\n  const triangleRange = 1 - TRIANGLE_PAUSE_SCALE_X;\n\n  return clamp01(\n    Math.max(\n      Math.abs(values.leftBar.opacity - target.leftBar.opacity),\n      Math.abs(values.rightBar.opacity - target.rightBar.opacity),\n      Math.abs(values.leftBar.scaleY - target.leftBar.scaleY) /\n        (1 - HIDDEN_BAR_SCALE),\n      Math.abs(values.rightBar.scaleX - target.rightBar.scaleX) /\n        (1 - HIDDEN_BAR_SCALE),\n      Math.abs(values.rightBar.translateX - target.rightBar.translateX) /\n        SHIFT_DISTANCE,\n      Math.abs(values.triangle.scaleX - target.triangle.scaleX) /\n        triangleRange,\n    ),\n  );\n}\n\nconst SURFACE_SIZE = 40;\nconst ICON_SIZE = 24;\nconst ICON_RATIO = `${(ICON_SIZE / SURFACE_SIZE) * 100}%`;\nconst useIsomorphicLayoutEffect =\n  typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect;\n\nexport type SlidingPlayButtonProps = Omit<\n  React.ComponentPropsWithoutRef<\"button\">,\n  \"aria-label\" | \"children\"\n> & {\n  /** Controlled playing state. */\n  playing?: boolean;\n  /** Initial playing state for uncontrolled usage. */\n  defaultPlaying?: boolean;\n  /** Called after the button requests a playing-state change. */\n  onPlayingChange?: (playing: boolean) => void;\n  /** Diameter in px, also published as `--sliding-play-button-size`. */\n  size?: number;\n  /**\n   * Overrides the full morph duration in seconds. The coordinated rebound\n   * choreography remains fixed; set `duration` to `0` for an instant change.\n   */\n  transition?: Pick<Transition, \"duration\">;\n  /** Accessible name while paused. */\n  playLabel?: string;\n  /** Accessible name while playing. */\n  pauseLabel?: string;\n  /** `ghost` sits on the page; `frosted` is the over-video glass. */\n  surface?: \"ghost\" | \"frosted\";\n};\n\nexport const SlidingPlayButton = React.forwardRef<\n  HTMLButtonElement,\n  SlidingPlayButtonProps\n>(function SlidingPlayButton(\n  {\n    playing,\n    defaultPlaying = false,\n    onPlayingChange,\n    size = SURFACE_SIZE,\n    transition,\n    playLabel = \"Play\",\n    pauseLabel = \"Pause\",\n    surface = \"ghost\",\n    className,\n    style,\n    type = \"button\",\n    disabled,\n    onClick,\n    ...props\n  },\n  ref,\n) {\n  const [internalPlaying, setInternalPlaying] = React.useState(defaultPlaying);\n  const leftBarRef = React.useRef<SVGRectElement | null>(null);\n  const rightBarRef = React.useRef<SVGRectElement | null>(null);\n  const triangleRef = React.useRef<SVGPathElement | null>(null);\n  const shouldReduceMotion = useReducedMotion();\n  const controlled = playing !== undefined;\n  const isPlaying = controlled ? playing : internalPlaying;\n  const progress = useMotionValue(1);\n  const instantTarget = React.useRef<boolean | null>(null);\n  const currentValues = React.useRef<SlidingPlayPauseIconValues | undefined>(\n    undefined,\n  );\n  currentValues.current ??= copyIconValues(\n    isPlaying ? PAUSE_ICON_VALUES : PLAY_ICON_VALUES,\n  );\n  const activeTransition = React.useRef<{\n    from: SlidingPlayPauseIconValues;\n    playing: boolean;\n  } | null>(null);\n  const initialStyles = React.useRef<SlidingPlayPauseIconStyles | undefined>(\n    undefined,\n  );\n  initialStyles.current ??= iconValuesToStyles(currentValues.current);\n  const requestedDuration = transition?.duration;\n  const morphDuration =\n    typeof requestedDuration === \"number\" && Number.isFinite(requestedDuration)\n      ? Math.max(0, requestedDuration)\n      : DEFAULT_MORPH_DURATION;\n\n  const applyIconValues = React.useCallback(\n    (values: SlidingPlayPauseIconValues) => {\n      const layers = iconValuesToStyles(values);\n\n      if (leftBarRef.current) {\n        leftBarRef.current.style.transform = layers.leftBar.transform;\n        leftBarRef.current.style.opacity = String(layers.leftBar.opacity);\n      }\n\n      if (rightBarRef.current) {\n        rightBarRef.current.style.transform = layers.rightBar.transform;\n        rightBarRef.current.style.opacity = String(layers.rightBar.opacity);\n      }\n\n      if (triangleRef.current) {\n        triangleRef.current.style.transform = layers.triangle.transform;\n      }\n    },\n    [],\n  );\n\n  useIsomorphicLayoutEffect(\n    () =>\n      progress.on(\"change\", (value) => {\n        const transition = activeTransition.current;\n        if (!transition) return;\n\n        const values = transitionIconValues(\n          value,\n          transition.playing,\n          transition.from,\n        );\n        currentValues.current = values;\n        applyIconValues(values);\n      }),\n    [applyIconValues, progress],\n  );\n\n  useIsomorphicLayoutEffect(() => {\n    const target = isPlaying ? PAUSE_ICON_VALUES : PLAY_ICON_VALUES;\n    const from =\n      currentValues.current ??\n      copyIconValues(isPlaying ? PLAY_ICON_VALUES : PAUSE_ICON_VALUES);\n\n    const shouldChangeInstantly =\n      shouldReduceMotion ||\n      morphDuration === 0 ||\n      instantTarget.current === isPlaying;\n\n    if (instantTarget.current === isPlaying) {\n      instantTarget.current = null;\n    }\n\n    if (shouldChangeInstantly) {\n      activeTransition.current = null;\n      currentValues.current = copyIconValues(target);\n      applyIconValues(target);\n      progress.set(1);\n      return;\n    }\n\n    const remaining = transitionDistance(from, target);\n\n    if (remaining <= EPSILON) {\n      activeTransition.current = null;\n      currentValues.current = copyIconValues(target);\n      applyIconValues(target);\n      return;\n    }\n\n    activeTransition.current = {\n      from: copyIconValues(from),\n      playing: isPlaying,\n    };\n    progress.set(0);\n\n    const controls = animate(progress, 1, {\n      duration: Math.max(\n        Math.min(MIN_MORPH_DURATION, morphDuration),\n        morphDuration * remaining,\n      ),\n      ease: \"linear\",\n    });\n\n    return () => controls.stop();\n  }, [applyIconValues, isPlaying, morphDuration, progress, shouldReduceMotion]);\n\n  const handleClick = React.useCallback(\n    (event: React.MouseEvent<HTMLButtonElement>) => {\n      onClick?.(event);\n\n      if (event.defaultPrevented || disabled) return;\n\n      const nextPlaying = !isPlaying;\n      // Native keyboard and assistive-technology clicks have no pointer detail.\n      // Keep those high-frequency interactions immediate instead of making the\n      // user wait for the decorative transform sequence.\n      instantTarget.current = event.detail === 0 ? nextPlaying : null;\n\n      if (!controlled) {\n        setInternalPlaying(nextPlaying);\n      }\n\n      onPlayingChange?.(nextPlaying);\n    },\n    [controlled, disabled, isPlaying, onClick, onPlayingChange],\n  );\n\n  const frosted = surface === \"frosted\";\n\n  return (\n    <button\n      ref={ref}\n      type={type}\n      disabled={disabled}\n      onClick={handleClick}\n      aria-label={isPlaying ? pauseLabel : playLabel}\n      data-slot=\"sliding-play-button\"\n      data-state={isPlaying ? \"playing\" : \"paused\"}\n      style={{\n        [\"--sliding-play-button-size\" as string]: `${size}px`,\n        width: \"var(--sliding-play-button-size)\",\n        height: \"var(--sliding-play-button-size)\",\n        ...style,\n      }}\n      className={cn(\n        \"relative inline-grid shrink-0 place-items-center rounded-full outline-none\",\n        \"transition-[background-color,transform] duration-100 ease-[cubic-bezier(0.23,1,0.32,1)]\",\n        \"active:scale-[0.97] disabled:pointer-events-none disabled:opacity-50\",\n        \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current\",\n        \"motion-reduce:transition-colors motion-reduce:active:scale-100\",\n        frosted\n          ? \"bg-ericts-media-control/30 text-ericts-media-control-foreground backdrop-blur-[16px] hover:bg-ericts-media-control/45\"\n          : \"hover:bg-muted hover:text-foreground\",\n        className,\n      )}\n      {...props}\n    >\n      <svg\n        aria-hidden\n        viewBox={`0 0 ${ICON_VIEW_BOX} ${ICON_VIEW_BOX}`}\n        data-slot=\"sliding-play-button-icon\"\n        style={{ width: ICON_RATIO, height: ICON_RATIO }}\n        className=\"fill-current\"\n      >\n        <rect\n          ref={leftBarRef}\n          data-slot=\"sliding-play-button-left-bar\"\n          className=\"motion-reduce:transition-opacity motion-reduce:duration-100 motion-reduce:ease-[cubic-bezier(0.23,1,0.32,1)]\"\n          x={LEFT_SLOT_X}\n          y={BAR_TOP}\n          width={BAR_WIDTH}\n          height={BAR_HEIGHT}\n          rx={BAR_RADIUS}\n          style={{\n            opacity: initialStyles.current.leftBar.opacity,\n            transform: initialStyles.current.leftBar.transform,\n            transformBox: \"view-box\",\n            transformOrigin: LEFT_BAR_ORIGIN,\n            willChange: \"transform, opacity\",\n          }}\n        />\n        <rect\n          ref={rightBarRef}\n          data-slot=\"sliding-play-button-right-bar\"\n          className=\"motion-reduce:transition-opacity motion-reduce:duration-100 motion-reduce:ease-[cubic-bezier(0.23,1,0.32,1)]\"\n          x={RIGHT_SLOT_X}\n          y={BAR_TOP}\n          width={BAR_WIDTH}\n          height={BAR_HEIGHT}\n          rx={BAR_RADIUS}\n          style={{\n            opacity: initialStyles.current.rightBar.opacity,\n            transform: initialStyles.current.rightBar.transform,\n            transformBox: \"view-box\",\n            transformOrigin: RIGHT_BAR_ORIGIN,\n            willChange: \"transform, opacity\",\n          }}\n        />\n        <path\n          ref={triangleRef}\n          data-slot=\"sliding-play-button-triangle\"\n          d={TRIANGLE_PATH}\n          style={{\n            opacity: initialStyles.current.triangle.opacity,\n            transform: initialStyles.current.triangle.transform,\n            transformBox: \"view-box\",\n            transformOrigin: TRIANGLE_ORIGIN,\n            willChange: \"transform\",\n          }}\n        />\n      </svg>\n    </button>\n  );\n});\n",
      "type": "registry:ui",
      "target": "@ui/sliding-play-button.tsx"
    }
  ],
  "cssVars": {
    "light": {
      "ericts-media-control": "oklch(0 0 0)",
      "ericts-media-control-foreground": "oklch(1 0 0)"
    },
    "dark": {
      "ericts-media-control": "oklch(0 0 0)",
      "ericts-media-control-foreground": "oklch(1 0 0)"
    }
  },
  "meta": {
    "tags": [
      "button",
      "toggle",
      "play",
      "pause",
      "media",
      "video",
      "controlled"
    ],
    "effects": [
      "svg-overlap",
      "svg-animation",
      "coordinated-translation",
      "transform-scale",
      "offset-transform-origin",
      "elastic-settle",
      "ease-out-rebound",
      "frosted-glass",
      "press-scale",
      "reduced-motion"
    ]
  },
  "categories": [
    "action"
  ],
  "type": "registry:ui"
}