this repo has no description
0
fork

Configure Feed

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

git stuff

+235 -255
+7 -2
.impeccable.md
··· 1 1 ## Design Context 2 2 3 3 ### Users 4 + 4 5 **Primary audience**: Developers learning TanStack Router/Query prefetching patterns 5 6 **Context**: Educational material consumed during focused learning sessions — following tutorials, referencing implementations, or teaching workshops 6 7 **Job to be done**: Understand how different prefetching strategies work through interactive examples; eventually read companion blog posts for deeper explanation 7 8 8 9 ### Brand Personality 10 + 9 11 **Voice**: Technical, minimalistic, informative 10 12 **Tone**: Precise but approachable — like a well-crafted developer tool or technical documentation that respects the reader's intelligence 11 13 **Emotional goals**: Confidence ("I understand this now"), clarity (no cognitive load from visual noise), trust (this is correct and well-built) 12 14 13 15 ### Aesthetic Direction 16 + 14 17 **Visual tone**: Developer console aesthetic — functional, information-dense, purposeful 15 18 **Theme**: Light AND dark mode (system preference + manual toggle) 16 19 **References**: 20 + 17 21 - https://www.effect.website/ — clean technical aesthetic, purposeful spacing, no decorative fluff, strong information hierarchy 18 - **Anti-references**: 22 + **Anti-references**: 19 23 - AI-generated UI (GPT-5+ style) — generic gradients, glassmorphism without purpose, "designed" looking elements that serve no function 20 24 - Gradient text, side-stripe borders, glow effects 21 25 - Template-y dashboard patterns ··· 26 30 27 31 2. **Information density is a feature** — This is teaching material. Users need to see code, data, and state at a glance. White space is good; empty space is wasteful. 28 32 29 - 3. **State should be visible** — Cache status, loading states, prefetch activity — these are the *point* of the demo. Make them obvious without being intrusive. 33 + 3. **State should be visible** — Cache status, loading states, prefetch activity — these are the _point_ of the demo. Make them obvious without being intrusive. 30 34 31 35 4. **Respect the code** — The typography and layout should make code snippets feel at home. Monospace is appropriate here because it's literally about code. 32 36 ··· 35 39 6. **Accessibility is non-negotiable** — WCAG 2.1 AA compliance for EU EAA requirements. Sufficient contrast, keyboard navigation, reduced motion support, screen reader friendly. 36 40 37 41 ### Technical Notes 42 + 38 43 - Current stack: TanStack Start, Tailwind v4, JetBrains Mono (already in place) 39 44 - The warm beige + amber palette works for light mode; needs a thoughtful dark counterpart 40 45 - Status dots (cached/fetching/idle) are a key interaction pattern — preserve and refine
src/components/Header.tsx src/components/header.tsx
+62 -253
src/styles/global.css
··· 1 1 @import "tailwindcss"; 2 + @import "./tokens.css"; 2 3 3 4 @plugin "tailwindcss-animate"; 4 5 5 6 @custom-variant dark (&:is(.dark *)); 6 7 7 - /* JetBrains Mono is loaded via Google Fonts in __root.tsx */ 8 - 8 + /* Shadcn/UI Theme Variables */ 9 9 :root { 10 - /* Developer Console Color Palette */ 11 - --bg-warm: #f5f5f0; 12 - --bg-warm-dark: #e8e8e0; 13 - --text-charcoal: #2a2a2a; 14 - --text-charcoal-light: #4a4a4a; 15 - --text-charcoal-muted: #6a6a6a; 16 - --accent-amber: #f5a623; 17 - --accent-amber-hover: #e09520; 18 - --accent-amber-light: #fff3dc; 19 - --border-hairline: #e5e5e0; 20 - --border-hairline-dark: #d0d0c8; 21 - --status-green: #22c55e; 22 - --status-amber: #f5a623; 23 - --status-red: #ef4444; 24 - 25 - /* CSS Variables for Tailwind compatibility */ 26 - --background: #f5f5f0; 27 - --foreground: #2a2a2a; 28 - --card: #ffffff; 29 - --card-foreground: #2a2a2a; 30 - --popover: #ffffff; 31 - --popover-foreground: #2a2a2a; 32 - --primary: #f5a623; 33 - --primary-foreground: #2a2a2a; 34 - --secondary: #e8e8e0; 35 - --secondary-foreground: #2a2a2a; 36 - --muted: #e8e8e0; 37 - --muted-foreground: #6a6a6a; 38 - --accent: #f5a623; 39 - --accent-foreground: #2a2a2a; 40 - --destructive: #ef4444; 41 - --destructive-foreground: #ffffff; 42 - --border: #e5e5e0; 43 - --input: #e5e5e0; 44 - --ring: #f5a623; 10 + --background: var(--bg-primary); 11 + --foreground: var(--text-primary); 12 + --card: var(--bg-card); 13 + --card-foreground: var(--text-primary); 14 + --popover: var(--bg-card); 15 + --popover-foreground: var(--text-primary); 16 + --primary: var(--accent-default); 17 + --primary-foreground: var(--text-inverse); 18 + --secondary: var(--bg-secondary); 19 + --secondary-foreground: var(--text-primary); 20 + --muted: var(--bg-secondary); 21 + --muted-foreground: var(--text-muted); 22 + --accent: var(--accent-default); 23 + --accent-foreground: var(--text-inverse); 24 + --destructive: var(--status-error); 25 + --destructive-foreground: var(--text-inverse); 26 + --border: var(--border-default); 27 + --input: var(--border-default); 28 + --ring: var(--ring-color); 45 29 --radius: 0; 46 - 47 - /* Typography */ 48 - --font-mono: "JetBrains Mono", "Fira Code", "SF Mono", Consolas, "Courier New", monospace; 49 30 } 50 31 51 32 @theme inline { ··· 87 68 @apply bg-background text-foreground; 88 69 font-family: var(--font-mono); 89 70 font-size: 14px; 90 - line-height: 1.6; 71 + line-height: var(--leading-normal); 91 72 -webkit-font-smoothing: antialiased; 92 73 -moz-osx-font-smoothing: grayscale; 93 74 } 94 75 95 76 code { 96 77 font-family: var(--font-mono); 97 - background: var(--bg-warm-dark); 78 + background: var(--bg-secondary); 98 79 padding: 0.125rem 0.375rem; 99 80 border-radius: 0; 100 81 font-size: 0.9em; ··· 108 89 h6 { 109 90 font-family: var(--font-mono); 110 91 font-weight: 600; 111 - line-height: 1.3; 92 + line-height: var(--leading-tight); 112 93 } 113 94 114 95 h1 { 115 - font-size: 1.75rem; 96 + font-size: var(--text-2xl); 116 97 } 117 98 118 99 h2 { 119 - font-size: 1.5rem; 100 + font-size: var(--text-xl); 120 101 } 121 102 122 103 h3 { 123 - font-size: 1.25rem; 104 + font-size: var(--text-lg); 124 105 } 125 106 126 107 a { 127 - color: var(--text-charcoal); 108 + color: var(--text-primary); 128 109 text-decoration: none; 129 - transition: color 0.15s ease; 110 + transition: color var(--duration-fast) var(--easing-default); 130 111 } 131 112 132 113 a:hover { 133 - color: var(--accent-amber-hover); 114 + color: var(--accent-hover); 134 115 } 135 116 } 136 117 ··· 142 123 gap: 0.5rem; 143 124 padding: 0.5rem 1rem; 144 125 border-left: 2px solid transparent; 145 - font-size: 0.875rem; 146 - color: var(--text-charcoal-light); 147 - transition: all 0.15s ease; 126 + font-size: var(--text-sm); 127 + color: var(--text-secondary); 128 + transition: all var(--duration-fast) var(--easing-default); 148 129 } 149 130 150 131 .nav-link:hover { 151 - color: var(--accent-amber); 152 - border-left-color: var(--accent-amber); 153 - background: var(--accent-amber-light); 132 + color: var(--accent-default); 133 + border-left-color: var(--accent-default); 134 + background: var(--accent-subtle); 154 135 } 155 136 156 137 .nav-link-active { 157 - color: var(--text-charcoal); 158 - border-left-color: var(--accent-amber); 138 + color: var(--text-primary); 139 + border-left-color: var(--accent-default); 159 140 font-weight: 500; 160 141 } 161 142 162 - /* Status dot indicators */ 163 - .status-dot { 164 - width: 8px; 165 - height: 8px; 166 - border-radius: 50%; 167 - display: inline-block; 168 - flex-shrink: 0; 169 - } 170 - 171 - .status-dot--cached { 172 - background-color: var(--status-green); 173 - box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2); 174 - } 175 - 176 - .status-dot--fetching { 177 - background-color: var(--status-amber); 178 - animation: pulse-dot 1.5s ease-in-out infinite; 179 - } 180 - 181 - .status-dot--idle { 182 - background-color: var(--border-hairline-dark); 183 - } 184 - 185 - @keyframes pulse-dot { 186 - 0%, 187 - 100% { 188 - opacity: 1; 189 - box-shadow: 0 0 0 2px rgba(245, 166, 35, 0.2); 190 - } 191 - 50% { 192 - opacity: 0.6; 193 - box-shadow: 0 0 0 4px rgba(245, 166, 35, 0.1); 194 - } 195 - } 196 - 197 - /* Card container with hairline border */ 198 - .console-card { 199 - background: var(--card); 200 - border: 1px solid var(--border-hairline); 201 - padding: 1.5rem; 202 - } 203 - 204 - .console-card:hover { 205 - border-color: var(--border-hairline-dark); 206 - } 207 - 208 - /* Code-style button/pagination item */ 209 - .code-button { 210 - display: inline-flex; 211 - align-items: center; 212 - gap: 0.5rem; 213 - padding: 0.5rem 0.75rem; 214 - border: 1px solid var(--border-hairline); 215 - background: var(--bg-warm); 216 - font-family: var(--font-mono); 217 - font-size: 0.875rem; 218 - color: var(--text-charcoal); 219 - cursor: pointer; 220 - transition: all 0.15s ease; 221 - } 222 - 223 - .code-button:hover:not(:disabled) { 224 - border-color: var(--accent-amber); 225 - background: var(--accent-amber-light); 226 - } 227 - 228 - .code-button:disabled { 229 - opacity: 0.5; 230 - cursor: not-allowed; 231 - } 232 - 233 - .code-button--active { 234 - background: var(--accent-amber); 235 - border-color: var(--accent-amber); 236 - color: var(--text-charcoal); 237 - } 238 - 239 - /* Page number in pagination */ 240 - .page-number { 241 - display: inline-flex; 242 - align-items: center; 243 - justify-content: center; 244 - min-width: 2.5rem; 245 - height: 2rem; 246 - padding: 0 0.5rem; 247 - border: 1px solid var(--border-hairline); 248 - font-family: var(--font-mono); 249 - font-size: 0.875rem; 250 - transition: all 0.15s ease; 251 - } 252 - 253 - .page-number:hover { 254 - border-color: var(--accent-amber); 255 - background: var(--accent-amber-light); 256 - } 257 - 258 - .page-number--active { 259 - background: var(--accent-amber); 260 - border-color: var(--accent-amber); 261 - font-weight: 500; 262 - } 263 - 264 - /* Section header */ 265 - .section-header { 266 - display: flex; 267 - align-items: center; 268 - gap: 0.75rem; 269 - padding: 0.75rem 0; 270 - border-bottom: 1px solid var(--border-hairline); 271 - margin-bottom: 1rem; 272 - } 273 - 274 - .section-header__title { 275 - font-size: 1rem; 276 - font-weight: 600; 277 - text-transform: uppercase; 278 - letter-spacing: 0.05em; 279 - color: var(--text-charcoal-light); 280 - } 281 - 282 143 /* Example card for landing page */ 283 144 .example-card { 284 - background: var(--card); 285 - border: 1px solid var(--border-hairline); 145 + background: var(--bg-card); 146 + border: 1px solid var(--border-default); 286 147 padding: 1.25rem; 287 - transition: all 0.15s ease; 148 + transition: all var(--duration-fast) var(--easing-default); 288 149 } 289 150 290 151 .example-card:hover { 291 - border-color: var(--accent-amber); 152 + border-color: var(--accent-default); 292 153 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); 293 154 } 294 155 295 156 .example-card__number { 296 - font-size: 0.75rem; 297 - color: var(--text-charcoal-muted); 157 + font-size: var(--text-xs); 158 + color: var(--text-muted); 298 159 text-transform: uppercase; 299 160 letter-spacing: 0.1em; 300 161 margin-bottom: 0.5rem; 301 162 } 302 163 303 164 .example-card__title { 304 - font-size: 1rem; 165 + font-size: var(--text-base); 305 166 font-weight: 600; 306 - color: var(--text-charcoal); 167 + color: var(--text-primary); 307 168 margin-bottom: 0.5rem; 308 169 } 309 170 310 171 .example-card__description { 311 - font-size: 0.875rem; 312 - color: var(--text-charcoal-muted); 313 - line-height: 1.5; 314 - } 315 - 316 - /* Table styling */ 317 - .data-table { 318 - width: 100%; 319 - border-collapse: collapse; 320 - font-size: 0.875rem; 321 - } 322 - 323 - .data-table th { 324 - text-align: left; 325 - padding: 0.75rem; 326 - border-bottom: 1px solid var(--border-hairline-dark); 327 - font-weight: 600; 328 - color: var(--text-charcoal-light); 329 - text-transform: uppercase; 330 - font-size: 0.75rem; 331 - letter-spacing: 0.05em; 332 - } 333 - 334 - .data-table td { 335 - padding: 0.75rem; 336 - border-bottom: 1px solid var(--border-hairline); 337 - } 338 - 339 - .data-table tr:hover td { 340 - background: var(--bg-warm); 341 - } 342 - 343 - /* Type badge */ 344 - .type-badge { 345 - display: inline-block; 346 - padding: 0.25rem 0.5rem; 347 - font-size: 0.75rem; 348 - border: 1px solid var(--border-hairline); 349 - background: var(--bg-warm); 350 - margin-right: 0.25rem; 172 + font-size: var(--text-sm); 173 + color: var(--text-muted); 174 + line-height: var(--leading-normal); 351 175 } 352 176 } 353 177 ··· 355 179 .font-mono { 356 180 font-family: var(--font-mono); 357 181 } 358 - 359 - .bg-warm { 360 - background-color: var(--bg-warm); 361 - } 182 + } 362 183 363 - .bg-warm-dark { 364 - background-color: var(--bg-warm-dark); 184 + /* Animations */ 185 + @keyframes pulse-dot { 186 + 0%, 187 + 100% { 188 + opacity: 1; 189 + box-shadow: 0 0 0 2px oklch(70% 0.15 85 / 0.2); 365 190 } 366 - 367 - .text-charcoal { 368 - color: var(--text-charcoal); 191 + 50% { 192 + opacity: 0.6; 193 + box-shadow: 0 0 0 4px oklch(70% 0.15 85 / 0.1); 369 194 } 195 + } 370 196 371 - .text-charcoal-light { 372 - color: var(--text-charcoal-light); 373 - } 374 - 375 - .text-charcoal-muted { 376 - color: var(--text-charcoal-muted); 377 - } 378 - 379 - .text-amber { 380 - color: var(--accent-amber); 381 - } 382 - 383 - .border-hairline { 384 - border-color: var(--border-hairline); 385 - } 386 - 387 - .border-hairline-dark { 388 - border-color: var(--border-hairline-dark); 389 - } 197 + .animate-pulse-dot { 198 + animation: pulse-dot 1.5s ease-in-out infinite; 390 199 }
+166
src/styles/tokens.css
··· 1 + /* Design Tokens - Developer Console Theme */ 2 + 3 + /* 4 + * Primitive Colors (OKLCH-based) 5 + * These define the raw color values, not semantic usage 6 + */ 7 + :root { 8 + /* Amber accent - Primary brand color */ 9 + --color-amber-50: oklch(97% 0.02 85); 10 + --color-amber-100: oklch(94% 0.04 85); 11 + --color-amber-200: oklch(88% 0.07 85); 12 + --color-amber-300: oklch(82% 0.1 85); 13 + --color-amber-400: oklch(76% 0.13 85); 14 + --color-amber-500: oklch(70% 0.15 85); 15 + --color-amber-600: oklch(64% 0.14 85); 16 + --color-amber-700: oklch(58% 0.13 85); 17 + 18 + /* Warm neutrals (tinted toward amber) */ 19 + --color-warm-0: oklch(100% 0.001 85); 20 + --color-warm-50: oklch(98% 0.003 85); 21 + --color-warm-100: oklch(96% 0.004 85); 22 + --color-warm-200: oklch(92% 0.005 85); 23 + --color-warm-300: oklch(88% 0.006 85); 24 + --color-warm-400: oklch(80% 0.007 85); 25 + --color-warm-500: oklch(70% 0.008 85); 26 + --color-warm-600: oklch(60% 0.009 85); 27 + --color-warm-700: oklch(50% 0.01 85); 28 + --color-warm-800: oklch(40% 0.011 85); 29 + --color-warm-900: oklch(30% 0.012 85); 30 + --color-warm-950: oklch(20% 0.013 85); 31 + 32 + /* Status colors */ 33 + --color-green-500: oklch(65% 0.2 145); 34 + --color-green-600: oklch(58% 0.18 145); 35 + --color-red-500: oklch(60% 0.2 25); 36 + --color-red-600: oklch(54% 0.18 25); 37 + 38 + /* Spacing scale (4pt base) */ 39 + --space-1: 0.25rem; /* 4px */ 40 + --space-2: 0.5rem; /* 8px */ 41 + --space-3: 0.75rem; /* 12px */ 42 + --space-4: 1rem; /* 16px */ 43 + --space-6: 1.5rem; /* 24px */ 44 + --space-8: 2rem; /* 32px */ 45 + --space-12: 3rem; /* 48px */ 46 + --space-16: 4rem; /* 64px */ 47 + --space-24: 6rem; /* 96px */ 48 + 49 + /* Typography */ 50 + --font-mono: "JetBrains Mono", "Fira Code", "SF Mono", Consolas, "Courier New", monospace; 51 + 52 + /* Font sizes */ 53 + --text-xs: 0.75rem; 54 + --text-sm: 0.875rem; 55 + --text-base: 1rem; 56 + --text-lg: 1.125rem; 57 + --text-xl: 1.25rem; 58 + --text-2xl: 1.5rem; 59 + 60 + /* Line heights */ 61 + --leading-tight: 1.3; 62 + --leading-normal: 1.6; 63 + --leading-relaxed: 1.75; 64 + 65 + /* Animation */ 66 + --duration-fast: 150ms; 67 + --duration-normal: 250ms; 68 + --easing-default: ease; 69 + } 70 + 71 + /* 72 + * Semantic Tokens (Light Mode) 73 + * These map primitives to meaning 74 + */ 75 + :root { 76 + /* Background */ 77 + --bg-primary: var(--color-warm-50); 78 + --bg-secondary: var(--color-warm-100); 79 + --bg-tertiary: var(--color-warm-200); 80 + --bg-card: var(--color-warm-0); 81 + 82 + /* Text */ 83 + --text-primary: var(--color-warm-900); 84 + --text-secondary: var(--color-warm-700); 85 + --text-muted: var(--color-warm-600); 86 + --text-inverse: var(--color-warm-0); 87 + 88 + /* Accent */ 89 + --accent-default: var(--color-amber-500); 90 + --accent-hover: var(--color-amber-600); 91 + --accent-subtle: var(--color-amber-100); 92 + --accent-muted: var(--color-amber-200); 93 + 94 + /* Border */ 95 + --border-default: var(--color-warm-300); 96 + --border-strong: var(--color-warm-400); 97 + --border-accent: var(--color-amber-500); 98 + 99 + /* Status */ 100 + --status-cached: var(--color-green-500); 101 + --status-fetching: var(--color-amber-500); 102 + --status-idle: var(--color-warm-400); 103 + --status-error: var(--color-red-500); 104 + 105 + /* Focus ring */ 106 + --ring-color: var(--color-amber-500); 107 + --ring-offset: 2px; 108 + } 109 + 110 + /* 111 + * Semantic Tokens (Dark Mode) 112 + */ 113 + @media (prefers-color-scheme: dark) { 114 + :root { 115 + --bg-primary: oklch(18% 0.008 85); 116 + --bg-secondary: oklch(22% 0.009 85); 117 + --bg-tertiary: oklch(28% 0.01 85); 118 + --bg-card: oklch(20% 0.009 85); 119 + 120 + --text-primary: oklch(92% 0.005 85); 121 + --text-secondary: oklch(80% 0.008 85); 122 + --text-muted: oklch(65% 0.01 85); 123 + --text-inverse: oklch(18% 0.008 85); 124 + 125 + --accent-default: var(--color-amber-400); 126 + --accent-hover: var(--color-amber-300); 127 + --accent-subtle: oklch(30% 0.03 85); 128 + --accent-muted: oklch(35% 0.04 85); 129 + 130 + --border-default: oklch(30% 0.012 85); 131 + --border-strong: oklch(38% 0.014 85); 132 + --border-accent: var(--color-amber-400); 133 + 134 + --status-cached: oklch(70% 0.18 145); 135 + --status-fetching: var(--color-amber-400); 136 + --status-idle: oklch(40% 0.01 85); 137 + --status-error: oklch(65% 0.18 25); 138 + } 139 + } 140 + 141 + /* Manual dark mode override */ 142 + [data-theme="dark"] { 143 + --bg-primary: oklch(18% 0.008 85); 144 + --bg-secondary: oklch(22% 0.009 85); 145 + --bg-tertiary: oklch(28% 0.01 85); 146 + --bg-card: oklch(20% 0.009 85); 147 + 148 + --text-primary: oklch(92% 0.005 85); 149 + --text-secondary: oklch(80% 0.008 85); 150 + --text-muted: oklch(65% 0.01 85); 151 + --text-inverse: oklch(18% 0.008 85); 152 + 153 + --accent-default: var(--color-amber-400); 154 + --accent-hover: var(--color-amber-300); 155 + --accent-subtle: oklch(30% 0.03 85); 156 + --accent-muted: oklch(35% 0.04 85); 157 + 158 + --border-default: oklch(30% 0.012 85); 159 + --border-strong: oklch(38% 0.014 85); 160 + --border-accent: var(--color-amber-400); 161 + 162 + --status-cached: oklch(70% 0.18 145); 163 + --status-fetching: var(--color-amber-400); 164 + --status-idle: oklch(40% 0.01 85); 165 + --status-error: oklch(65% 0.18 25); 166 + }