사용량 카드가 달린 업무용 앱 사이드바
내비게이션 블록로고, 아이콘 메뉴 8개, 하단 워크스페이스 사용량 카드(저장공간·AI 크레딧 막대, 업그레이드 버튼)가 있는 흰색 세로 사이드바입니다. 모바일에서는 메뉴 버튼으로 접고 펼칩니다.
Claude Code · 모름 · React (TSX)
결과 코드펼치기
import { useState } from "react";
const FONT_URL = "https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&display=swap";
type IconProps = { className?: string };
function Svg({ className, children }: IconProps & { children: React.ReactNode }) {
return (
<svg className={className} width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
{children}
</svg>
);
}
const icons = {
logo: (p: IconProps) => (
<Svg {...p}>
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" />
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" />
</Svg>
),
dashboard: (p: IconProps) => (
<Svg {...p}>
<rect x="3" y="3" width="7" height="9" rx="1" />
<rect x="14" y="3" width="7" height="5" rx="1" />
<rect x="14" y="12" width="7" height="9" rx="1" />
<rect x="3" y="16" width="7" height="5" rx="1" />
</Svg>
),
notes: (p: IconProps) => (
<Svg {...p}>
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
</Svg>
),
cpu: (p: IconProps) => (
<Svg {...p}>
<rect x="4" y="4" width="16" height="16" rx="2" />
<rect x="9" y="9" width="6" height="6" />
<path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3" />
</Svg>
),
layers: (p: IconProps) => (
<Svg {...p}>
<path d="M12 2 2 7l10 5 10-5-10-5z" />
<path d="m2 17 10 5 10-5" />
<path d="m2 12 10 5 10-5" />
</Svg>
),
chart: (p: IconProps) => (
<Svg {...p}>
<path d="M12 20V10M18 20V4M6 20v-4" />
</Svg>
),
printer: (p: IconProps) => (
<Svg {...p}>
<path d="M6 9V2h12v7" />
<path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" />
<rect x="6" y="14" width="12" height="8" />
</Svg>
),
users: (p: IconProps) => (
<Svg {...p}>
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" />
</Svg>
),
bell: (p: IconProps) => (
<Svg {...p}>
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
<path d="M13.73 21a2 2 0 0 1-3.46 0" />
</Svg>
),
menu: (p: IconProps) => (
<Svg {...p}>
<path d="M3 6h18M3 12h18M3 18h18" />
</Svg>
),
};
const NAV = [
{ id: "dashboard", label: "대시보드", icon: icons.dashboard },
{ id: "notes", label: "노트 관리", icon: icons.notes },
{ id: "ai", label: "AI 도구", icon: icons.cpu },
{ id: "assets", label: "콘텐츠 & 에셋", icon: icons.layers },
{ id: "analytics", label: "분석", icon: icons.chart },
{ id: "print", label: "인쇄", icon: icons.printer },
{ id: "members", label: "멤버 & 권한", icon: icons.users },
{ id: "alerts", label: "알림", icon: icons.bell },
];
const RAINBOW =
"linear-gradient(90deg, #ffcccc 0%, #ffd6c4 10%, #ffe5bc 20%, #f6ffbc 30%, #d7ffc3 40%, #b6ffec 50%, #bbf7ff 60%, #b7bdff 70%, #d3adff 80%, #ffb6e0 90%, #ffb6b7 95%, #ffdac2 100%)";
function UsageBar({ label, value, fill }: { label: string; value: string; fill: string }) {
return (
<div className="flex flex-col gap-1.5">
<div className="flex items-start justify-between text-[11px] leading-normal">
<span className="font-normal text-[#6f767e]">{label}</span>
<span className="font-semibold text-[#1a1d1f]">{value}</span>
</div>
<div className="h-1.5 w-full overflow-hidden rounded-[3px] bg-[#efefef]" role="meter" aria-label={label} aria-valuemin={0} aria-valuemax={100} aria-valuenow={100}>
<div className="h-full w-full" style={{ background: fill }} />
</div>
</div>
);
}
export default function AppSidebar() {
const [active, setActive] = useState("dashboard");
const [open, setOpen] = useState(true);
return (
<section className="w-full bg-[#f4f5f7] p-4 font-['Geist',sans-serif] sm:p-6">
<link rel="stylesheet" href={FONT_URL} precedence="default" />
<div className="mx-auto flex w-full max-w-[1200px] flex-col gap-4 md:flex-row">
<aside className="flex w-full flex-col justify-between rounded-2xl border-r border-[#efefef] bg-white px-4 py-6 md:min-h-[760px] md:w-[220px] md:shrink-0 md:py-8">
<div className="flex flex-col gap-8">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2 pl-2">
<span className="flex size-8 items-center justify-center rounded-lg bg-[#5b8def] text-white">
<icons.logo />
</span>
<span className="text-[18px] font-bold leading-normal text-[#1a1d1f]">Leafnote</span>
</div>
<button
type="button"
onClick={() => setOpen((v) => !v)}
aria-expanded={open}
aria-controls="sidebar-panel"
aria-label="메뉴 열기"
className="rounded-lg p-2 text-[#6f767e] hover:bg-[#f7f8fa] focus-visible:outline-2 focus-visible:outline-[#5b8def] md:hidden"
>
<icons.menu />
</button>
</div>
<nav id="sidebar-panel" aria-label="주요 메뉴" className={`${open ? "flex" : "hidden"} flex-col gap-1 md:flex`}>
{NAV.map((item) => {
const isActive = active === item.id;
const Icon = item.icon;
return (
<button
key={item.id}
type="button"
onClick={() => setActive(item.id)}
aria-current={isActive ? "page" : undefined}
className={`flex w-full items-center gap-3 rounded-xl px-4 py-3 text-left text-[14px] leading-normal transition-colors duration-150 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#5b8def] ${
isActive ? "bg-[#5b8def] font-semibold text-white" : "font-medium text-[#6f767e] hover:bg-[#f7f8fa] hover:text-[#1a1d1f]"
}`}
>
<Icon className="shrink-0" />
<span className="flex-1">{item.label}</span>
</button>
);
})}
</nav>
</div>
<div className={`${open ? "flex" : "hidden"} mt-8 flex-col gap-3 rounded-2xl bg-[#f7f8fa] p-4 md:flex`}>
<div className="flex items-center justify-between">
<span className="text-[13px] font-semibold leading-normal text-[#1a1d1f]">Demo Workspace</span>
<span className="rounded px-2 py-0.5 text-[11px] font-semibold leading-normal text-[#27ae60]" style={{ background: "rgba(39,174,96,0.08)" }}>
PRO
</span>
</div>
<UsageBar label="저장공간" value="14.8K / 14.8K" fill="#5b8def" />
<UsageBar label="AI 크레딧" value="14.8K / 14.8K" fill={RAINBOW} />
<button
type="button"
className="w-full rounded-lg bg-[#5b8def] py-2 text-[13px] font-semibold leading-normal text-white transition-colors hover:bg-[#3b82f6] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#5b8def]"
>
업그레이드
</button>
</div>
</aside>
<div className="hidden flex-1 rounded-2xl border border-dashed border-[#e5e7eb] md:block" aria-hidden="true" />
</div>
</section>
);
}
적용 사이트 0
이 블록이 실제로 쓰인 사이트입니다.
아직 등록된 사이트가 없습니다. 이 블록을 쓴 사이트가 있다면 알려 주세요.
재현 후기
v1 · 0개아직 이 버전의 프롬프트로 직접 만들어 본 사람이 없습니다. 첫 번째로 재현해 보세요.