대표 색을 바꿀 수 있는 사진가 포트폴리오 프로필 헤더
콘텐츠 블록오로라 커버, 이니셜 아바타, 소개와 링크, 숫자 통계를 담은 프로필 헤더입니다. 색 점을 누르면 커버, 아바타, 강조색이 함께 바뀝니다.
Claude Code · claude-opus-5-5 · React (TSX)
결과 코드펼치기
import { useState } from "react";
const ACCENTS = [
{ id: "amber", label: "앰버", a: "#fcd34d", b: "#fb923c" },
{ id: "teal", label: "틸", a: "#5eead4", b: "#38bdf8" },
{ id: "rose", label: "로즈", a: "#fda4af", b: "#e879f9" },
{ id: "violet", label: "바이올렛", a: "#a78bfa", b: "#f472b6" },
];
const mix = (c: string, p: number) => `color-mix(in srgb, ${c} ${p}%, transparent)`;
export default function PortfolioProfileHeader() {
const [accent, setAccent] = useState(ACCENTS[0]);
const [following, setFollowing] = useState(false);
const cover = `radial-gradient(60% 140% at 12% 0%, ${mix(accent.a, 55)}, transparent 70%), radial-gradient(55% 120% at 88% 10%, ${mix(accent.b, 45)}, transparent 70%), #18181b`;
return (
<section className="w-full bg-[#09090b] px-4 py-12 text-zinc-100 sm:px-8">
<div className="mx-auto max-w-5xl">
<div className="overflow-hidden rounded-2xl border border-white/10 bg-[#111113]">
<div className="relative h-36 transition-[background] duration-500 sm:h-48" style={{ background: cover }} aria-hidden>
<div className="absolute inset-x-0 bottom-0 h-16 bg-gradient-to-t from-[#111113]/70 to-transparent" />
</div>
<div className="px-5 pb-6 sm:px-8">
<div className="relative -mt-12 flex flex-wrap items-end justify-between gap-4">
<div className="rounded-full bg-[#111113] p-1">
<span
className="flex h-[92px] w-[92px] items-center justify-center rounded-full text-3xl font-semibold text-zinc-900"
style={{ background: `linear-gradient(135deg, ${accent.a}, ${accent.b})` }}
aria-hidden
>
윤
</span>
</div>
<button
type="button"
onClick={() => setFollowing((f) => !f)}
className="h-9 rounded-lg px-4 text-sm font-medium text-zinc-900 transition active:scale-[0.98]"
style={following ? { background: "#27272a", color: "#e4e4e7" } : { background: accent.a }}
>
{following ? "구독 중" : "구독하기"}
</button>
</div>
<p className="mt-4 text-[11px] font-semibold uppercase tracking-[0.22em]" style={{ color: accent.a }}>
Portfolio
</p>
<h2 className="mt-1 text-2xl font-semibold tracking-tight sm:text-3xl">빛과 그림자 사진관</h2>
<p className="mt-1 text-sm text-zinc-400">윤서하 · @seoha.lens · 2021년부터 활동</p>
<p className="mt-3 max-w-2xl text-[15px] leading-relaxed text-zinc-300">
도시의 오후 빛을 찍습니다. 필름 느낌의 보정 프리셋과 촬영 노트를 함께 올려요.
</p>
<ul aria-label="링크" className="mt-4 flex flex-wrap gap-2">
{["instagram.com", "behance.net", "작업 의뢰"].map((l) => (
<li key={l}>
<a
href="#"
className="inline-flex h-8 items-center gap-1.5 rounded-lg border border-white/10 bg-white/[0.04] px-3 text-[13px] text-zinc-400 transition hover:text-zinc-100"
>
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" aria-hidden style={{ color: accent.a }}>
<path
d="M6.5 9.5a3 3 0 0 0 4.24 0l2.12-2.12a3 3 0 0 0-4.24-4.24l-.7.7M9.5 6.5a3 3 0 0 0-4.24 0L3.14 8.62a3 3 0 0 0 4.24 4.24l.7-.7"
stroke="currentColor"
strokeWidth="1.4"
strokeLinecap="round"
/>
</svg>
{l}
</a>
</li>
))}
</ul>
<div className="mt-5 flex flex-wrap items-end justify-between gap-4">
<dl className="flex gap-8 text-sm">
{[
["작품", 128],
["구독자", 2340],
["프리셋", 12],
].map(([label, value]) => (
<div key={label}>
<dt className="text-xs text-zinc-500">{label}</dt>
<dd className="mt-0.5 text-lg font-semibold tabular-nums">{Number(value).toLocaleString("ko-KR")}</dd>
</div>
))}
</dl>
<div role="radiogroup" aria-label="대표 색" className="flex gap-2">
{ACCENTS.map((c) => (
<button
key={c.id}
type="button"
role="radio"
aria-checked={accent.id === c.id}
aria-label={c.label}
onClick={() => setAccent(c)}
className={`h-7 w-7 rounded-full border-2 transition-transform hover:scale-110 ${
accent.id === c.id ? "border-white" : "border-transparent"
}`}
style={{ background: `linear-gradient(135deg, ${c.a}, ${c.b})` }}
/>
))}
</div>
</div>
</div>
</div>
</div>
</section>
);
}
적용 사이트 0
이 블록이 실제로 쓰인 사이트입니다.
아직 등록된 사이트가 없습니다. 이 블록을 쓴 사이트가 있다면 알려 주세요.
재현 후기
v1 · 0개아직 이 버전의 프롬프트로 직접 만들어 본 사람이 없습니다. 첫 번째로 재현해 보세요.