mobile bluesky app made with flutter lazurite.stormlightlabs.org/
mobile bluesky flutter
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: expand theme selection plan

+486 -39
+117 -34
docs/designs/settings.html
··· 35 35 36 36 .theme-selector { 37 37 padding: 16px; 38 - display: flex; 39 - gap: 16px; 40 - justify-content: center; 38 + display: grid; 39 + grid-template-columns: repeat(4, 1fr); 40 + gap: 8px; 41 + justify-items: center; 41 42 } 42 43 43 44 .theme-option { ··· 59 60 background-color: var(--surface-variant); 60 61 } 61 62 63 + .theme-section-label { 64 + grid-column: 1 / -1; 65 + font-size: 12px; 66 + font-weight: 600; 67 + color: var(--text-muted); 68 + text-transform: uppercase; 69 + letter-spacing: 0.5px; 70 + padding: 8px 0 0; 71 + width: 100%; 72 + text-align: left; 73 + } 74 + 75 + .theme-section-label:first-child { 76 + padding-top: 0; 77 + } 78 + 62 79 .theme-option-preview { 63 - width: 80px; 64 - height: 80px; 80 + width: 64px; 81 + height: 64px; 65 82 border-radius: 12px; 66 83 border: 2px solid var(--border); 67 84 overflow: hidden; ··· 234 251 <div class="settings-group"> 235 252 <!-- Theme Selector --> 236 253 <div class="theme-selector"> 254 + <!-- Oxocarbon --> 255 + <div class="theme-section-label">Oxocarbon</div> 237 256 <div class="theme-option active" data-theme="light" onclick="setTheme('light')"> 238 257 <div class="theme-option-preview theme-preview-light"> 239 258 <div class="theme-option-check"> 240 - <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"> 241 - <polyline points="20 6 9 17 4 12"/> 242 - </svg> 259 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> 243 260 </div> 244 261 </div> 245 262 <span class="theme-option-label">Light</span> 246 263 </div> 247 - 248 264 <div class="theme-option" data-theme="dark" onclick="setTheme('dark')"> 249 265 <div class="theme-option-preview theme-preview-dark"> 250 266 <div class="theme-option-check"> 251 - <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"> 252 - <polyline points="20 6 9 17 4 12"/> 253 - </svg> 267 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> 254 268 </div> 255 269 </div> 256 270 <span class="theme-option-label">Dark</span> 257 271 </div> 272 + <!-- spacers for 4-col grid --> 273 + <div></div> 274 + <div></div> 275 + 276 + <!-- Catppuccin --> 277 + <div class="theme-section-label">Catppuccin</div> 278 + <div class="theme-option" data-theme="catppuccin-latte" onclick="setTheme('catppuccin-latte')"> 279 + <div class="theme-option-preview theme-preview-catppuccin-latte"> 280 + <div class="theme-option-check"> 281 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> 282 + </div> 283 + </div> 284 + <span class="theme-option-label">Latte</span> 285 + </div> 286 + <div class="theme-option" data-theme="catppuccin-mocha" onclick="setTheme('catppuccin-mocha')"> 287 + <div class="theme-option-preview theme-preview-catppuccin-mocha"> 288 + <div class="theme-option-check"> 289 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> 290 + </div> 291 + </div> 292 + <span class="theme-option-label">Mocha</span> 293 + </div> 294 + <div></div> 295 + <div></div> 296 + 297 + <!-- Nord --> 298 + <div class="theme-section-label">Nord</div> 299 + <div class="theme-option" data-theme="nord-light" onclick="setTheme('nord-light')"> 300 + <div class="theme-option-preview theme-preview-nord-light"> 301 + <div class="theme-option-check"> 302 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> 303 + </div> 304 + </div> 305 + <span class="theme-option-label">Snow</span> 306 + </div> 307 + <div class="theme-option" data-theme="nord-dark" onclick="setTheme('nord-dark')"> 308 + <div class="theme-option-preview theme-preview-nord-dark"> 309 + <div class="theme-option-check"> 310 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> 311 + </div> 312 + </div> 313 + <span class="theme-option-label">Polar</span> 314 + </div> 315 + <div></div> 316 + <div></div> 317 + 318 + <!-- Rosé Pine --> 319 + <div class="theme-section-label">Rosé Pine</div> 320 + <div class="theme-option" data-theme="rose-pine-dawn" onclick="setTheme('rose-pine-dawn')"> 321 + <div class="theme-option-preview theme-preview-rose-pine-dawn"> 322 + <div class="theme-option-check"> 323 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> 324 + </div> 325 + </div> 326 + <span class="theme-option-label">Dawn</span> 327 + </div> 328 + <div class="theme-option" data-theme="rose-pine-moon" onclick="setTheme('rose-pine-moon')"> 329 + <div class="theme-option-preview theme-preview-rose-pine-moon"> 330 + <div class="theme-option-check"> 331 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> 332 + </div> 333 + </div> 334 + <span class="theme-option-label">Moon</span> 335 + </div> 336 + <div></div> 337 + <div></div> 258 338 </div> 259 339 260 340 <!-- Auto Theme Toggle --> ··· 471 551 472 552 <script> 473 553 // Theme management 554 + const ALL_THEMES = ['light', 'dark', 'catppuccin-latte', 'catppuccin-mocha', 'nord-light', 'nord-dark', 'rose-pine-dawn', 'rose-pine-moon']; 555 + 474 556 function setTheme(theme) { 475 557 // Update UI 476 558 document.querySelectorAll('.theme-option').forEach(el => { 477 559 el.classList.remove('active'); 478 560 }); 479 - document.querySelector(`[data-theme="${theme}"]`).classList.add('active'); 480 - 481 - // Apply theme 482 - if (theme === 'dark') { 483 - document.documentElement.setAttribute('data-theme', 'dark'); 484 - localStorage.setItem('theme', 'dark'); 485 - } else { 561 + const target = document.querySelector(`.theme-option[data-theme="${theme}"]`); 562 + if (target) target.classList.add('active'); 563 + 564 + // Apply theme — 'light' is the default (no data-theme attr), everything else sets the attr 565 + if (theme === 'light') { 486 566 document.documentElement.removeAttribute('data-theme'); 487 - localStorage.setItem('theme', 'light'); 567 + } else { 568 + document.documentElement.setAttribute('data-theme', theme); 488 569 } 489 - 570 + localStorage.setItem('theme', theme); 571 + 490 572 // Disable auto theme 491 573 document.getElementById('auto-theme-toggle').classList.remove('active'); 492 574 localStorage.removeItem('auto-theme'); ··· 521 603 522 604 // Initialize theme on load 523 605 (function() { 524 - const savedTheme = localStorage.getItem('theme'); 606 + const savedTheme = localStorage.getItem('theme') || 'light'; 525 607 const autoTheme = localStorage.getItem('auto-theme'); 526 - 608 + 527 609 if (autoTheme === 'true') { 528 610 document.getElementById('auto-theme-toggle').classList.add('active'); 529 611 } 530 - 531 - if (savedTheme === 'dark') { 532 - document.documentElement.setAttribute('data-theme', 'dark'); 533 - document.querySelectorAll('.theme-option').forEach(el => { 534 - el.classList.remove('active'); 535 - }); 536 - document.querySelector('[data-theme="dark"]').classList.add('active'); 612 + 613 + // Apply saved theme 614 + document.querySelectorAll('.theme-option').forEach(el => { 615 + el.classList.remove('active'); 616 + }); 617 + 618 + if (savedTheme === 'light') { 619 + document.documentElement.removeAttribute('data-theme'); 537 620 } else { 538 - document.querySelectorAll('.theme-option').forEach(el => { 539 - el.classList.remove('active'); 540 - }); 541 - document.querySelector('[data-theme="light"]').classList.add('active'); 621 + document.documentElement.setAttribute('data-theme', savedTheme); 542 622 } 623 + 624 + const active = document.querySelector(`.theme-option[data-theme="${savedTheme}"]`); 625 + if (active) active.classList.add('active'); 543 626 })(); 544 627 </script> 545 628 </body>
+190 -1
docs/designs/styles.css
··· 36 36 --text-muted: var(--oxo-light-text-muted); 37 37 } 38 38 39 - /* Dark theme */ 39 + /* Oxocarbon Dark */ 40 40 [data-theme="dark"] { 41 41 --bg: var(--oxo-dark-bg); 42 42 --surface: var(--oxo-dark-surface); ··· 45 45 --text-primary: var(--oxo-dark-text-primary); 46 46 --text-secondary: var(--oxo-dark-text-secondary); 47 47 --text-muted: var(--oxo-dark-text-muted); 48 + } 49 + 50 + /* Catppuccin Theme Variables */ 51 + :root { 52 + /* Catppuccin Latte (Light) */ 53 + --ctp-latte-bg: #eff1f5; 54 + --ctp-latte-surface: #e6e9ef; 55 + --ctp-latte-surface-variant: #ccd0da; 56 + --ctp-latte-border: #bcc0cc; 57 + --ctp-latte-text-primary: #4c4f69; 58 + --ctp-latte-text-secondary: #6c6f85; 59 + --ctp-latte-text-muted: #9ca0b0; 60 + 61 + /* Catppuccin Mocha (Dark) */ 62 + --ctp-mocha-bg: #1e1e2e; 63 + --ctp-mocha-surface: #181825; 64 + --ctp-mocha-surface-variant: #313244; 65 + --ctp-mocha-border: #45475a; 66 + --ctp-mocha-text-primary: #cdd6f4; 67 + --ctp-mocha-text-secondary: #a6adc8; 68 + --ctp-mocha-text-muted: #7f849c; 69 + } 70 + 71 + /* Catppuccin Latte */ 72 + [data-theme="catppuccin-latte"] { 73 + --bg: var(--ctp-latte-bg); 74 + --surface: var(--ctp-latte-surface); 75 + --surface-variant: var(--ctp-latte-surface-variant); 76 + --border: var(--ctp-latte-border); 77 + --text-primary: var(--ctp-latte-text-primary); 78 + --text-secondary: var(--ctp-latte-text-secondary); 79 + --text-muted: var(--ctp-latte-text-muted); 80 + --accent-primary: #7287fd; 81 + --accent-primary-hover: #5c6ee6; 82 + --accent-secondary: #209fb5; 83 + --accent-success: #40a02b; 84 + --accent-error: #d20f39; 85 + --accent-warning: #fe640b; 86 + } 87 + 88 + /* Catppuccin Mocha */ 89 + [data-theme="catppuccin-mocha"] { 90 + --bg: var(--ctp-mocha-bg); 91 + --surface: var(--ctp-mocha-surface); 92 + --surface-variant: var(--ctp-mocha-surface-variant); 93 + --border: var(--ctp-mocha-border); 94 + --text-primary: var(--ctp-mocha-text-primary); 95 + --text-secondary: var(--ctp-mocha-text-secondary); 96 + --text-muted: var(--ctp-mocha-text-muted); 97 + --accent-primary: #b4befe; 98 + --accent-primary-hover: #9aa3e6; 99 + --accent-secondary: #74c7ec; 100 + --accent-success: #a6e3a1; 101 + --accent-error: #f38ba8; 102 + --accent-warning: #fab387; 103 + } 104 + 105 + /* Nord Theme Variables */ 106 + :root { 107 + /* Nord Snow Storm (Light) */ 108 + --nord-light-bg: #eceff4; 109 + --nord-light-surface: #e5e9f0; 110 + --nord-light-surface-variant: #d8dee9; 111 + --nord-light-border: #c2c8d4; 112 + --nord-light-text-primary: #2e3440; 113 + --nord-light-text-secondary: #434c5e; 114 + --nord-light-text-muted: #4c566a; 115 + 116 + /* Nord Polar Night (Dark) */ 117 + --nord-dark-bg: #2e3440; 118 + --nord-dark-surface: #3b4252; 119 + --nord-dark-surface-variant: #434c5e; 120 + --nord-dark-border: #4c566a; 121 + --nord-dark-text-primary: #eceff4; 122 + --nord-dark-text-secondary: #d8dee9; 123 + --nord-dark-text-muted: #a5abb6; 124 + } 125 + 126 + /* Nord Snow Storm */ 127 + [data-theme="nord-light"] { 128 + --bg: var(--nord-light-bg); 129 + --surface: var(--nord-light-surface); 130 + --surface-variant: var(--nord-light-surface-variant); 131 + --border: var(--nord-light-border); 132 + --text-primary: var(--nord-light-text-primary); 133 + --text-secondary: var(--nord-light-text-secondary); 134 + --text-muted: var(--nord-light-text-muted); 135 + --accent-primary: #5e81ac; 136 + --accent-primary-hover: #4c6d96; 137 + --accent-secondary: #88c0d0; 138 + --accent-success: #a3be8c; 139 + --accent-error: #bf616a; 140 + --accent-warning: #d08770; 141 + } 142 + 143 + /* Nord Polar Night */ 144 + [data-theme="nord-dark"] { 145 + --bg: var(--nord-dark-bg); 146 + --surface: var(--nord-dark-surface); 147 + --surface-variant: var(--nord-dark-surface-variant); 148 + --border: var(--nord-dark-border); 149 + --text-primary: var(--nord-dark-text-primary); 150 + --text-secondary: var(--nord-dark-text-secondary); 151 + --text-muted: var(--nord-dark-text-muted); 152 + --accent-primary: #88c0d0; 153 + --accent-primary-hover: #6fb0c2; 154 + --accent-secondary: #81a1c1; 155 + --accent-success: #a3be8c; 156 + --accent-error: #bf616a; 157 + --accent-warning: #d08770; 158 + } 159 + 160 + /* Rosé Pine Theme Variables */ 161 + :root { 162 + /* Rosé Pine Dawn (Light) */ 163 + --rose-light-bg: #faf4ed; 164 + --rose-light-surface: #fffaf3; 165 + --rose-light-surface-variant: #f2e9e1; 166 + --rose-light-border: #dfdad9; 167 + --rose-light-text-primary: #575279; 168 + --rose-light-text-secondary: #797593; 169 + --rose-light-text-muted: #9893a5; 170 + 171 + /* Rosé Pine Main (Dark) */ 172 + --rose-dark-bg: #191724; 173 + --rose-dark-surface: #1f1d2e; 174 + --rose-dark-surface-variant: #26233a; 175 + --rose-dark-border: #524f67; 176 + --rose-dark-text-primary: #e0def4; 177 + --rose-dark-text-secondary: #908caa; 178 + --rose-dark-text-muted: #6e6a86; 179 + } 180 + 181 + /* Rosé Pine Dawn */ 182 + [data-theme="rose-pine-dawn"] { 183 + --bg: var(--rose-light-bg); 184 + --surface: var(--rose-light-surface); 185 + --surface-variant: var(--rose-light-surface-variant); 186 + --border: var(--rose-light-border); 187 + --text-primary: var(--rose-light-text-primary); 188 + --text-secondary: var(--rose-light-text-secondary); 189 + --text-muted: var(--rose-light-text-muted); 190 + --accent-primary: #d7827e; 191 + --accent-primary-hover: #c56d69; 192 + --accent-secondary: #56949f; 193 + --accent-success: #286983; 194 + --accent-error: #b4637a; 195 + --accent-warning: #ea9d34; 196 + } 197 + 198 + /* Rosé Pine Main */ 199 + [data-theme="rose-pine-moon"] { 200 + --bg: var(--rose-dark-bg); 201 + --surface: var(--rose-dark-surface); 202 + --surface-variant: var(--rose-dark-surface-variant); 203 + --border: var(--rose-dark-border); 204 + --text-primary: var(--rose-dark-text-primary); 205 + --text-secondary: var(--rose-dark-text-secondary); 206 + --text-muted: var(--rose-dark-text-muted); 207 + --accent-primary: #ebbcba; 208 + --accent-primary-hover: #d9a8a6; 209 + --accent-secondary: #9ccfd8; 210 + --accent-success: #31748f; 211 + --accent-error: #eb6f92; 212 + --accent-warning: #f6c177; 48 213 } 49 214 50 215 /* Base Reset */ ··· 457 622 458 623 .theme-preview-dark { 459 624 background: linear-gradient(135deg, #161616 50%, #262626 50%); 625 + } 626 + 627 + .theme-preview-catppuccin-latte { 628 + background: linear-gradient(135deg, #eff1f5 50%, #e6e9ef 50%); 629 + } 630 + 631 + .theme-preview-catppuccin-mocha { 632 + background: linear-gradient(135deg, #1e1e2e 50%, #181825 50%); 633 + } 634 + 635 + .theme-preview-nord-light { 636 + background: linear-gradient(135deg, #eceff4 50%, #e5e9f0 50%); 637 + } 638 + 639 + .theme-preview-nord-dark { 640 + background: linear-gradient(135deg, #2e3440 50%, #3b4252 50%); 641 + } 642 + 643 + .theme-preview-rose-pine-dawn { 644 + background: linear-gradient(135deg, #faf4ed 50%, #fffaf3 50%); 645 + } 646 + 647 + .theme-preview-rose-pine-moon { 648 + background: linear-gradient(135deg, #191724 50%, #1f1d2e 50%); 460 649 } 461 650 462 651 /* Scrollbar Styling */
+141 -3
docs/specs/phase-1.md
··· 141 141 Apply via `ThemeMode` on `MaterialApp`. Use `HydratedBloc` or a `SettingsCubit` 142 142 that reads/writes the preference. 143 143 144 - ### 2. Custom Themes — Oxocarbon 144 + ### 2. Custom Themes 145 + 146 + Ship built-in theme palettes. Each theme provides a dark and light variant. 147 + Persist the user's theme choice in the Drift `settings` table. Expose each 148 + via factory constructors (e.g. `CatppuccinTheme.dark()`). 145 149 146 - Ship two built-in themes derived from the Oxocarbon palette (IBM Carbon 147 - inspired): 150 + #### Oxocarbon (IBM Carbon inspired) 148 151 149 152 **Dark**: 150 153 ··· 190 193 191 194 Map these tokens to a `ThemeData` / `ColorScheme` and expose a 192 195 `OxocarbonTheme.dark()` / `OxocarbonTheme.light()` factory. 196 + 197 + #### Catppuccin 198 + 199 + A community-driven pastel theme. Use **Mocha** (dark) and **Latte** (light). 200 + 201 + **Mocha (Dark)**: 202 + 203 + | Token | Hex | Role | 204 + | --------- | --------- | --------------------------- | 205 + | base | `#1e1e2e` | Background | 206 + | mantle | `#181825` | Surface / card | 207 + | surface0 | `#313244` | Selection / divider | 208 + | surface1 | `#45475a` | Muted text | 209 + | subtext0 | `#a6adc8` | Secondary text | 210 + | text | `#cdd6f4` | Primary text | 211 + | lavender | `#b4befe` | Primary accent | 212 + | blue | `#89b4fa` | Blue accent | 213 + | sapphire | `#74c7ec` | Cyan highlight | 214 + | green | `#a6e3a1` | Green / success | 215 + | red | `#f38ba8` | Red / error | 216 + | peach | `#fab387` | Orange / warning | 217 + | mauve | `#cba6f7` | Purple accent | 218 + | pink | `#f5c2e7` | Pink accent | 219 + | rosewater | `#f5e0dc` | Warm highlight | 220 + 221 + **Latte (Light)**: 222 + 223 + | Token | Hex | Role | 224 + | --------- | --------- | --------------------------- | 225 + | base | `#eff1f5` | Background | 226 + | mantle | `#e6e9ef` | Surface / card | 227 + | surface0 | `#ccd0da` | Selection / divider | 228 + | surface1 | `#bcc0cc` | Muted text | 229 + | subtext0 | `#6c6f85` | Secondary text | 230 + | text | `#4c4f69` | Primary text | 231 + | lavender | `#7287fd` | Primary accent | 232 + | blue | `#1e66f5` | Blue accent | 233 + | sapphire | `#209fb5` | Cyan highlight | 234 + | green | `#40a02b` | Green / success | 235 + | red | `#d20f39` | Red / error | 236 + | peach | `#fe640b` | Orange / warning | 237 + | mauve | `#8839ef` | Purple accent | 238 + | pink | `#ea76cb` | Pink accent | 239 + | rosewater | `#dc8a78` | Warm highlight | 240 + 241 + Map to `CatppuccinTheme.dark()` / `CatppuccinTheme.light()`. 242 + 243 + #### Nord 244 + 245 + An arctic, north-bluish palette inspired by the polar night and aurora 246 + borealis. 247 + 248 + **Polar Night (Dark)**: 249 + 250 + | Token | Hex | Role | 251 + | ------- | --------- | --------------------------- | 252 + | nord0 | `#2e3440` | Background | 253 + | nord1 | `#3b4252` | Surface / card | 254 + | nord2 | `#434c5e` | Selection / divider | 255 + | nord3 | `#4c566a` | Muted text | 256 + | nord4 | `#d8dee9` | Secondary text | 257 + | nord5 | `#e5e9f0` | Primary text | 258 + | nord6 | `#eceff4` | Bright text | 259 + | nord7 | `#8fbcbb` | Teal accent | 260 + | nord8 | `#88c0d0` | Cyan / primary accent | 261 + | nord9 | `#81a1c1` | Blue accent | 262 + | nord10 | `#5e81ac` | Deep blue | 263 + | nord11 | `#bf616a` | Red / error | 264 + | nord12 | `#d08770` | Orange / warning | 265 + | nord13 | `#ebcb8b` | Yellow | 266 + | nord14 | `#a3be8c` | Green / success | 267 + | nord15 | `#b48ead` | Purple accent | 268 + 269 + **Snow Storm (Light)**: 270 + 271 + | Token | Hex | Role | 272 + | ------- | --------- | --------------------------- | 273 + | nord0 | `#eceff4` | Background | 274 + | nord1 | `#e5e9f0` | Surface / card | 275 + | nord2 | `#d8dee9` | Selection / divider | 276 + | nord3 | `#4c566a` | Primary text | 277 + | nord4 | `#434c5e` | Secondary text | 278 + | nord5 | `#3b4252` | Subheading text | 279 + | nord6 | `#2e3440` | Bright / heading text | 280 + | nord7 | `#8fbcbb` | Teal accent | 281 + | nord8 | `#88c0d0` | Cyan / primary accent | 282 + | nord9 | `#81a1c1` | Blue accent | 283 + | nord10 | `#5e81ac` | Deep blue | 284 + | nord11 | `#bf616a` | Red / error | 285 + | nord12 | `#d08770` | Orange / warning | 286 + | nord13 | `#ebcb8b` | Yellow | 287 + | nord14 | `#a3be8c` | Green / success | 288 + | nord15 | `#b48ead` | Purple accent | 289 + 290 + Map to `NordTheme.dark()` / `NordTheme.light()`. 291 + 292 + #### Rosé Pine 293 + 294 + An all-natural pine theme with muted, elegant tones. 295 + 296 + **Main (Dark)**: 297 + 298 + | Token | Hex | Role | 299 + | -------------- | --------- | --------------------------- | 300 + | base | `#191724` | Background | 301 + | surface | `#1f1d2e` | Surface / card | 302 + | overlay | `#26233a` | Selection / divider | 303 + | muted | `#6e6a86` | Muted text | 304 + | subtle | `#908caa` | Secondary text | 305 + | text | `#e0def4` | Primary text | 306 + | love | `#eb6f92` | Red / error | 307 + | gold | `#f6c177` | Yellow / warning | 308 + | rose | `#ebbcba` | Rose accent (primary) | 309 + | pine | `#31748f` | Teal / deep accent | 310 + | foam | `#9ccfd8` | Cyan highlight | 311 + | iris | `#c4a7e7` | Purple accent | 312 + 313 + **Dawn (Light)**: 314 + 315 + | Token | Hex | Role | 316 + | -------------- | --------- | --------------------------- | 317 + | base | `#faf4ed` | Background | 318 + | surface | `#fffaf3` | Surface / card | 319 + | overlay | `#f2e9e1` | Selection / divider | 320 + | muted | `#9893a5` | Muted text | 321 + | subtle | `#797593` | Secondary text | 322 + | text | `#575279` | Primary text | 323 + | love | `#b4637a` | Red / error | 324 + | gold | `#ea9d34` | Yellow / warning | 325 + | rose | `#d7827e` | Rose accent (primary) | 326 + | pine | `#286983` | Teal / deep accent | 327 + | foam | `#56949f` | Cyan highlight | 328 + | iris | `#907aa9` | Purple accent | 329 + 330 + Map to `RosePineTheme.dark()` / `RosePineTheme.light()`. 193 331 194 332 ## Development 195 333
+7 -1
docs/tasks/phase-1.md
··· 30 30 - [ ] `SettingsCubit` backed by Drift — theme mode preference (system / light / dark) 31 31 - [ ] Oxocarbon Dark `ThemeData` / `ColorScheme` 32 32 - [ ] Oxocarbon Light `ThemeData` / `ColorScheme` 33 - - [ ] Theme picker in settings screen 33 + - [ ] Catppuccin Mocha (Dark) `ThemeData` / `ColorScheme` 34 + - [ ] Catppuccin Latte (Light) `ThemeData` / `ColorScheme` 35 + - [ ] Nord Polar Night (Dark) `ThemeData` / `ColorScheme` 36 + - [ ] Nord Snow Storm (Light) `ThemeData` / `ColorScheme` 37 + - [ ] Rosé Pine Main (Dark) `ThemeData` / `ColorScheme` 38 + - [ ] Rosé Pine Dawn (Light) `ThemeData` / `ColorScheme` 39 + - [ ] Theme picker in settings screen (all 4 palettes × 2 variants + system) 34 40 - [ ] Respect system theme when set to "system" 35 41 36 42 ## M4 — Dev Scripts
+31
justfile
··· 1 + # Format all Dart files 2 + format: 3 + dart format lib test 4 + 5 + alias fmt := format 6 + 7 + # Run static analysis 8 + lint: 9 + flutter analyze 10 + 11 + # Test with failures only to focus on failures and hanging tests 12 + test-quiet *paths='': 13 + flutter test {{ paths }} --reporter=failures-only --fail-fast --timeout=120s 14 + 15 + # Run all tests 16 + test *paths='': 17 + flutter test {{ paths }} --fail-fast --timeout=120s 18 + 19 + generate: 20 + dart run build_runner build --delete-conflicting-outputs 21 + 22 + # Run code gen 23 + gen: generate format 24 + 25 + # Run format, lint, and test 26 + check: format lint test 27 + 28 + find-comments: 29 + rg -n --pcre2 '^\s*//(?![!/])' -g '*.dart' -g '!*.g.dart' -g '!*.freezed.dart' 30 + 31 + alias cmt := find-comments