diff --git a/app/static/app.css b/app/static/app.css new file mode 100644 index 0000000..a773caa --- /dev/null +++ b/app/static/app.css @@ -0,0 +1,251 @@ +/* 커스텀 스타일 — design-ref.md (Toss 스타일) 기반. Pico 뒤에 로드해 변수 오버라이드. + 폰트는 폐쇄망 대비 로컬 동봉(static/fonts), CDN 의존 없음. */ + +@font-face { + font-family: "Pretendard Variable"; + font-weight: 45 920; + font-style: normal; + font-display: swap; + src: url("/static/fonts/PretendardVariable.woff2") format("woff2-variations"); +} + +:root { + /* palette (design-ref §2) */ + --bg: #ffffff; + --bg-alt: #f9fafb; + --surface: #f2f4f6; + --surface-2: #e8f3ff; + --text: #191f28; + --text-secondary: #333d4b; + --text-muted: #6b7684; + --text-dim: #8b95a1; + --border: #e5e8eb; + --border-strong: #d1d6db; + --accent: #3182f6; + --accent-hover: #1b64da; + --accent-deep: #1e40af; + --accent-soft: #e8f3ff; + /* 밝은 상태색은 옅은 배경 위 대비 위해 텍스트용으로 약간 어둡게 */ + --success-text: #00a37a; + --warning-text: #c5700a; + --danger: #f04452; + + --radius-card: 12px; + + /* Pico 매핑 */ + --pico-font-family-sans-serif: "Pretendard Variable", -apple-system, + BlinkMacSystemFont, "Apple SD Gothic Neo", "Segoe UI", "Noto Sans KR", + system-ui, sans-serif; + --pico-font-family: var(--pico-font-family-sans-serif); + --pico-line-height: 1.55; + --pico-border-radius: 7px; /* 버튼/인풋 (design-ref §4) */ + --pico-background-color: var(--bg-alt); + --pico-color: var(--text-secondary); + --pico-h1-color: var(--text); + --pico-h2-color: var(--text); + --pico-h3-color: var(--text); + --pico-muted-color: var(--text-muted); + --pico-primary: #ffffff; + --pico-primary-background: var(--accent); + --pico-primary-hover-background: var(--accent-hover); + --pico-primary-border: var(--accent); + --pico-primary-hover-border: var(--accent-hover); + --pico-card-background-color: var(--bg); + --pico-card-border-color: var(--border); + --pico-card-box-shadow: 0 1px 3px rgba(25, 31, 40, 0.04); +} + +body { + background: var(--bg-alt); + color: var(--text-secondary); +} + +main.container { + max-width: 1040px; + padding-block: 32px; +} + +h1, +h2, +h3 { + color: var(--text); + font-weight: 700; + letter-spacing: -0.01em; +} +h2 { + font-size: 20px; + margin: 0; +} + +section { + margin-bottom: 40px; +} + +/* 상단 내비 (design-ref §4 web top nav: white, 1px bottom border) */ +.app-nav { + background: var(--bg); + border-bottom: 1px solid var(--border); +} +.app-nav .container { + max-width: 1040px; + padding-block: 16px; +} +.app-nav .brand { + margin: 0; + font-size: 17px; + font-weight: 700; + color: var(--text); + letter-spacing: -0.01em; +} +.app-nav .brand .accent { + color: var(--accent); +} + +.section-head { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; +} + +/* 요약 카드 */ +.summary { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 16px; + margin-bottom: 24px; +} +.summary .card { + background: var(--bg); + border: 1px solid var(--border); + border-radius: var(--radius-card); + padding: 20px; + box-shadow: 0 1px 3px rgba(25, 31, 40, 0.04); +} +.summary .value { + font-size: 32px; + font-weight: 700; + color: var(--text); + line-height: 1.1; + font-variant-numeric: tabular-nums; /* 숫자 정렬 (design-ref §3) */ +} +.summary .label { + margin-top: 6px; + font-size: 13px; + font-weight: 500; + color: var(--text-muted); +} + +/* 저장소 표 (카드로 감싸기, hairline 행) */ +.table-card { + background: var(--bg); + border: 1px solid var(--border); + border-radius: var(--radius-card); + overflow: hidden; + box-shadow: 0 1px 3px rgba(25, 31, 40, 0.04); +} +.table-card table { + margin: 0; +} +.table-card thead th { + font-size: 12px; + font-weight: 600; + color: var(--text-dim); + text-transform: uppercase; + letter-spacing: 0.03em; + border-bottom: 1px solid var(--border); +} +.table-card tbody td { + border-bottom: 1px solid var(--border); + padding-block: 16px; + vertical-align: middle; +} +.table-card tbody tr:last-child td { + border-bottom: none; +} +.repo-name { + font-weight: 600; + color: var(--text); +} +.num { + font-variant-numeric: tabular-nums; +} +td.num, +th.num { + text-align: right; +} + +/* 검증 배지 칩 (dot + 라벨, 이모지 없음) */ +.badge { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + border-radius: 999px; + font-size: 13px; + font-weight: 500; + white-space: nowrap; +} +.badge::before { + content: ""; + width: 7px; + height: 7px; + border-radius: 50%; + background: currentColor; +} +.badge-pass { + color: var(--success-text); + background: rgba(0, 200, 150, 0.12); +} +.badge-warn { + color: var(--warning-text); + background: rgba(255, 149, 0, 0.13); +} +.badge-unset { + color: var(--text-muted); + background: var(--surface); +} + +/* 보조 버튼: 틴티드 블루 (design-ref §4 two-step blue) */ +button.secondary, +[role="button"].secondary, +a.secondary { + --pico-background-color: var(--surface-2); + --pico-border-color: var(--surface-2); + --pico-color: var(--accent-hover); + font-weight: 500; +} +button:disabled, +[role="button"]:disabled { + opacity: 0.45; +} + +/* 연결 상태 표시 (dot + 텍스트) */ +.status-line { + display: inline-flex; + align-items: center; + gap: 8px; + font-weight: 500; +} +.status-line::before { + content: ""; + width: 9px; + height: 9px; + border-radius: 50%; + background: currentColor; +} +.status-ok { + color: var(--success-text); +} +.status-bad { + color: var(--danger); +} +.status-loading { + color: var(--text-muted); +} + +@media (max-width: 640px) { + .summary { + grid-template-columns: repeat(2, 1fr); + } +} diff --git a/app/static/fonts/PretendardVariable.woff2 b/app/static/fonts/PretendardVariable.woff2 new file mode 100644 index 0000000..49c54b5 Binary files /dev/null and b/app/static/fonts/PretendardVariable.woff2 differ