---
title: "GradientBorder"
description: "A wrapper that draws a rotating gradient border around its content."
category: Effects
status: beta
since: 0.3.4
tags: [border, gradient, highlight]
syncStatus: reviewed
verified: true
---

## Usage

### Basic
:::TuffDemoWrapper{demo="GradientBorderGradientBorderDemo" code-lang="vue"}
---
code: |
  <template>
    <TxGradientBorder :padding="16" :border-radius="16">
      <div class="surface">Content</div>
    </TxGradientBorder>
  </template>
---
:::

### Semantic Root
`as` sets the root element, such as `section` or `li`.

```vue
<template>
  <TxGradientBorder as="section" :border-width="3" :border-radius="20" padding="1rem 1.25rem">
    <article class="rounded-[16px] bg-[var(--tx-bg-color)] p-4">
      <h3>Release candidate</h3>
      <p>Ready for manual QA.</p>
    </article>
  </TxGradientBorder>
</template>
```

### Custom Units
String sizes are kept as-is, so CSS variables and multi-value padding work.

```vue
<template>
  <TxGradientBorder
    border-width="0.125rem"
    border-radius="var(--radius-lg)"
    padding="1rem 1.5rem"
    :animation-duration="6"
  >
    <div class="rounded-[inherit] bg-[var(--tx-bg-color)] p-4">
      Uses project tokens
    </div>
  </TxGradientBorder>
</template>
```

### Best Practices

- Put a surface with its own background inside, with a radius of `borderRadius - borderWidth`, so no corners show.
- Use `as` for semantics instead of adding another landmark around the component.
- Keep one gradient highlight per region and slow `animationDuration` when several share a screen; use `TxBadge` or `TxTag` for small status accents.
- Don't wrap controls whose focus rings must escape the border, unless the inner content styles focus itself.

## API Reference

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `as` | `string` | `'div'` | Root element tag. |
| `borderWidth` | `string \| number` | `'2px'` | Border width, also the glow's blur radius; numbers are px. |
| `borderRadius` | `string \| number` | `'12px'` | Radius of the wrapper and the ring; numbers are px. |
| `padding` | `string \| number` | `'12px'` | Padding of the content wrapper; numbers are px. |
| `animationDuration` | `number` | `4` | Seconds per rotation. |

### Slots

| Slot | Props | Description |
|------|------|-------------|
| `default` | - | Content rendered inside `.tx-gradient-border__inner`. |

### CSS Variables

| Variable | Source | Description |
|----------|--------|-------------|
| `--tx-gradient-border-width` | `borderWidth` | Border thickness and blur distance. |
| `--tx-gradient-border-radius` | `borderRadius` | Radius of the wrapper and the ring. |
| `--tx-gradient-inner-padding` | `padding` | Padding of the content wrapper. |
| `--tx-gradient-duration` | `animationDuration` | Rotation duration, with an `s` suffix. |
| `--tx-gradient-angle` | internal animation | Registered angle property the gradient uses. |

## Overview

- `as` sets the root, and one `.tx-gradient-border__inner` wraps the slot.
- The ring sits on its own `aria-hidden` layer with `pointer-events: none`; slotted content owns all interaction.
- The ring follows `borderRadius`, and its glow is not clipped: it spreads to both sides of the wrapper's edge.
- `.tx-gradient-border__inner` is `overflow: hidden` with the same radius, so child focus rings or shadows past it are clipped.
- Under reduced motion the rotation stops and the border rests at a static gradient angle.

## Technologies

- The ring is cut from a filled box with `mask`, and the blur sits on its parent layer because `filter` runs before `mask` on one element; the registered `--tx-gradient-angle` drives the rotation.
- Source: `packages/tuffex/packages/components/src/gradient-border/`.

<TuffDocSourceLink />
