nnuer
강희태@gmlxo9917

기본 팔레트·시맨틱 토큰·사용 원칙을 담은 컬러 시스템 문서

콘텐츠 블록

7가지 색상의 50~900 단계 스와치, 브랜드·텍스트·상태·서피스·사이드바 시맨틱 토큰, 사용 원칙 카드 3개를 보여 주는 디자인 시스템 컬러 문서 섹션입니다. 스와치를 누르면 색 코드가 복사됩니다.

블록 신고

운영자가 확인한 뒤 필요하면 블록을 숨깁니다.

사유
Claude Code · 모름 · React (TSX)
결과 코드펼치기
import { useState } from "react";

const FONT_URL =
  "https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=Gothic+A1:wght@400;500;600;700&display=swap";

const STEPS = ["50", "100", "200", "300", "400", "500", "600", "700", "800", "900"];

const PRIMITIVES: { name: string; hex: string[] }[] = [
  { name: "Gray", hex: ["#F9FAFB", "#F3F4F6", "#E5E7EB", "#D1D5DB", "#9CA3AF", "#6B7280", "#4B5563", "#374151", "#1F2937", "#111827"] },
  { name: "Blue", hex: ["#EFF6FF", "#DBEAFE", "#BFDBFE", "#93C5FD", "#60A5FA", "#3B82F6", "#2563EB", "#1D4ED8", "#1E40AF", "#1E3A8A"] },
  { name: "Purple", hex: ["#FAF5FF", "#F3E8FF", "#E9D5FF", "#D8B4FE", "#C084FC", "#A855F7", "#9333EA", "#7E22CE", "#6B21A8", "#4C1D95"] },
  { name: "Green", hex: ["#F0FDF4", "#DCFCE7", "#BBF7D0", "#86EFAC", "#4ADE80", "#22C55E", "#16A34A", "#15803D", "#166534", "#14532D"] },
  { name: "Red", hex: ["#FEF2F2", "#FEE2E2", "#FECACA", "#FCA5A5", "#F87171", "#EF4444", "#DC2626", "#B91C1C", "#991B1B", "#7F1D1D"] },
  { name: "Yellow", hex: ["#FEFCE8", "#FEF9C3", "#FEF08A", "#FDE047", "#FACC15", "#EAB308", "#CA8A04", "#A16207", "#854D0E", "#713F12"] },
  { name: "Indigo", hex: ["#EEF2FF", "#E0E7FF", "#C7D2FE", "#A5B4FC", "#818CF8", "#6366F1", "#4F46E5", "#4338CA", "#3730A3", "#121224"] },
];

const PILL_GROUPS: { title: string; items: { name: string; hex: string; dark?: boolean }[] }[] = [
  {
    title: "Brand",
    items: [
      { name: "primary", hex: "#5B8DEF" },
      { name: "primary-hover", hex: "#3B82F6" },
      { name: "accent", hex: "#AD7BFF" },
    ],
  },
  {
    title: "Text",
    items: [
      { name: "primary", hex: "#111827" },
      { name: "secondary", hex: "#6B7280" },
      { name: "tertiary", hex: "#9CA3AF" },
      { name: "disabled", hex: "#D1D5DB", dark: true },
    ],
  },
];

const STATUS = [
  { name: "success", hex: "#27AE60" },
  { name: "error", hex: "#FF4D4F" },
  { name: "warning", hex: "#F2C94C" },
  { name: "info", hex: "#5B8DEF" },
];

const SURFACE = [
  { name: "page", hex: "#F9FAFB" },
  { name: "card", hex: "#F3F4F6" },
  { name: "subtle", hex: "#E5E7EB" },
];

const SIDEBAR = [
  { name: "bg", hex: "#121224" },
  { name: "surface", hex: "#1A1D1F" },
  { name: "active", hex: "#383D61" },
  { name: "accent", hex: "#595E8C" },
  { name: "muted", hex: "#A5B4FC" },
];

const RULES = [
  {
    title: "Primitive → Semantic 참조",
    body: "실제 디자인 작업에는 항상 Semantic 토큰을 사용하십시오. Primitive 색상을 직접 참조하는 것은 금지되며, 이는 테마 변경 및 유지보수를 어렵게 합니다.",
  },
  {
    title: "Brand vs Accent 구분",
    body: "Brand/Primary는 주요 CTA(Call to Action)와 핵심 인터랙션 요소에 사용합니다. Accent는 보조적인 강조나 특정 기능적 상태를 나타낼 때 제한적으로 사용합니다.",
  },
  {
    title: "Sidebar는 별도 스케일",
    body: "사이드바와 같은 특정 주요 레이아웃 영역은 Indigo 다크 스케일 전용 토큰을 사용합니다. 이는 일반적인 콘텐츠 영역과의 시각적 계층 구조를 명확히 분리하기 위함입니다.",
  },
];

function SectionTitle({ title, desc }: { title: string; desc: string }) {
  return (
    <div className="flex flex-col gap-2">
      <h3 className="border-l-4 border-[#6366f1] pl-4 text-[18px] font-bold leading-normal text-[#111827] sm:text-[20px]">{title}</h3>
      <p className="text-[14px] leading-normal text-[#6b7280]">{desc}</p>
    </div>
  );
}

function GroupLabel({ children }: { children: string }) {
  return <p className="font-['Inter',sans-serif] text-[14px] font-bold uppercase leading-normal text-[#9ca3af]">{children}</p>;
}

export default function ColorSystemDoc() {
  const [copied, setCopied] = useState("");

  const copy = async (hex: string) => {
    try {
      await navigator.clipboard.writeText(hex);
      setCopied(hex);
    } catch {
      setCopied("");
      return;
    }
    window.setTimeout(() => setCopied((c) => (c === hex ? "" : c)), 1200);
  };

  const swatchButton = "transition-transform duration-150 hover:-translate-y-0.5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#6366f1]";

  return (
    <section className="w-full bg-white px-4 py-12 font-['Geist',sans-serif] sm:px-10 sm:py-20 lg:px-20">
      <link rel="stylesheet" href={FONT_URL} precedence="default" />
      <div className="mx-auto flex w-full max-w-[1474px] flex-col gap-16 sm:gap-[100px]">
        <h2 className="text-[24px] font-bold leading-normal text-[#111827] sm:text-[28px]">05 · COLOR SYSTEM</h2>

        <div className="flex flex-col gap-10 sm:gap-[60px]">
          <SectionTitle title="Primitive Scale (기본 색상 스케일)" desc="디자인 시스템의 기초가 되는 7가지 주요 색상 팔레트입니다." />
          <div className="grid grid-cols-2 gap-x-6 gap-y-10 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-7">
            {PRIMITIVES.map((p) => (
              <div key={p.name} className="flex flex-col gap-4">
                <p className="text-[14px] font-bold leading-normal text-[#111827]">{p.name}</p>
                <div className="flex flex-col gap-3">
                  {p.hex.map((hex, i) => (
                    <div key={hex + i} className="flex items-center gap-3">
                      <span className="w-6 shrink-0 text-right text-[10px] leading-normal text-[#9ca3af]">{STEPS[i]}</span>
                      <button type="button" onClick={() => copy(hex)} className={`flex min-w-0 flex-1 flex-col gap-1 text-left ${swatchButton}`} aria-label={`${p.name} ${STEPS[i]} ${hex} 복사`}>
                        <span className="h-10 w-full rounded-[4px] border border-[#e5e7eb]" style={{ background: hex }} />
                        <span className="text-[11px] leading-normal text-[#9ca3af]">{copied === hex ? "복사됨" : hex}</span>
                      </button>
                    </div>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </div>

        <div className="flex flex-col gap-10 sm:gap-[60px]">
          <SectionTitle title="Semantic Tokens (시멘틱 토큰)" desc="기능과 용도에 따라 정의된 색상 토큰입니다." />
          <div className="flex flex-col gap-10">
            {PILL_GROUPS.map((g) => (
              <div key={g.title} className="flex flex-col gap-3">
                <GroupLabel>{g.title}</GroupLabel>
                <div className="flex flex-wrap gap-2">
                  {g.items.map((it) => (
                    <button
                      key={it.name}
                      type="button"
                      onClick={() => copy(it.hex)}
                      className={`flex items-center gap-2 rounded-[99px] px-3 py-1.5 text-[12px] leading-normal ${swatchButton}`}
                      style={{ background: it.hex }}
                    >
                      <span className={`font-semibold ${it.dark ? "text-[#4b5563]" : "text-white"}`}>{it.name}</span>
                      <span className={`text-[11px] ${it.dark ? "text-[#6b7280]" : "text-white/60"}`}>{copied === it.hex ? "복사됨" : it.hex}</span>
                    </button>
                  ))}
                </div>
              </div>
            ))}

            <div className="flex flex-col gap-3">
              <GroupLabel>Status</GroupLabel>
              <div className="flex flex-wrap gap-2">
                {STATUS.map((it) => (
                  <button
                    key={it.name}
                    type="button"
                    onClick={() => copy(it.hex)}
                    className={`flex items-center gap-2 rounded-md border border-[#e5e7eb] bg-white px-3 py-1.5 text-[12px] leading-normal ${swatchButton}`}
                  >
                    <span className="h-3.5 w-[3px] rounded-full" style={{ background: it.hex }} />
                    <span className="font-semibold text-[#111827]">{it.name}</span>
                    <span className="text-[11px] text-[#9ca3af]">{copied === it.hex ? "복사됨" : it.hex}</span>
                  </button>
                ))}
              </div>
            </div>

            <div className="grid grid-cols-1 gap-10 lg:grid-cols-[auto_1fr] lg:gap-16">
              <div className="flex flex-col gap-4">
                <GroupLabel>Surface</GroupLabel>
                <div className="flex flex-wrap gap-5">
                  {SURFACE.map((it) => (
                    <button key={it.name} type="button" onClick={() => copy(it.hex)} className={`flex w-[140px] flex-col gap-3 text-left ${swatchButton}`}>
                      <span className="h-20 w-full rounded-lg border border-[#e5e7eb]" style={{ background: it.hex }} />
                      <span className="flex flex-col gap-0.5 leading-normal">
                        <span className="text-[13px] font-bold text-[#111827]">{it.name}</span>
                        <span className="text-[11px] text-[#6b7280]">{copied === it.hex ? "복사됨" : it.hex}</span>
                      </span>
                    </button>
                  ))}
                </div>
              </div>
              <div className="flex flex-col gap-4">
                <GroupLabel>Sidebar</GroupLabel>
                <div className="flex flex-wrap gap-5">
                  {SIDEBAR.map((it) => (
                    <button key={it.name} type="button" onClick={() => copy(it.hex)} className={`flex w-[140px] flex-col gap-3 text-left ${swatchButton}`}>
                      <span className="h-20 w-full rounded-lg border border-[#e5e7eb]" style={{ background: it.hex }} />
                      <span className="flex flex-col gap-0.5 leading-normal">
                        <span className="text-[13px] font-bold text-[#111827]">{it.name}</span>
                        <span className="text-[11px] text-[#6b7280]">{copied === it.hex ? "복사됨" : it.hex}</span>
                      </span>
                    </button>
                  ))}
                </div>
              </div>
            </div>
          </div>
        </div>

        <div className="flex flex-col gap-10 sm:gap-[60px]">
          <SectionTitle title="Usage Rules (사용 원칙)" desc="일관된 사용자 경험을 위한 색상 사용 가이드라인입니다." />
          <div className="grid grid-cols-1 gap-6 md:grid-cols-3">
            {RULES.map((r) => (
              <div key={r.title} className="flex flex-col gap-3 rounded-xl border border-[#e5e7eb] bg-[#f9fafb] p-6">
                <p className="text-[14px] font-bold leading-normal text-[#111827]">{r.title}</p>
                <p className="font-['Gothic_A1',sans-serif] text-[13px] leading-[1.6] text-[#6b7280]">{r.body}</p>
              </div>
            ))}
          </div>
        </div>

        <div className="flex flex-wrap items-center justify-between gap-4 border-t border-[#e5e7eb] pt-10">
          <p className="text-[13px] leading-normal text-[#9ca3af]">© 2026 Leafnote Design Team</p>
          <p className="flex items-center gap-2 text-[13px] font-semibold leading-normal text-[#6366f1]">
            <span className="size-3 rounded-[2px] bg-[#6366f1]" aria-hidden="true" />
            Leafnote Design
          </p>
        </div>
        <p className="sr-only" aria-live="polite">
          {copied ? `${copied} 복사됨` : ""}
        </p>
      </div>
    </section>
  );
}

적용 사이트 0

이 블록이 실제로 쓰인 사이트입니다.

로그인하고 내 사이트 추가

아직 등록된 사이트가 없습니다. 이 블록을 쓴 사이트가 있다면 알려 주세요.

로그인하고 재현 결과 올리기

재현 후기

v1 · 0개

아직 이 버전의 프롬프트로 직접 만들어 본 사람이 없습니다. 첫 번째로 재현해 보세요.