1 GitHub star = 1 new component.

Star the repo
E-commerceecommerceshopherolandingproduct

Store Hero

Shop landing hero with gradient headline, CTAs, proof stats and a floating featured-product stage.

Live Preview

New season ยท 2026

Gear that feels made for you

Thoughtfully designed audio, wearables and everyday carry. Free shipping over $75 and 30-day returns on everything.

from 12k reviews
4.9/5
average delivery
48h
free returns
30 days

Usage

Copy and paste this code into your React component:

tsx
import { StoreHero } from 'react-component-kit-ui/ecommerce';

<StoreHero title="Gear that feels" highlight="made for you" primary={{ label: 'Shop now', href: '/shop/' }} />

Manual Installation

If you are not using the npm package, create a new file and paste the following code: src/components/StoreHero.tsx + StoreHero.css:

StoreHero.tsx
'use client';

import ProductMedia from './ProductMedia';
import ProductRating from './ProductRating';
import { formatMoney } from './cart-store';
import type { Product } from './types';
import './StoreHero.css';

/** class:list โ†’ className */
function cx(...args: unknown[]): string {
  const out: string[] = [];
  const walk = (v: unknown) => {
    if (!v) return;
    if (typeof v === 'string' || typeof v === 'number') out.push(String(v));
    else if (Array.isArray(v)) v.forEach(walk);
    else if (typeof v === 'object') for (const [k, on] of Object.entries(v as Record<string, unknown>)) if (on) out.push(k);
  };
  args.forEach(walk);
  return out.join(' ');
}

interface Cta { label: string; href: string }

/**
 * StoreHero โ€” Shop landing hero: headline, CTAs, quick stats and a featured product
 * stage with floating price and rating chips.
 *
 * @param {string} eyebrow - Small label above the title. Default 'New season ยท 2026'.
 * @param {string} title - Headline. Default 'Gear that feels'.
 * @param {string} highlight - Gradient part of the headline. Default 'made for you'.
 * @param {string} subtitle - Supporting copy.
 * @param {{label: string, href: string}} primary - Main CTA.
 * @param {{label: string, href: string}} secondary - Secondary CTA (optional).
 * @param {Array<{value: string, label: string}>} stats - Proof points below the CTAs.
 * @param {Product} product - Featured product shown on the stage.
 * @param {string} productHref - Featured product link.
 * @param {string} currency - ISO currency. Default 'USD'.
 * @param {string} locale - Number format locale. Default 'en-US'.
 * @param {string} variant - Layout (split|centered). Default 'split'.
 * @param {string} size - Vertical rhythm (sm|md|lg). Default 'md'.
 * @param {string} className - Extra class.
 *
 * Accessibility: one h1; decorative stage art is aria-hidden; the featured product is a real link.
 */
export interface StoreHeroProps {
  eyebrow?: string;
  title?: string;
  highlight?: string;
  subtitle?: string;
  primary?: Cta;
  secondary?: Cta;
  stats?: Array<{ value: string; label: string }>;
  product?: Product;
  productHref?: string;
  currency?: string;
  locale?: string;
  variant?: 'split' | 'centered';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

export function StoreHero({
  eyebrow = 'New season ยท 2026',
  title = 'Gear that feels',
  highlight = 'made for you',
  subtitle = 'Thoughtfully designed audio, wearables and everyday carry. Free shipping over $75 and 30-day returns on everything.',
  primary = { label: 'Shop the collection', href: '#' },
  secondary = { label: 'Explore new arrivals', href: '#' },
  stats = [
    { value: '4.9/5', label: 'from 12k reviews' },
    { value: '48h', label: 'average delivery' },
    { value: '30 days', label: 'free returns' },
  ],
  product = { slug: 'aurora-headphones', name: 'Aurora Wireless Headphones', price: 189, compareAt: 229, category: 'audio', rating: 4.8, reviews: 312, icon: 'headphones', hue: 38 },
  productHref = '#',
  currency = 'USD',
  locale = 'en-US',
  variant = 'split',
  size = 'md',
  className = '',
}: StoreHeroProps) {
  const money = { currency, locale };
  return (
    <>
      <section className={cx(['store-hero', `store-hero--${variant}`, `store-hero--${size}`, className])} data-rk-92f260="">
        <div className="store-hero__glow" aria-hidden="true" data-rk-92f260=""></div>
        <div className="store-hero__inner" data-rk-92f260="">
          <div className="store-hero__copy" data-rk-92f260="">
            {eyebrow && <p className="store-hero__eyebrow" data-rk-92f260=""><span aria-hidden="true" data-rk-92f260=""></span>{eyebrow}</p>}
            <h1 className="store-hero__title" data-rk-92f260="">{title} <em data-rk-92f260="">{highlight}</em></h1>
            {subtitle && <p className="store-hero__subtitle" data-rk-92f260="">{subtitle}</p>}
            <div className="store-hero__ctas" data-rk-92f260="">
              {primary && (
                <a className="store-hero__btn store-hero__btn--primary" href={primary.href} data-rk-92f260="">
                  {primary.label}
                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" data-rk-92f260=""><path d="M5 12h14M13 6l6 6-6 6" data-rk-92f260="" /></svg>
                </a>
              )}
              {secondary && <a className="store-hero__btn store-hero__btn--ghost" href={secondary.href} data-rk-92f260="">{secondary.label}</a>}
            </div>
            {stats.length > 0 && (
              <dl className="store-hero__stats" data-rk-92f260="">
                {stats.map((s, __i: number) => (
                  <div key={__i} data-rk-92f260=""><dt data-rk-92f260="">{s.label}</dt><dd data-rk-92f260="">{s.value}</dd></div>
                ))}
              </dl>
            )}
          </div>
      
          {product && (
            <div className="store-hero__stage" data-rk-92f260="">
              <div className="store-hero__ring" aria-hidden="true" data-rk-92f260=""></div>
              <a className="store-hero__product" href={productHref} data-rk-92f260="">
                <ProductMedia image={product.image} alt={product.name} icon={product.icon} hue={product.hue} size="lg" ratio="4 / 5" />
                <span className="store-hero__chip store-hero__chip--price" data-rk-92f260="">
                  <small data-rk-92f260="">{product.name}</small>
                  <strong data-rk-92f260="">{formatMoney(product.price, money)}</strong>
                  {product.compareAt && <s data-rk-92f260=""><span className="sr-only" data-rk-92f260="">Was </span>{formatMoney(product.compareAt, money)}</s>}
                </span>
                {product.rating !== undefined && (
                  <span className="store-hero__chip store-hero__chip--rating" data-rk-92f260="">
                    <ProductRating value={product.rating} count={product.reviews} />
                  </span>
                )}
              </a>
            </div>
          )}
        </div>
      </section>
    </>
  );
}

export default StoreHero;
StoreHero.css
/* StoreHero โ€” ported from astro-component-kit (ecommerce/StoreHero.astro) */

.store-hero:where([data-rk-92f260]) {
  container-type: inline-size;
  --hero-pad: clamp(3rem, 7vw, 6rem);
  position: relative;
  overflow: hidden;
  padding: var(--hero-pad) var(--sp-6, 1.5rem);
  isolation: isolate;
}
.store-hero--sm:where([data-rk-92f260]) { --hero-pad: clamp(2rem, 5vw, 3.5rem); }
.store-hero--lg:where([data-rk-92f260]) { --hero-pad: clamp(4rem, 9vw, 8rem); }

.store-hero__glow:where([data-rk-92f260]) {
  position: absolute;
  inset: -20% -10% auto 30%;
  height: 120%;
  background: radial-gradient(closest-side, rgba(212, 160, 23, 0.16), transparent 70%);
  z-index: -1;
  pointer-events: none;
}

.store-hero__inner:where([data-rk-92f260]) {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 4rem);
  max-width: 1280px;
  margin: 0 auto;
}
.store-hero--centered:where([data-rk-92f260]) .store-hero__inner:where([data-rk-92f260]) { grid-template-columns: 1fr; text-align: center; justify-items: center; }
.store-hero--centered:where([data-rk-92f260]) .store-hero__ctas:where([data-rk-92f260]), .store-hero--centered:where([data-rk-92f260]) .store-hero__stats:where([data-rk-92f260]) { justify-content: center; }
.store-hero--centered:where([data-rk-92f260]) .store-hero__stage:where([data-rk-92f260]) { max-width: 420px; }

.store-hero__eyebrow:where([data-rk-92f260]) {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2, 0.5rem);
  margin: 0 0 var(--sp-4, 1rem);
  padding: 0.35rem 0.85rem;
  border-radius: var(--r-full, 9999px);
  border: 1px solid rgba(212, 160, 23, 0.3);
  background: rgba(212, 160, 23, 0.08);
  color: var(--c-accent, #fef3c7);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.store-hero__eyebrow:where([data-rk-92f260]) span:where([data-rk-92f260]) { width: 6px; height: 6px; border-radius: 50%; background: var(--c-primary-light, #d4a017); box-shadow: 0 0 10px var(--c-primary-light, #d4a017); }

.store-hero__title:where([data-rk-92f260]) {
  margin: 0;
  font-size: clamp(2.4rem, 6vw, 4.6rem);
  font-weight: 900;
  line-height: 1.02;
  letter-spacing: -0.04em;
  color: var(--c-text-1, #fff);
}
.store-hero__title:where([data-rk-92f260]) em:where([data-rk-92f260]) {
  display: block;
  font-style: normal;
  background: linear-gradient(100deg, var(--c-accent, #fef3c7), var(--c-primary-light, #d4a017) 55%, var(--c-primary, #b8860b));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.store-hero__subtitle:where([data-rk-92f260]) { max-width: 34rem; margin: var(--sp-5, 1.25rem) 0 0; font-size: 1.05rem; line-height: 1.65; color: var(--c-text-2, #a3a3a3); }
.store-hero--centered:where([data-rk-92f260]) .store-hero__subtitle:where([data-rk-92f260]) { margin-inline: auto; }

.store-hero__ctas:where([data-rk-92f260]) { display: flex; flex-wrap: wrap; gap: var(--sp-3, 0.75rem); margin-top: var(--sp-8, 2rem); }
.store-hero__btn:where([data-rk-92f260]) {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2, 0.5rem);
  min-height: 52px;
  padding: 0 var(--sp-6, 1.5rem);
  border-radius: var(--r-full, 9999px);
  font-weight: 700;
  font-size: 0.95rem;
  text-decoration: none;
  transition: transform 0.2s ease, filter 0.2s ease, background 0.2s ease;
}
.store-hero__btn--primary:where([data-rk-92f260]) { background: var(--c-primary-light, #d4a017); color: var(--c-on-primary, #050505); }
.store-hero__btn--primary:hover:where([data-rk-92f260]) { color: var(--c-on-primary, #050505); filter: brightness(1.08); transform: translateY(-2px); }
.store-hero__btn--ghost:where([data-rk-92f260]) { border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.14); color: var(--c-text-1, #fff); }
.store-hero__btn--ghost:hover:where([data-rk-92f260]) { background: rgb(var(--ink-rgb, 255 255 255) / 0.05); color: var(--c-text-1, #fff); }
.store-hero__btn:focus-visible:where([data-rk-92f260]), .store-hero__product:focus-visible:where([data-rk-92f260]) { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 3px; }

.store-hero__stats:where([data-rk-92f260]) { display: flex; flex-wrap: wrap; gap: var(--sp-8, 2rem); margin: var(--sp-10, 2.5rem) 0 0; }
.store-hero__stats:where([data-rk-92f260]) div:where([data-rk-92f260]) { display: flex; flex-direction: column-reverse; }
.store-hero__stats:where([data-rk-92f260]) dt:where([data-rk-92f260]) { font-size: 0.8rem; color: var(--c-text-2, #a3a3a3); }
.store-hero__stats:where([data-rk-92f260]) dd:where([data-rk-92f260]) { margin: 0; font-size: 1.35rem; font-weight: 800; color: var(--c-text-1, #fff); letter-spacing: -0.02em; }

.store-hero__stage:where([data-rk-92f260]) { position: relative; width: 100%; max-width: 480px; justify-self: center; }
.store-hero__ring:where([data-rk-92f260]) {
  position: absolute;
  inset: 8% -6%;
  border-radius: 50%;
  border: 1px dashed rgba(212, 160, 23, 0.25);
  animation: store-hero-sh-spin 40s linear infinite;
}
.store-hero__product:where([data-rk-92f260]) {
  position: relative;
  display: block;
  border-radius: var(--r-xl, 28px);
  border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
  overflow: visible;
  color: inherit;
  text-decoration: none;
  animation: store-hero-sh-float 7s ease-in-out infinite;
}
.store-hero__product:where([data-rk-92f260]) .p-media { border-radius: var(--r-xl, 28px); }
.store-hero__chip:where([data-rk-92f260]) {
  position: absolute;
  display: grid;
  gap: 2px;
  padding: var(--sp-3, 0.75rem) var(--sp-4, 1rem);
  border-radius: var(--r-md, 12px);
  border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.1);
  background: rgb(var(--paper-rgb, 5 5 5) / 0.78);
  backdrop-filter: blur(14px);
  box-shadow: 0 20px 40px -20px rgba(0, 0, 0, 0.8);
  text-align: left;
}
.store-hero__chip--price:where([data-rk-92f260]) { left: -6%; bottom: 12%; }
.store-hero__chip--price:where([data-rk-92f260]) small:where([data-rk-92f260]) { font-size: 0.72rem; color: var(--c-text-2, #a3a3a3); }
.store-hero__chip--price:where([data-rk-92f260]) strong:where([data-rk-92f260]) { font-size: 1.25rem; color: var(--c-text-1, #fff); }
.store-hero__chip--price:where([data-rk-92f260]) s:where([data-rk-92f260]) { font-size: 0.78rem; color: var(--c-text-2, #a3a3a3); }
.store-hero__chip--rating:where([data-rk-92f260]) { right: -4%; top: 10%; }

@keyframes store-hero-sh-float { 50% { transform: translateY(-10px); } }
@keyframes store-hero-sh-spin { to { transform: rotate(360deg); } }

.sr-only:where([data-rk-92f260]) { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

@container (max-width: 900px) {
  .store-hero__inner:where([data-rk-92f260]) { grid-template-columns: 1fr; }
  .store-hero__stage:where([data-rk-92f260]) { max-width: 360px; }
  .store-hero__chip--price:where([data-rk-92f260]) { left: 0; }
  .store-hero__chip--rating:where([data-rk-92f260]) { right: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .store-hero__product:where([data-rk-92f260]), .store-hero__ring:where([data-rk-92f260]) { animation: none; }
}

Quick Info

Category
E-commerce
Filename
StoreHero.tsx
Dependencies
None โ€” pure React + CSS
Tags
ecommerceshopherolandingproduct