fix(frontend): Badge 다크 팔레트를 translucent로 통일 + 쇼케이스 한/영 병기

- badgeVariants 다크 팔레트를 classes 기반 4종 카탈로그와 동일 패턴으로 통일
  - 이전: dark:bg-X-400 dark:text-slate-900 dark:border-X-600 (솔리드)
  - 이후: dark:bg-X-500/20 dark:text-X-400 dark:border-X-500/30 (translucent)
  - 라이트 팔레트는 그대로 유지 (이미 통일되어 있음)
- CatalogSection: 각 카탈로그 항목을 [code / 한글 배지 / 영문 배지] 3열 행으로 렌더
  - 한글/영문 라벨 두 버전을 한눈에 비교 검토 가능
  - 추적 ID Trk는 행 전체를 감싸서 호버/복사 동작
This commit is contained in:
htlee 2026-04-08 11:28:02 +09:00
부모 f589cb0f94
커밋 d7f8db88ee
2개의 변경된 파일53개의 추가작업 그리고 31개의 파일을 삭제

파일 보기

@ -43,10 +43,14 @@ interface AnyMeta {
type AnyCatalog = Record<string, AnyMeta>;
function getLabel(meta: AnyMeta): string {
function getKoLabel(meta: AnyMeta): string {
return meta.fallback?.ko ?? meta.label ?? meta.code;
}
function getEnLabel(meta: AnyMeta): string | undefined {
return meta.fallback?.en;
}
/** classes가 문자열인 경우 그대로, 객체인 경우 bg+text 조합 */
function getFallbackClasses(meta: AnyMeta): string | undefined {
if (typeof meta.classes === 'string') return meta.classes;
@ -56,31 +60,49 @@ function getFallbackClasses(meta: AnyMeta): string | undefined {
return undefined;
}
/** 카탈로그 항목을 Badge 또는 fallback span으로 렌더 */
function CatalogBadges({ catalog, idPrefix }: { catalog: AnyCatalog; idPrefix: string }) {
const entries = Object.values(catalog);
/** 단일 배지 렌더 — Badge(intent) 또는 span(classes) */
function renderBadge(meta: AnyMeta, label: string): ReactNode {
if (meta.intent) {
return (
<div className="flex flex-wrap gap-2">
{entries.map((meta) => {
const label = getLabel(meta);
const trkId = `${idPrefix}-${meta.code}`;
const content: ReactNode = meta.intent ? (
<Badge intent={meta.intent} size="sm">
{label}
</Badge>
) : (
<span
className={`inline-flex items-center px-2 py-0.5 rounded-md border text-[12px] font-semibold ${
);
}
const classes =
getFallbackClasses(meta) ??
'bg-slate-100 text-slate-700 border-slate-300 dark:bg-slate-500/20 dark:text-slate-300 dark:border-slate-500/30'
}`}
'bg-slate-100 text-slate-700 border-slate-300 dark:bg-slate-500/20 dark:text-slate-300 dark:border-slate-500/30';
return (
<span
className={`inline-flex items-center px-2 py-0.5 rounded-md border text-[12px] font-semibold ${classes}`}
>
{label}
</span>
);
}
/** 카탈로그 항목을 행 단위로 렌더: [code] [한글 배지] [영문 배지] */
function CatalogBadges({ catalog, idPrefix }: { catalog: AnyCatalog; idPrefix: string }) {
const entries = Object.values(catalog);
return (
<Trk key={meta.code} id={trkId} inline>
{content}
<div className="space-y-1.5">
{entries.map((meta) => {
const koLabel = getKoLabel(meta);
const enLabel = getEnLabel(meta);
const trkId = `${idPrefix}-${meta.code}`;
return (
<Trk key={meta.code} id={trkId} className="flex items-center gap-3 rounded-sm">
<code className="text-[10px] text-hint font-mono whitespace-nowrap w-32 shrink-0 truncate">
{meta.code}
</code>
<div className="flex-1">{renderBadge(meta, koLabel)}</div>
<div className="flex-1">
{enLabel ? (
renderBadge(meta, enLabel)
) : (
<span className="text-[10px] text-hint italic">(no en)</span>
)}
</div>
</Trk>
);
})}

파일 보기

@ -19,9 +19,9 @@ export const cardVariants = cva('rounded-xl border border-border', {
/** / 150+
*
* ( ):
* - ** **: (-400) + (slate-900) + (-600)
*
* ( + classes ):
* - ** **: (-500/20) + (-400) + (-500/30)
* (classes 4 )
* - ** **: (-100) + (-900) + (-300)
* ,
* -
@ -35,18 +35,18 @@ export const badgeVariants = cva(
variants: {
intent: {
critical:
'bg-red-100 text-red-900 border-red-300 dark:bg-red-400 dark:text-slate-900 dark:border-red-600',
high: 'bg-orange-100 text-orange-900 border-orange-300 dark:bg-orange-400 dark:text-slate-900 dark:border-orange-600',
'bg-red-100 text-red-900 border-red-300 dark:bg-red-500/20 dark:text-red-400 dark:border-red-500/30',
high: 'bg-orange-100 text-orange-900 border-orange-300 dark:bg-orange-500/20 dark:text-orange-400 dark:border-orange-500/30',
warning:
'bg-yellow-100 text-yellow-900 border-yellow-300 dark:bg-yellow-400 dark:text-slate-900 dark:border-yellow-600',
info: 'bg-blue-100 text-blue-900 border-blue-300 dark:bg-blue-400 dark:text-slate-900 dark:border-blue-600',
'bg-yellow-100 text-yellow-900 border-yellow-300 dark:bg-yellow-500/20 dark:text-yellow-400 dark:border-yellow-500/30',
info: 'bg-blue-100 text-blue-900 border-blue-300 dark:bg-blue-500/20 dark:text-blue-400 dark:border-blue-500/30',
success:
'bg-green-100 text-green-900 border-green-300 dark:bg-green-400 dark:text-slate-900 dark:border-green-600',
'bg-green-100 text-green-900 border-green-300 dark:bg-green-500/20 dark:text-green-400 dark:border-green-500/30',
muted:
'bg-slate-100 text-slate-800 border-slate-300 dark:bg-slate-400 dark:text-slate-900 dark:border-slate-600',
'bg-slate-100 text-slate-700 border-slate-300 dark:bg-slate-500/20 dark:text-slate-300 dark:border-slate-500/30',
purple:
'bg-purple-100 text-purple-900 border-purple-300 dark:bg-purple-400 dark:text-slate-900 dark:border-purple-600',
cyan: 'bg-cyan-100 text-cyan-900 border-cyan-300 dark:bg-cyan-400 dark:text-slate-900 dark:border-cyan-600',
'bg-purple-100 text-purple-900 border-purple-300 dark:bg-purple-500/20 dark:text-purple-400 dark:border-purple-500/30',
cyan: 'bg-cyan-100 text-cyan-900 border-cyan-300 dark:bg-cyan-500/20 dark:text-cyan-400 dark:border-cyan-500/30',
},
// rem 기반 — root font-size 대비 비율, 화면 비율 변경 시 자동 조정
// xs ≈ 11px, sm ≈ 12px, md ≈ 13px, lg ≈ 14px (root 14px 기준)