Add UI/UX design philosophy document combining Palantir's information density and Toss's user-centric approach

This commit is contained in:
DDD1542
2026-04-03 15:51:33 +09:00
parent 1348ad118d
commit f92e8729ae
48 changed files with 9797 additions and 446 deletions

View File

@@ -0,0 +1,768 @@
/* ============================================================
ERP Preset Common CSS
- erp-node globals.css 기준 디자인 토큰
- Vivid Blue 테마 (NOT Indigo)
- Palantir 밀도 + Toss 친화
============================================================ */
/* ===== Google Fonts ===== */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
/* ===== Design Tokens (globals.css 동기화) ===== */
:root {
/* Light Theme (기본) */
--background: 0 0% 100%;
--foreground: 224 71% 4%;
--card: 0 0% 100%;
--card-foreground: 224 71% 4%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 0 0% 100%;
--secondary: 220 14.3% 95.9%;
--secondary-foreground: 220.9 39.3% 11%;
--muted: 220 14.3% 95.9%;
--muted-foreground: 220 8.9% 46.1%;
--accent: 220 14.3% 95.9%;
--accent-foreground: 220.9 39.3% 11%;
--destructive: 0 84.2% 60.2%;
--border: 220 13% 88%;
--input: 220 13% 88%;
--ring: 217.2 91.2% 59.8%;
--success: 142 76% 36%;
--warning: 38 92% 50%;
--info: 188 94% 43%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 8px;
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
}
/* Dark Theme (Palantir-Inspired Deep Navy) */
.dark {
--background: 222 47% 6%;
--foreground: 210 20% 95%;
--card: 220 40% 9%;
--card-foreground: 210 20% 95%;
--primary: 217 91% 65%;
--primary-foreground: 0 0% 100%;
--secondary: 220 25% 14%;
--secondary-foreground: 210 20% 90%;
--muted: 220 20% 13%;
--muted-foreground: 215 15% 58%;
--accent: 220 25% 16%;
--accent-foreground: 210 20% 90%;
--destructive: 0 72% 51%;
--border: 220 20% 18%;
--input: 220 20% 18%;
--ring: 217 91% 65%;
--success: 142 70% 42%;
--warning: 38 92% 55%;
--info: 188 90% 48%;
--chart-1: 220 70% 55%;
--chart-2: 160 60% 48%;
--chart-3: 30 80% 58%;
--chart-4: 280 65% 63%;
--chart-5: 340 75% 58%;
}
/* ===== Reset & Base ===== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: var(--font-sans);
font-size: 14px;
line-height: 1.5;
color: hsl(var(--foreground));
background: hsl(var(--background));
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* ===== 인터랙티브 요소 트랜지션 ===== */
button, a, input, textarea, select {
transition: color 150ms ease, background-color 150ms ease, border-color 150ms ease, box-shadow 150ms ease;
}
/* ===== 스크롤바 ===== */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: hsl(var(--muted)); }
::-webkit-scrollbar-thumb { background: hsl(var(--muted-foreground) / 0.3); border-radius: 5px; }
::-webkit-scrollbar-thumb:hover { background: hsl(var(--muted-foreground) / 0.5); }
* { scrollbar-width: thin; scrollbar-color: hsl(var(--muted-foreground) / 0.3) hsl(var(--muted)); }
/* ===== Layout ===== */
.page-container {
display: flex;
flex-direction: column;
height: 100vh;
padding: 16px;
gap: 12px;
overflow: hidden;
}
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 4px;
}
.page-title {
font-size: 18px;
font-weight: 700;
color: hsl(var(--foreground));
}
/* ===== 검색 필터 ===== */
.search-filter {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: hsl(var(--card));
border: 1px solid hsl(var(--border));
border-radius: var(--radius);
flex-wrap: wrap;
}
.search-filter .field-group {
display: flex;
align-items: center;
gap: 6px;
}
.search-filter label {
font-size: 12px;
font-weight: 600;
color: hsl(var(--muted-foreground));
white-space: nowrap;
}
.search-filter .actions {
display: flex;
gap: 6px;
margin-left: auto;
}
/* ===== 입력 필드 ===== */
input[type="text"], input[type="number"], input[type="date"],
input[type="search"], select, textarea {
height: 36px;
padding: 0 10px;
font-size: 13px;
font-family: var(--font-sans);
color: hsl(var(--foreground));
background: hsl(var(--background));
border: 1px solid hsl(var(--border));
border-radius: var(--radius-sm);
outline: none;
}
input:focus, select:focus, textarea:focus {
border-color: hsl(var(--ring));
box-shadow: 0 0 0 3px hsl(var(--ring) / 0.15);
}
input::placeholder { color: hsl(var(--muted-foreground) / 0.6); }
textarea { height: auto; padding: 8px 10px; min-height: 60px; resize: vertical; }
/* ===== 버튼 ===== */
.btn {
display: inline-flex;
align-items: center;
gap: 6px;
height: 36px;
padding: 0 14px;
font-size: 13px;
font-weight: 600;
font-family: var(--font-sans);
border-radius: var(--radius-sm);
cursor: pointer;
border: 1px solid transparent;
white-space: nowrap;
}
.btn-sm { height: 30px; padding: 0 10px; font-size: 12px; }
.btn-xs { height: 26px; padding: 0 8px; font-size: 11px; }
.btn-primary {
background: hsl(var(--primary));
color: hsl(var(--primary-foreground));
border-color: hsl(var(--primary));
}
.btn-primary:hover {
filter: brightness(1.1);
box-shadow: 0 2px 12px hsl(var(--primary) / 0.3);
}
.btn-secondary {
background: transparent;
color: hsl(var(--foreground));
border-color: hsl(var(--border));
}
.btn-secondary:hover {
background: hsl(var(--muted));
border-color: hsl(var(--border));
}
.btn-destructive {
background: hsl(var(--destructive) / 0.08);
color: hsl(var(--destructive));
border-color: hsl(var(--destructive) / 0.2);
}
.btn-destructive:hover {
background: hsl(var(--destructive) / 0.15);
}
.btn-ghost {
background: transparent;
color: hsl(var(--muted-foreground));
border: none;
}
.btn-ghost:hover { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
.btn svg { width: 16px; height: 16px; }
.btn-sm svg { width: 14px; height: 14px; }
/* ===== 카드 / 패널 ===== */
.card {
background: hsl(var(--card));
border: 1px solid hsl(var(--border));
border-radius: var(--radius);
overflow: hidden;
}
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
border-bottom: 1px solid hsl(var(--border));
}
.panel-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 600;
color: hsl(var(--foreground));
}
.panel-actions {
display: flex;
align-items: center;
gap: 6px;
}
/* ===== 뱃지 ===== */
.badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
font-size: 11px;
font-weight: 600;
border-radius: 4px;
white-space: nowrap;
}
.badge-count {
background: hsl(var(--muted));
color: hsl(var(--muted-foreground));
font-family: var(--font-mono);
font-size: 11px;
padding: 1px 8px;
border-radius: 10px;
}
.badge-primary { background: hsl(var(--primary) / 0.1); color: hsl(var(--primary)); }
.badge-success { background: hsl(var(--success) / 0.1); color: hsl(var(--success)); }
.badge-warning { background: hsl(var(--warning) / 0.1); color: hsl(var(--warning)); }
.badge-danger { background: hsl(var(--destructive) / 0.1); color: hsl(var(--destructive)); }
.badge-info { background: hsl(var(--info) / 0.1); color: hsl(var(--info)); }
.badge-muted { background: hsl(var(--muted)); color: hsl(var(--muted-foreground)); }
/* ===== 데이터 테이블 ===== */
.data-table-container {
flex: 1;
overflow: auto;
position: relative;
}
.data-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.data-table thead {
position: sticky;
top: 0;
z-index: 10;
}
.data-table th {
padding: 8px 12px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: hsl(var(--muted-foreground));
background: hsl(var(--muted));
border-bottom: 1px solid hsl(var(--border));
text-align: left;
white-space: nowrap;
user-select: none;
}
.data-table td {
padding: 8px 12px;
font-size: 13px;
color: hsl(var(--foreground));
border-bottom: 1px solid hsl(var(--border));
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.data-table tbody tr {
cursor: pointer;
transition: background-color 100ms ease;
}
.data-table tbody tr:hover {
background: hsl(var(--muted) / 0.5);
}
.data-table tbody tr.selected {
background: hsl(var(--primary) / 0.06);
border-left: 3px solid hsl(var(--primary));
}
.data-table tbody tr.selected td:first-child {
padding-left: 9px; /* 3px border 보상 */
}
/* 숫자 / 코드 셀 */
.cell-mono {
font-family: var(--font-mono);
font-size: 12px;
}
.cell-number {
font-family: var(--font-mono);
font-size: 12px;
text-align: right;
}
/* 체크박스 */
.data-table input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: hsl(var(--primary));
cursor: pointer;
}
/* ===== 리사이즈 핸들 (좌우 분할) ===== */
.resize-handle {
width: 6px;
cursor: col-resize;
background: hsl(var(--border));
transition: background 150ms ease;
flex-shrink: 0;
position: relative;
}
.resize-handle:hover,
.resize-handle.active {
background: hsl(var(--primary));
}
.resize-handle::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 2px;
height: 24px;
border-radius: 1px;
background: hsl(var(--muted-foreground) / 0.3);
}
/* ===== 탭 ===== */
.tabs-header {
display: flex;
border-bottom: 1px solid hsl(var(--border));
gap: 0;
}
.tab-btn {
padding: 10px 16px;
font-size: 13px;
font-weight: 500;
color: hsl(var(--muted-foreground));
background: none;
border: none;
border-bottom: 2px solid transparent;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: color 150ms, border-color 150ms;
}
.tab-btn:hover {
color: hsl(var(--foreground));
}
.tab-btn.active {
color: hsl(var(--foreground));
font-weight: 600;
border-bottom-color: hsl(var(--primary));
}
.tab-btn .tab-badge {
background: hsl(var(--primary) / 0.1);
color: hsl(var(--primary));
font-size: 11px;
font-weight: 600;
padding: 1px 7px;
border-radius: 10px;
font-family: var(--font-mono);
}
.tab-content { display: none; }
.tab-content.active { display: block; }
/* ===== 모달 / 다이얼로그 ===== */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 1040;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
pointer-events: none;
transition: opacity 200ms ease;
}
.modal-overlay.open {
opacity: 1;
pointer-events: auto;
}
.modal-content {
background: hsl(var(--card));
border: 1px solid hsl(var(--border));
border-radius: 12px;
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.2);
max-height: 85vh;
display: flex;
flex-direction: column;
transform: translateY(8px);
transition: transform 200ms ease;
}
.modal-overlay.open .modal-content {
transform: translateY(0);
}
/* 다크모드 모달 그림자 강화 */
.dark .modal-content {
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
}
.modal-sm { width: min(480px, 92vw); }
.modal-md { width: min(640px, 92vw); }
.modal-lg { width: min(900px, 95vw); }
.modal-xl { width: min(1200px, 95vw); }
.modal-full { width: 95vw; height: 90vh; }
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid hsl(var(--border));
}
.modal-header h2 {
font-size: 16px;
font-weight: 700;
}
.modal-body {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 12px 20px;
border-top: 1px solid hsl(var(--border));
}
/* 닫기 버튼 */
.modal-close {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-sm);
background: none;
border: none;
color: hsl(var(--muted-foreground));
cursor: pointer;
}
.modal-close:hover {
background: hsl(var(--muted));
color: hsl(var(--foreground));
}
/* ===== 확인 다이얼로그 ===== */
.confirm-dialog .modal-content {
text-align: center;
padding: 24px;
}
.confirm-icon {
width: 48px;
height: 48px;
margin: 0 auto 12px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.confirm-icon.warn {
background: hsl(var(--warning) / 0.1);
color: hsl(var(--warning));
}
.confirm-icon.danger {
background: hsl(var(--destructive) / 0.1);
color: hsl(var(--destructive));
}
.confirm-title {
font-size: 16px;
font-weight: 700;
margin-bottom: 8px;
}
.confirm-desc {
font-size: 13px;
color: hsl(var(--muted-foreground));
margin-bottom: 20px;
}
/* ===== 빈 상태 ===== */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
gap: 12px;
border: 2px dashed hsl(var(--border));
border-radius: var(--radius);
text-align: center;
}
.empty-state svg {
width: 40px;
height: 40px;
color: hsl(var(--muted-foreground) / 0.4);
}
.empty-state .empty-title {
font-size: 14px;
font-weight: 600;
color: hsl(var(--muted-foreground));
}
.empty-state .empty-desc {
font-size: 12px;
color: hsl(var(--muted-foreground) / 0.7);
}
/* ===== 폼 그리드 ===== */
.form-grid {
display: grid;
gap: 16px;
}
.form-grid-2 { grid-template-columns: 1fr 1fr; }
.form-grid-3 { grid-template-columns: 1fr 1fr 1fr; }
.form-grid-4 { grid-template-columns: 1fr 1fr 1fr 1fr; }
.form-group {
display: flex;
flex-direction: column;
gap: 4px;
}
.form-label {
font-size: 12px;
font-weight: 600;
color: hsl(var(--muted-foreground));
}
.form-label .required {
color: hsl(var(--destructive));
margin-left: 2px;
}
/* ===== 페이지네이션 ===== */
.pagination {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
border-top: 1px solid hsl(var(--border));
font-size: 12px;
color: hsl(var(--muted-foreground));
}
.pagination-info { font-family: var(--font-mono); }
.pagination-buttons {
display: flex;
gap: 2px;
}
.page-btn {
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-sm);
border: 1px solid hsl(var(--border));
background: none;
color: hsl(var(--muted-foreground));
font-size: 12px;
cursor: pointer;
}
.page-btn:hover { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
.page-btn.active { background: hsl(var(--primary)); color: white; border-color: hsl(var(--primary)); }
.page-btn:disabled { opacity: 0.3; cursor: not-allowed; }
/* ===== 상태 뱃지 (한글) ===== */
.status-확정, .status-사용, .status-정상, .status-완료 { background: hsl(var(--success) / 0.1); color: hsl(var(--success)); }
.status-진행중, .status-진행, .status-점검중 { background: hsl(var(--primary) / 0.1); color: hsl(var(--primary)); }
.status-대기, .status-교정예정 { background: hsl(var(--warning) / 0.1); color: hsl(var(--warning)); }
.status-취소, .status-미사용, .status-폐기, .status-수리중 { background: hsl(var(--destructive) / 0.1); color: hsl(var(--destructive)); }
/* ===== 유틸리티 ===== */
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.text-right { text-align: right; }
.text-center { text-align: center; }
.flex-1 { flex: 1; }
.gap-2 { gap: 8px; }
.gap-3 { gap: 12px; }
.gap-4 { gap: 16px; }
.mt-2 { margin-top: 8px; }
.mt-3 { margin-top: 12px; }
.mb-2 { margin-bottom: 8px; }
.hidden { display: none !important; }
/* ===== 테마 토글 ===== */
.theme-toggle {
position: fixed;
top: 12px;
right: 12px;
z-index: 9999;
width: 36px;
height: 36px;
border-radius: var(--radius-sm);
border: 1px solid hsl(var(--border));
background: hsl(var(--card));
color: hsl(var(--muted-foreground));
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.theme-toggle:hover {
background: hsl(var(--muted));
color: hsl(var(--foreground));
}
/* ===== 프로그레스 바 ===== */
.progress-bar {
height: 4px;
background: hsl(var(--muted));
border-radius: 2px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
border-radius: 2px;
transition: width 300ms ease;
}
.progress-green { background: hsl(var(--success)); }
.progress-yellow { background: hsl(var(--warning)); }
.progress-red { background: hsl(var(--destructive)); }
/* ===== 트리뷰 ===== */
.tree-node {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 13px;
transition: background 100ms;
}
.tree-node:hover { background: hsl(var(--muted) / 0.5); }
.tree-node.selected {
background: hsl(var(--primary) / 0.06);
border-left: 3px solid hsl(var(--primary));
}
.tree-toggle {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
color: hsl(var(--muted-foreground));
transition: transform 150ms;
}
.tree-toggle.expanded { transform: rotate(90deg); }
.tree-type-badge {
font-size: 10px;
font-weight: 600;
padding: 1px 6px;
border-radius: 3px;
}
/* ===== 애니메이션 ===== */
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeInUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes slideDown { from { opacity: 0; max-height: 0; } to { opacity: 1; max-height: 500px; } }
.animate-fadeIn { animation: fadeIn 200ms ease; }
.animate-fadeInUp { animation: fadeInUp 200ms ease; }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff