|
| 1 | +--- |
| 2 | +version: alpha |
| 3 | +name: CLHS Score |
| 4 | +description: Design system for the CLHS Score web app, Android app, and demo site. |
| 5 | +colors: |
| 6 | + primary: "#1565C0" |
| 7 | + primaryDark: "#82B1FF" |
| 8 | + primaryWeb: "#2D5DAA" |
| 9 | + primaryDemo: "#4A9EFF" |
| 10 | + onPrimary: "#FFFFFF" |
| 11 | + primaryContainer: "#BBDEFB" |
| 12 | + onPrimaryContainer: "#002171" |
| 13 | + secondary: "#4F6354" |
| 14 | + secondaryDark: "#B6CCBB" |
| 15 | + secondaryContainer: "#D2E8D7" |
| 16 | + tertiary: "#33618D" |
| 17 | + success: "#126738" |
| 18 | + successDark: "#81C995" |
| 19 | + warning: "#946C00" |
| 20 | + warningDark: "#FDE293" |
| 21 | + danger: "#BA1A1A" |
| 22 | + dangerDark: "#FFB4AB" |
| 23 | + backgroundLight: "#FBFCFF" |
| 24 | + backgroundDark: "#121212" |
| 25 | + backgroundWebDark: "#111318" |
| 26 | + demoBackground: "#0A0A0F" |
| 27 | + surfaceLight: "#FBFCFF" |
| 28 | + surfaceDark: "#121212" |
| 29 | + surfaceWebDark: "#111318" |
| 30 | + surfaceElevatedLight: "#FFFFFF" |
| 31 | + surfaceElevatedDark: "#1E2025" |
| 32 | + surfaceContainerLight: "#EEF2F6" |
| 33 | + surfaceContainerDark: "#1E2225" |
| 34 | + surfaceVariantLight: "#DDE3EA" |
| 35 | + surfaceVariantDark: "#43474E" |
| 36 | + textLight: "#1A1C1E" |
| 37 | + textDark: "#E2E2E6" |
| 38 | + textWebDark: "#E1E2E8" |
| 39 | + textSecondaryLight: "#43474E" |
| 40 | + textSecondaryDark: "#C3C7CF" |
| 41 | + outlineLight: "#73777F" |
| 42 | + outlineDark: "#8D9199" |
| 43 | + outlineVariantLight: "#C3C7CF" |
| 44 | + outlineVariantDark: "#43474E" |
| 45 | +typography: |
| 46 | + display: |
| 47 | + fontFamily: Roboto |
| 48 | + fontSize: 3rem |
| 49 | + fontWeight: 700 |
| 50 | + lineHeight: 1.1 |
| 51 | + letterSpacing: 0 |
| 52 | + title-lg: |
| 53 | + fontFamily: Roboto |
| 54 | + fontSize: 1.375rem |
| 55 | + fontWeight: 600 |
| 56 | + lineHeight: 1.25 |
| 57 | + letterSpacing: 0 |
| 58 | + title-md: |
| 59 | + fontFamily: Roboto |
| 60 | + fontSize: 1.125rem |
| 61 | + fontWeight: 600 |
| 62 | + lineHeight: 1.35 |
| 63 | + letterSpacing: 0 |
| 64 | + body: |
| 65 | + fontFamily: Roboto |
| 66 | + fontSize: 1rem |
| 67 | + fontWeight: 400 |
| 68 | + lineHeight: 1.5 |
| 69 | + letterSpacing: 0 |
| 70 | + body-zh: |
| 71 | + fontFamily: Noto Sans TC |
| 72 | + fontSize: 1rem |
| 73 | + fontWeight: 400 |
| 74 | + lineHeight: 1.65 |
| 75 | + letterSpacing: 0 |
| 76 | + label: |
| 77 | + fontFamily: Roboto |
| 78 | + fontSize: 0.8125rem |
| 79 | + fontWeight: 600 |
| 80 | + lineHeight: 1.25 |
| 81 | + letterSpacing: 0 |
| 82 | + numeric: |
| 83 | + fontFamily: Roboto |
| 84 | + fontSize: 1rem |
| 85 | + fontWeight: 600 |
| 86 | + lineHeight: 1.3 |
| 87 | + letterSpacing: 0 |
| 88 | + fontFeature: tnum |
| 89 | +rounded: |
| 90 | + sm: 8px |
| 91 | + md: 16px |
| 92 | + lg: 24px |
| 93 | + xl: 32px |
| 94 | + pill: 9999px |
| 95 | +spacing: |
| 96 | + xs: 4px |
| 97 | + sm: 8px |
| 98 | + md: 16px |
| 99 | + lg: 24px |
| 100 | + xl: 32px |
| 101 | + section: 64px |
| 102 | +components: |
| 103 | + button-primary: |
| 104 | + backgroundColor: "{colors.primary}" |
| 105 | + textColor: "{colors.onPrimary}" |
| 106 | + typography: "{typography.label}" |
| 107 | + rounded: "{rounded.md}" |
| 108 | + padding: 14px |
| 109 | + button-primary-hover: |
| 110 | + backgroundColor: "{colors.primaryWeb}" |
| 111 | + textColor: "{colors.onPrimary}" |
| 112 | + rounded: "{rounded.md}" |
| 113 | + card: |
| 114 | + backgroundColor: "{colors.surfaceContainerLight}" |
| 115 | + textColor: "{colors.textLight}" |
| 116 | + rounded: "{rounded.md}" |
| 117 | + padding: 16px |
| 118 | + card-dark: |
| 119 | + backgroundColor: "{colors.surfaceContainerDark}" |
| 120 | + textColor: "{colors.textDark}" |
| 121 | + rounded: "{rounded.md}" |
| 122 | + padding: 16px |
| 123 | + hero-card: |
| 124 | + backgroundColor: "{colors.primaryContainer}" |
| 125 | + textColor: "{colors.onPrimaryContainer}" |
| 126 | + rounded: "{rounded.lg}" |
| 127 | + padding: 24px |
| 128 | + chip: |
| 129 | + backgroundColor: "{colors.surfaceVariantLight}" |
| 130 | + textColor: "{colors.textSecondaryLight}" |
| 131 | + typography: "{typography.label}" |
| 132 | + rounded: "{rounded.pill}" |
| 133 | + padding: 8px |
| 134 | + chart-primary: |
| 135 | + backgroundColor: "{colors.primary}" |
| 136 | + textColor: "{colors.onPrimary}" |
| 137 | + rounded: "{rounded.sm}" |
| 138 | + size: 12px |
| 139 | +--- |
| 140 | + |
| 141 | +## Overview |
| 142 | + |
| 143 | +壢中成績的介面是「安靜、可信、可重複使用的成績工具」,不是行銷式儀表板。設計重點是讓學生快速確認平均、排名、科目落點與趨勢,因此資訊層級、數字可讀性和狀態辨識要優先於裝飾。 |
| 144 | + |
| 145 | +Web 與 Android app 採 Material 3 語彙:藍色作為主要行動與資料焦點,綠色處理成長/優勢,黃橘色處理提醒,紅色只用於錯誤或低分風險。Demo site 可以更有展示感,但仍要延續深色、藍色焦點與清楚的 app icon 訊號。 |
| 146 | + |
| 147 | +## Colors |
| 148 | + |
| 149 | +色彩以 Material 3 的 light/dark scheme 為核心。Android 的 `ScoreTheme.kt` 是 app 端主要來源;Web 的 `web/frontend/styles/tokens.css` 是網頁端主要來源。 |
| 150 | + |
| 151 | +- **Primary** 用於主要 CTA、選取狀態、目前資料點、進度與重點數字。 |
| 152 | +- **Surface** 用於卡片、底部導覽、彈窗與表格容器;不要把大面積內容做成過亮或過飽和。 |
| 153 | +- **Success / warning / danger** 只表達成績語意或系統狀態,不應當作裝飾色。 |
| 154 | +- **Demo background** 可以使用接近黑色的深色背景與白色粒子,但粒子不可遮擋文字或 icon。 |
| 155 | + |
| 156 | +## Typography |
| 157 | + |
| 158 | +Android 使用 Material 3 預設 Roboto,Web 使用 `Roboto` + `Noto Sans TC`,Demo 使用 `Outfit` + `Noto Sans TC`。新介面若未明確屬於 demo,預設使用 Roboto / Noto Sans TC。 |
| 159 | + |
| 160 | +數字欄位、平均、排名、百分比、分數差距必須使用 tabular numbers 或等寬數字特性,避免資料更新時水平跳動。中文說明文字使用較寬鬆 line-height,標題保持短句,不做過度行銷化文案。 |
| 161 | + |
| 162 | +## Layout |
| 163 | + |
| 164 | +畫面應以「總覽 → 排名/平均 → 科目細節 → 圖表/趨勢 → 操作」的閱讀順序組織。手機 app 優先單欄、卡片堆疊;Web 可使用兩欄或卡片 grid,但不要讓同一層級資訊密度過高。 |
| 165 | + |
| 166 | +常用間距以 8px 系列建立:8px 用於小元素內距,16px 用於卡片內容,24px 用於卡片群組,32px 以上用於區塊分隔。主要內容容器在 Web 上維持可掃讀寬度,避免滿版拉長成績表格。 |
| 167 | + |
| 168 | +## Elevation & Depth |
| 169 | + |
| 170 | +深色模式偏向低陰影、靠 surface 色階與細邊框建立層級。Light mode 可以使用柔和陰影,但卡片不應浮得太重。Demo 首屏可使用 glow、粒子與 icon shadow,但產品內頁要避免大面積裝飾光暈。 |
| 171 | + |
| 172 | +互動狀態以 subtle background、outline、primary tint 或 1-2px 位移呈現;不要用大幅縮放造成資料版面跳動。 |
| 173 | + |
| 174 | +## Shapes |
| 175 | + |
| 176 | +標準卡片使用 16px 圓角,重要摘要卡可用 24px。小標籤、分布條、chip 使用 pill 或 9999px。App icon 保持圓角方形,不要把產品 UI 裡的資料卡做成過度有機或不規則形狀。 |
| 177 | + |
| 178 | +圖表中的 bar、legend marker、distribution segment 可用 4-12px 小圓角;表格列與資料行保持穩定高度,避免 hover 改變布局。 |
| 179 | + |
| 180 | +## Components |
| 181 | + |
| 182 | +Primary button 用於登入、同步、下載、重新載入與主要確認動作;同一畫面只保留一個最強 primary CTA。次要動作使用 tonal、outline 或 text button。 |
| 183 | + |
| 184 | +成績卡應包含科目名稱、分數、班平均/標準差等必要上下文;高低分顏色要搭配文字標籤,不可只靠顏色傳達。排名與百分比要同時顯示目前值與總數或 PR 說明。 |
| 185 | + |
| 186 | +圖表卡要優先讓資料可讀:清楚 legend、足夠 label 空間、淡色 grid、強調我的成績與班級平均。Radar、bar、distribution chart 應共享同一套 primary/outline/semantic colors。 |
| 187 | + |
| 188 | +Modal 用於登入、分享、免責與確認流程。Modal 文字要短,表單錯誤用 inline feedback,避免清空使用者已輸入內容。 |
| 189 | + |
| 190 | +## Do's and Don'ts |
| 191 | + |
| 192 | +Do: |
| 193 | +- 保持工具感:資訊密度可以高,但層級要清楚。 |
| 194 | +- 使用 Material 3 color roles 和既有 tokens,不臨時創造近似藍色。 |
| 195 | +- 讓平均、排名、百分比等數字對齊且容易比較。 |
| 196 | +- 在 demo 或行銷頁可加入動態背景,但互動效果必須在內容層之後。 |
| 197 | + |
| 198 | +Don't: |
| 199 | +- 不要把學生資料、帳密、token 或正式環境 secret 寫進 UI、文件範例或截圖。 |
| 200 | +- 不要用單一顏色家族鋪滿整個 app;藍色是焦點,不是背景全部。 |
| 201 | +- 不要在產品內頁使用大 hero、過多裝飾卡片或阻礙掃讀的動畫。 |
| 202 | +- 不要只用顏色表示成績好壞;必須保留文字或數字上下文。 |
0 commit comments