Components Border plasma

Petal Pro is the full SaaS app this is built for

Auth, billing, admin, and Claude Code integration included. One purchase, unlimited projects.

Border plasma

An animated plasma border for the card you want noticed - conic hues drifting around the frame, honouring reduced motion. Pure CSS, no hook.
A breathing glow

The default. The whole ring lights at once and breathes, so nothing travels across the screen - it pulls the eye without dragging it. Colours come off the theme's primary and secondary tokens unless you pass your own.

Upgrade to Pro

Unlimited projects and priority support.

heex
<.border_plasma class="w-full max-w-sm">
<div class="p-8">
<div class="text-lg font-semibold text-gray-900 dark:text-gray-100">
Upgrade to Pro
</div>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
Unlimited projects and priority support.
</p>
</div>
</.border_plasma>
A rotating sweep

mode="rotate" spins a conic gradient arc around the ring instead of breathing it. Busier than pulse, so it earns its place on one hero panel rather than every card on the page.

Now in beta

Ship your next Phoenix app in a weekend.

heex
<.border_plasma mode="rotate" duration="5s" class="w-full max-w-sm">
<div class="p-8">
<div class="text-lg font-semibold text-gray-900 dark:text-gray-100">
Now in beta
</div>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
Ship your next Phoenix app in a weekend.
</p>
</div>
</.border_plasma>
On a CTA

Wrap a button-sized block and the plasma becomes the button's edge. Turn intensity up to strong and set your own colours when the glow needs to carry across a dark hero.

Buy now
heex
<.border_plasma intensity="strong" color_from="#f43f5e" color_to="#3b82f6" class="inline-block">
<div class="px-6 py-2.5 text-sm font-medium text-gray-900 dark:text-gray-100">
Buy now
</div>
</.border_plasma>
Turned down

intensity="subtle" with a hairline border_width is the ambient setting - close to shine_border in weight, but lit all the way round. Good for a card that should feel alive without shouting.

Weekly digest

One email, every Friday. No noise.

heex
<.border_plasma intensity="subtle" border_width="1px" duration="6s" class="w-full max-w-sm">
<div class="p-8">
<div class="text-lg font-semibold text-gray-900 dark:text-gray-100">
Weekly digest
</div>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
One email, every Friday. No noise.
</p>
</div>
</.border_plasma>
Properties
Attribute Type Default Description
border_radius string nil border radius of the container. When unset, follows the theme radius (--pc-radius, scaled for panels)
border_width string "1px" thickness of the glowing rim line. The reference look is a 1px hairline; 2px+ reads as a band
class any nil extra classes for the container
color_from string nil palette="brand" only: overrides the primary anchor colour
color_to string nil palette="brand" only: overrides the secondary anchor colour
duration string nil length of one breath (pulse) or one lap (rotate). Defaults to 2.3s for pulse, 2s for rotate
glow string "outside" where PULSE's light lives: a halo spilling past the border onto the page (outside, the default), a wash hugging the edges inside a crisp silhouette (inside), or the two together (both - the most paint-expensive option; prefer outside or inside when several instances share a view). Rotate always keeps its light inside the silhouette, as the reference effect does
one of: "outside", "inside", "both"
intensity string "medium" how bright the rim jewels burn and how much light washes inward over the panel
one of: "subtle", "medium", "strong"
mode string "pulse" pulse breathes the whole ring in place; rotate sweeps a conic gradient arc around the border
one of: "pulse", "rotate"
palette string "rainbow" where the light gets its colours: an eight-hue rainbow (the reference look), the theme's primary/secondary (brand), grayscale (mono), blues (ocean), or ambers (sunset). Only rainbow runs the slow hue revolution; every other palette holds its anchor hues, so brand light stays on brand
one of: "rainbow", "brand", "mono", "ocean", "sunset"
rest global
:inner_block slot slot