Skip to content
All Taste components

Gradient heroes

App icon halo hero

An app launch section with the app icon at its centre, ringed by a turning, breathing halo of colour.

rotating colour ring · pulsing glow · app icon · download buttons · rating

Plan the week’s meals before you’re hungry.

Pantry builds a shopping list from the recipes you pick and what is already in your fridge, so less ends up in the bin.

4.8 · 12,400 ratings
import { Leaf, Smartphone, Star } from "lucide-react";

/* The ring is a conic gradient cut to a band with a radial mask, blurred once,
   then turned; its wrapper breathes.
Add once to globals.css:
@keyframes halo-spin { to { transform: rotate(1turn); } }
@keyframes halo-pulse { from { transform: scale(.92); opacity: .7; } to { transform: scale(1.06); opacity: 1; } }
@keyframes drift-a { to { transform: translate(18%, 22%) scale(1.12); } }
@keyframes drift-b { to { transform: translate(-22%, 18%) scale(.9); } }
*/

const ringMask = "radial-gradient(closest-side, transparent 58%, var(--tst-fg) 66%, var(--tst-fg) 76%, transparent 86%)";

export function HeroHalo() {
  return (
    <section aria-labelledby="halo-title" className="relative isolate overflow-hidden bg-bg px-5 pb-24 pt-5 font-sans text-fg sm:px-10">
      <div aria-hidden className="absolute inset-0 -z-10 overflow-hidden bg-[radial-gradient(60%_50%_at_50%_30%,color-mix(in_srgb,var(--tst-primary)_8%,var(--tst-bg)),var(--tst-bg))]">
        <span className="absolute -bottom-[40%] -left-[8%] aspect-square w-[max(40%,300px)] rounded-full bg-[radial-gradient(closest-side,color-mix(in_srgb,var(--tst-info)_30%,transparent),transparent)] animate-[drift-a_24s_ease-in-out_infinite_alternate] motion-reduce:animate-none" />
        <span className="absolute -bottom-[36%] -right-[10%] aspect-square w-[max(40%,300px)] rounded-full bg-[radial-gradient(closest-side,color-mix(in_srgb,var(--tst-warning)_28%,transparent),transparent)] animate-[drift-b_28s_ease-in-out_infinite_alternate] motion-reduce:animate-none" />
      </div>

      <nav aria-label="Main" className="mx-auto mb-12 flex max-w-[1080px] items-center justify-between gap-4 sm:mb-16">
        <a href="/" className="flex items-center gap-2 text-[15px] font-semibold">
          <span className="size-[22px] rounded-sm bg-gradient-to-br from-primary to-info" /> Pantry
        </a>
        <a href="/download" className="inline-flex h-8 items-center rounded bg-primary px-3 text-[13px] font-medium text-primary-fg hover:bg-primary-hover">Get the app</a>
      </nav>

      <div className="mx-auto flex max-w-[1080px] flex-col items-center gap-5 text-center">
        <div className="relative -mb-9 -mt-6 grid size-60 place-items-center animate-rise motion-reduce:animate-none">
          <span aria-hidden className="absolute -inset-[30%] rounded-full bg-[radial-gradient(closest-side,color-mix(in_srgb,var(--tst-primary)_40%,transparent),transparent)] animate-[halo-pulse_6s_ease-in-out_infinite_alternate] motion-reduce:animate-none" />
          <span aria-hidden className="absolute inset-0 animate-[halo-pulse_6s_ease-in-out_infinite_alternate] motion-reduce:animate-none">
            <span className="absolute inset-0 rounded-full bg-[conic-gradient(var(--tst-primary),var(--tst-info),var(--tst-success),var(--tst-warning),var(--tst-danger),var(--tst-primary))] blur-[10px] will-change-transform animate-[halo-spin_18s_linear_infinite] motion-reduce:animate-none"
              style={{ maskImage: ringMask, WebkitMaskImage: ringMask }} />
          </span>
          <span role="img" aria-label="Pantry app icon" className="relative grid size-24 place-items-center rounded-[calc(var(--tst-radius-xl)*1.4)] bg-gradient-to-br from-primary to-[color-mix(in_srgb,var(--tst-primary)_55%,var(--tst-info))] text-primary-fg shadow-[0_20px_40px_-16px_color-mix(in_srgb,var(--tst-primary)_70%,transparent)]">
            <Leaf className="size-11" aria-hidden />
          </span>
        </div>
        <h1 id="halo-title" className="max-w-[13ch] text-balance text-4xl font-medium leading-[1.02] tracking-[-0.04em] sm:text-6xl lg:text-7xl animate-rise [animation-delay:80ms] motion-reduce:animate-none">
          Plan the week’s meals before you’re hungry.
        </h1>
        <p className="max-w-xl text-pretty text-base leading-relaxed text-fg-muted sm:text-lg animate-rise [animation-delay:160ms] motion-reduce:animate-none">
          Pantry builds a shopping list from the recipes you pick and what is already in your fridge, so less ends up in the bin.
        </p>
        <div className="flex flex-wrap justify-center gap-2.5 animate-rise [animation-delay:240ms] motion-reduce:animate-none">
          <a href="/ios" className="inline-flex h-11 items-center gap-1.5 rounded bg-primary px-5 font-medium text-primary-fg hover:bg-primary-hover active:scale-[.97]">
            <Smartphone className="size-4" aria-hidden /> Download for iPhone
          </a>
          <a href="/android" className="inline-flex h-11 items-center gap-1.5 rounded border border-border-strong bg-bg px-5 font-medium hover:bg-surface active:scale-[.97]">
            <Smartphone className="size-4" aria-hidden /> Get it on Android
          </a>
        </div>
        <p className="inline-flex items-center gap-2 text-[13px] text-fg-muted">
          <span className="inline-flex gap-0.5 text-warning" aria-label="Rated 4.8 out of 5">
            {[0, 1, 2, 3, 4].map((i) => <Star key={i} className="size-3.5 fill-current" aria-hidden />)}
          </span>
          4.8 · 12,400 ratings
        </p>
      </div>
    </section>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.