# Gradient Glow (/ui/components/react/gradient-glow)



<Playground component="gradient-glow" />

## Usage [#usage]

```tsx
import { GradientGlow } from '@appica/ui-react/gradient-glow'
```

```tsx
<GradientGlow>
  <YourContent />
</GradientGlow>
```

`GradientGlow` is a `<div>` wrapper that paints a blurred, animated gradient behind its `children` —
the colored "halo" you see around AI features. The gradient slowly rotates a full turn while the glow
gently breathes in and out, so the colors drift around the element for a living, glowing feel. Tune the look with `from` / `via` / `to` (the three
gradient stops), `angle` (its resting direction), `blur` (how soft the halo is), and `speed` (seconds per
rotation).

The glow extends past the element's edges because the layer is blurred — give your `children` an opaque
surface (e.g. `bg-background`) so the halo reads as a surrounding shadow rather than washing over the
content. The decorative layers are `aria-hidden` and `pointer-events-none`, so they never reach assistive
tech or intercept clicks, and the wrapper forwards `className`, `style`, and any native `<div>` attributes.

## Examples [#examples]

### Basic glow [#basic-glow]

The default `aurora` gradient — sky blue, lilac, and warm sand — drifting behind a card. By default only
the blurred glow is rendered; the wrapper's radius (`rounded-2xl`) should match your content's radius so
the halo hugs the corners.

```tsx
import { GradientGlow } from '@appica/ui-react/gradient-glow'

export default function GradientGlowBasic() {
  return (
    <GradientGlow className="w-full max-w-80">
      <div className="bg-background text-foreground-intense flex min-h-32 w-full items-center justify-center rounded-2xl px-6 text-center text-sm font-medium">
        How can I assist you today?
      </div>
    </GradientGlow>
  )
}
```

### Animated gradient border [#animated-gradient-border]

Set `border` to add a 1px stroke that follows the same animated gradient, in sync with the glow. Tune its
thickness with `borderWidth`. Pair it with a softer, wider `blur` for a crisp edge over a diffuse halo.

```tsx
import { GradientGlow } from '@appica/ui-react/gradient-glow'

export default function GradientGlowBorder() {
  return (
    <GradientGlow border blur="xl" className="w-full max-w-80">
      <div className="bg-background text-foreground-intense flex min-h-32 w-full items-center justify-center rounded-2xl px-6 text-center text-sm font-medium">
        Smart comparison
      </div>
    </GradientGlow>
  )
}
```

### Custom colors [#custom-colors]

The three stops are fully adjustable via `from`, `via`, and `to` — pass any CSS color. Mix your own
palette to match a brand or mood.

```tsx
import { GradientGlow } from '@appica/ui-react/gradient-glow'

const PALETTES = [
  { label: 'Sunset', from: '#FCA5A5', via: '#FDC49B', to: '#FCE3A6' },
  { label: 'Ocean', from: '#9DE7D8', via: '#9CC0F9', to: '#BEB0F7' },
  { label: 'Candy', from: '#F0ABFC', via: '#C4B5FD', to: '#93C5FD' },
]

export default function GradientGlowCustomColors() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-8">
      {PALETTES.map((p) => (
        <GradientGlow key={p.label} from={p.from} via={p.via} to={p.to}>
          <div className="bg-background text-foreground-intense flex size-28 items-center justify-center rounded-2xl text-sm font-medium">
            {p.label}
          </div>
        </GradientGlow>
      ))}
    </div>
  )
}
```

### Blur / softness [#blur--softness]

`blur` controls how soft the halo is, mapped to Tailwind's `blur-*` scale (`sm` through `3xl`). Larger
values spread the glow further past the edges for a more diffuse, ambient look.

```tsx
import { GradientGlow } from '@appica/ui-react/gradient-glow'

const LEVELS = ['md', 'lg', '2xl'] as const

export default function GradientGlowBlur() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-10">
      {LEVELS.map((blur) => (
        <GradientGlow key={blur} blur={blur}>
          <div className="bg-background text-foreground-intense flex size-28 items-center justify-center rounded-2xl text-sm font-medium">
            blur=&quot;{blur}&quot;
          </div>
        </GradientGlow>
      ))}
    </div>
  )
}
```

### Reveal on interaction [#reveal-on-interaction]

Set `revealOn` to keep the glow hidden until an interaction, then fade it in (the animation resumes from
paused). Accepts `'hover'` (pointer-only, like Tailwind's `hover:` variant) and `'press'` (works on touch),
or an array to combine them. Since `'hover'` can't fire on touch, opt into `showOnTouch` to keep the glow
persistently visible there instead of hidden. Hover or press a button below.

```tsx
import { GradientGlow } from '@appica/ui-react/gradient-glow'
import { buttonVariants } from '@appica/ui-react/button'
import { cn } from '@/lib/utils'

export default function GradientGlowRevealOnInteraction() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-8">
      <GradientGlow revealOn="hover" pressScale border showOnTouch blur="md" className="rounded-full">
        <button type="button" className={cn(buttonVariants({ variant: 'outline', size: 'lg' }), 'rounded-full')}>
          Hover me
        </button>
      </GradientGlow>
      <GradientGlow revealOn="press" pressScale border blur="md" className="rounded-full">
        <button type="button" className={cn(buttonVariants({ variant: 'outline', size: 'lg' }), 'rounded-full')}>
          Press me
        </button>
      </GradientGlow>
    </div>
  )
}
```

### Controlled (loading state) [#controlled-loading-state]

For event-driven states — a request in flight, an AI task running — drive the glow with the controlled
`reveal` boolean instead. It's OR-ed with any `revealOn` interaction, so you can combine "glow on hover"
with "glow while loading". Click the button to kick off a mock request.

```tsx
'use client'

import { useRef, useState } from 'react'
import { GradientGlow } from '@appica/ui-react/gradient-glow'
import { buttonVariants } from '@appica/ui-react/button'
import { Spinner } from '@appica/ui-react/spinner'
import { Sparkle } from '@appica/icons-react'
import { cn } from '@/lib/utils'

export default function GradientGlowLoading() {
  const [loading, setLoading] = useState(false)
  const timer = useRef<ReturnType<typeof setTimeout> | null>(null)

  function run() {
    if (timer.current) clearTimeout(timer.current)
    setLoading(true)
    timer.current = setTimeout(() => setLoading(false), 5000)
  }

  return (
    <GradientGlow reveal={loading} border blur="md" speed={3} className="rounded-full">
      <button
        type="button"
        onClick={run}
        className={cn(buttonVariants({ variant: 'outline', size: 'lg' }), 'rounded-full')}
      >
        {loading ? (
          <Spinner variant="sparkle" currentColor data-icon="start" className="text-xl" />
        ) : (
          <Sparkle data-icon="start" />
        )}
        {loading ? 'Thinking…' : 'Ask AI'}
      </button>
    </GradientGlow>
  )
}
```

## API reference [#api-reference]

| Prop          | <ColMinWidth width="210">Type</ColMinWidth>                 | Default     | <ColMinWidth width="220">Description</ColMinWidth>                                                                         |
| ------------- | ----------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------- |
| `from`        | `string`                                                    | `'#8EC5FF'` | First gradient stop.                                                                                                       |
| `via`         | `string`                                                    | `'#EFADF7'` | Middle gradient stop.                                                                                                      |
| `to`          | `string`                                                    | `'#FFD69B'` | Last gradient stop.                                                                                                        |
| `angle`       | `number`                                                    | `95`        | Resting gradient direction in degrees; the animation rotates a full turn around it.                                        |
| `blur`        | <Code>'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl' \| '3xl'</Code> | `'lg'`      | Softness of the glow, mapped to a Tailwind `blur-*` utility.                                                               |
| `border`      | `boolean`                                                   | `false`     | Add a 1px stroke that follows the same animated gradient.                                                                  |
| `borderWidth` | `number`                                                    | `1`         | Stroke thickness in px when `border` is on.                                                                                |
| `speed`       | `number`                                                    | `4`         | Seconds for one full rotation (and one breath) of the gradient.                                                            |
| `revealOn`    | <Code>'hover' \| 'press' \| Array</Code>                    | —           | Reveal only on interaction. `'hover'` is pointer-only; `'press'` works on touch. Combine via an array. Omit for always-on. |
| `reveal`      | `boolean`                                                   | —           | Controlled visibility for programmatic states (loading, etc.); OR-ed with `revealOn`.                                      |
| `showOnTouch` | `boolean`                                                   | `false`     | With `revealOn="hover"`, keep the glow visible on touch devices (which have no hover) instead of hidden.                   |
| `pressScale`  | `boolean`                                                   | `false`     | Scale the glow down while pressed, to track a child `Button`'s own active-press scale.                                     |
| `className`   | `string`                                                    | —           | Extra classes, merged via `tailwind-merge`. Set the radius here to match your content.                                     |
| `style`       | `CSSProperties`                                             | —           | Inline styles on the wrapper.                                                                                              |

`GradientGlow` renders a `<div>` and forwards every remaining native `<div>` attribute. The glow and
border layers are `aria-hidden` and `pointer-events-none`.

In dark mode the glow and border are automatically dimmed to 60% opacity so they don't look too bright
against dark backgrounds. Tune this via the `--gradient-glow-opacity` CSS variable, e.g.
`className="dark:[--gradient-glow-opacity:0.8]"`.

## Accessibility [#accessibility]

* The glow and border layers are `aria-hidden` and `pointer-events-none`, so they're skipped by assistive
  tech and never intercept clicks.
* Content is wrapped, not replaced — your `children` stay in the normal flow and fully accessible.
* The motion honours `prefers-reduced-motion`: the rotation and pulse only run under the `motion-safe`
  variant, so reduced-motion users see a static gradient instead of the drift.
* The glow is purely decorative — keep your content's own surface and text contrast intact so it stays
  readable regardless of the colors behind it.
