Innovations

Luxe Portrait

Luxury split layout — full-height portrait on the left, dark links column on the right with gold (#c9a961) divider lines. Playfair serif name, gold italic handle. On mobile, stacks portrait above the link column.

Preview

Source

tsx
"use client";

import {
  Instagram,
  Twitter,
  Youtube,
  Mail,
  Globe,
  ShoppingBag,
  PlayCircle,
  Linkedin,
  Music2,
  ArrowUpRight,
  type LucideIcon,
} from "lucide-react";
import type { LinkIcon, LinksInBioData, RichBlock } from "../types";
import { defaultData } from "../defaultData";

const ICONS: Record<LinkIcon, LucideIcon> = {
  instagram: Instagram,
  twitter: Twitter,
  tiktok: Music2,
  youtube: Youtube,
  spotify: Music2,
  apple: Music2,
  linkedin: Linkedin,
  email: Mail,
  globe: Globe,
  shop: ShoppingBag,
  play: PlayCircle,
};

const NIGHT = "#0c0c0e";
const NIGHT_SOFT = "#15151a";
const GOLD = "#c9a961";
const GOLD_SOFT = "#a98a45";
const CREAM = "#f3e7c9";

function FeaturedProductCard({ block }: { block: Extract<RichBlock, { kind: "featured-product" }> }) {
  return (
    <a
      href={block.href}
      className="group block overflow-hidden border transition-all hover:-translate-y-0.5"
      style={{
        borderColor: GOLD_SOFT,
        background: NIGHT_SOFT,
      }}
    >
      <div className="aspect-[16/10] overflow-hidden">
        <img
          src={block.image}
          alt={block.title}
          width={800}
          height={500}
          loading="lazy"
          className="h-full w-full object-cover transition-transform duration-700 group-hover:scale-[1.04]"
        />
      </div>
      <div className="flex items-center justify-between gap-3 p-4">
        <div className="min-w-0">
          <p className="text-[10px] uppercase tracking-[0.28em]" style={{ color: GOLD }}>
            Featured
          </p>
          <p
            className="mt-1 truncate text-base"
            style={{
              fontFamily: "'Playfair Display', serif",
              color: CREAM,
            }}
          >
            {block.title}
          </p>
        </div>
        <span
          className="shrink-0 text-sm font-semibold"
          style={{ fontFamily: "'Playfair Display', serif", color: GOLD }}
        >
          {block.price}
        </span>
      </div>
    </a>
  );
}

export interface LinksInBioLuxePortraitProps {
  data?: LinksInBioData;
}

export default function LinksInBioLuxePortrait({
  data = defaultData,
}: LinksInBioLuxePortraitProps) {
  const product = data.richBlocks?.find(
    (b): b is Extract<RichBlock, { kind: "featured-product" }> =>
      b.kind === "featured-product"
  );

  return (
    <>
      <link rel="preconnect" href="https://fonts.googleapis.com" />
      <link
        rel="stylesheet"
        href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Inter:wght@400;500;600&display=swap"
      />

      <section
        className="relative min-h-screen w-full"
        style={{ background: NIGHT, color: CREAM, fontFamily: "'Inter', sans-serif" }}
      >
        <div className="grid min-h-screen lg:grid-cols-2">
          {/* PORTRAIT — full-height on desktop, top hero on mobile */}
          <div className="relative h-72 overflow-hidden lg:h-auto lg:min-h-screen">
            <img
              src={data.avatar}
              alt={data.name}
              width={1200}
              height={1600}
              loading="eager"
              className="h-full w-full object-cover"
            />
            <div
              aria-hidden
              className="absolute inset-0"
              style={{
                background:
                  "linear-gradient(180deg, rgba(12,12,14,0.0) 35%, rgba(12,12,14,0.85) 100%)",
              }}
            />
            <div
              aria-hidden
              className="hidden lg:block lg:absolute lg:inset-y-0 lg:right-0 lg:w-32"
              style={{
                background: "linear-gradient(90deg, rgba(12,12,14,0) 0%, rgba(12,12,14,1) 100%)",
              }}
            />

            {/* Mobile: name overlay anchored bottom */}
            <div className="absolute inset-x-0 bottom-0 px-6 pb-6 lg:hidden">
              <p
                className="text-[10px] uppercase tracking-[0.32em]"
                style={{ color: GOLD }}
              >
                {data.handle}
              </p>
              <h1
                className="mt-2"
                style={{
                  fontFamily: "'Playfair Display', serif",
                  fontSize: "clamp(2rem, 7vw, 2.6rem)",
                  lineHeight: 1.05,
                }}
              >
                {data.name}
              </h1>
            </div>
          </div>

          {/* LINKS COLUMN */}
          <div className="px-6 py-10 sm:px-10 sm:py-14 lg:px-14">
            <div className="mx-auto w-full max-w-[460px]">
              {/* Desktop name block */}
              <div className="hidden lg:block">
                <p
                  className="text-[10px] uppercase tracking-[0.32em]"
                  style={{ color: GOLD }}
                >
                  {data.handle}
                </p>
                <h1
                  className="mt-3"
                  style={{
                    fontFamily: "'Playfair Display', serif",
                    fontSize: "clamp(2.4rem, 4.4vw, 3.1rem)",
                    lineHeight: 1.05,
                  }}
                >
                  {data.name}
                </h1>
                <p
                  className="mt-2 text-sm italic"
                  style={{ fontFamily: "'Playfair Display', serif", color: GOLD }}
                >
                  {data.verified ? "Verified · By appointment" : "By appointment"}
                </p>
              </div>

              {data.bio && (
                <p className="mt-5 text-[15px] leading-relaxed text-white/70 lg:mt-7">
                  {data.bio}
                </p>
              )}

              <div
                aria-hidden
                className="my-7"
                style={{
                  height: "1px",
                  background: `linear-gradient(90deg, transparent, ${GOLD_SOFT}, transparent)`,
                }}
              />

              <ul className="space-y-1">
                {data.links.map((link, i) => {
                  const Icon = link.icon ? ICONS[link.icon] : ArrowUpRight;
                  const isLast = i === data.links.length - 1;
                  return (
                    <li key={link.label}>
                      <a
                        href={link.href}
                        className="group flex items-center justify-between gap-4 px-1 py-4 transition-colors"
                        style={{
                          borderBottom: isLast ? "none" : `1px solid ${GOLD_SOFT}33`,
                        }}
                      >
                        <div className="flex min-w-0 items-center gap-3">
                          <Icon
                            className="h-4 w-4 shrink-0 transition-colors"
                            style={{ color: GOLD }}
                            strokeWidth={1.5}
                          />
                          <span
                            className="truncate text-[15px]"
                            style={{ color: CREAM }}
                          >
                            {link.label}
                          </span>
                          {link.badge && (
                            <span
                              className="ml-2 shrink-0 border px-2 py-0.5 text-[10px] font-medium uppercase tracking-[0.18em]"
                              style={{ borderColor: GOLD_SOFT, color: GOLD }}
                            >
                              {link.badge}
                            </span>
                          )}
                        </div>
                        <ArrowUpRight
                          className="h-4 w-4 shrink-0 transition-transform group-hover:-translate-y-0.5 group-hover:translate-x-0.5"
                          style={{ color: GOLD }}
                        />
                      </a>
                    </li>
                  );
                })}
              </ul>

              {product && (
                <div className="mt-8">
                  <FeaturedProductCard block={product} />
                </div>
              )}

              {data.socials && data.socials.length > 0 && (
                <>
                  <div
                    aria-hidden
                    className="my-8"
                    style={{
                      height: "1px",
                      background: `linear-gradient(90deg, transparent, ${GOLD_SOFT}, transparent)`,
                    }}
                  />
                  <div className="flex items-center justify-center gap-5">
                    {data.socials.map((s, i) => {
                      const Icon = ICONS[s.type] ?? Globe;
                      return (
                        <a
                          key={i}
                          href={s.href}
                          aria-label={s.type}
                          className="transition-opacity hover:opacity-100"
                          style={{ color: GOLD, opacity: 0.7 }}
                        >
                          <Icon className="h-4 w-4" strokeWidth={1.5} />
                        </a>
                      );
                    })}
                  </div>
                </>
              )}

              <p
                className="mt-10 text-center text-[10px] uppercase tracking-[0.32em]"
                style={{ color: GOLD_SOFT }}
              >
                Atelier · Established
              </p>
            </div>
          </div>
        </div>
      </section>
    </>
  );
}
Claude Code Instructions

CLI Install

npx innovations add luxe-portrait

Where to use it

A luxury, by-appointment link-in-bio page with a dramatic portrait + dark gold-accented link column. Hardcoded dark palette. Pass a typed 'data' prop (LinksInBioData from src/registry/links-in-bio/types.ts) to swap content. With no prop it renders sample data. Add a richBlocks entry with kind 'featured-product' to render a featured offer card (image + title + price + href) below the link list. In Astro: import LinksInBioLuxePortrait from '../components/innovations/links-in-bio/luxe-portrait'; <LinksInBioLuxePortrait client:load data={myProfile} /> In Next.js: import LinksInBioLuxePortrait from '@/components/innovations/links-in-bio/luxe-portrait'; Best for: high-end coaches, designers, photographers, luxury services. The portrait must be a strong vertical-friendly image — works best with a clean studio shot or editorial portrait.