Skip to main content

max / balanced_breakfast

theme: Balanced Breakfast styles purely intent-specified (no brand aliases) Component CSS + JS inline styles reference the intent tokens directly. Accent split: blue -> action (primary buttons/links), red -> danger, green -> success; the neobrute signature yellow (focus/active/markers) -> category-four (a brand accent, not a status); purple/cyan + [data-color] badges -> the category ramp. Removed the brand-alias block; kept BB-local helpers (danger-button hover, yellow highlight, neobrute shadow) as color-mix over intents, plus the neobrute geometry. --overlay is now a theme intent. Completes the family: all four products purely intent-specified. Findings in _private docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-06-13 02:33 UTC
Commit: c3a5d48baa84a3eda2bf9d8c1c699ff85a529c31
Parent: bad54e5
2 files changed, +94 insertions, -108 deletions
@@ -146,29 +146,15 @@
146 146 --category-four: #f2a60d;
147 147 --category-five: #614c61;
148 148 --category-six: #465953;
149 -
150 - /* === BRAND ALIASES — BB vocabulary over the intent layer ===
151 - Accent hues map to the categorical ramp (preserves the [data-color]
152 - badge colors). Per-status hovers BB needs that aren't universal intents
153 - are derived here via color-mix in OKLCH (perceptual, matches the crate's
154 - approach) rather than recomputed in JS. */
155 - --bg-primary: var(--surface-page);
156 - --bg-secondary: var(--surface-overlay);
157 - --bg-tertiary: var(--surface-sunken);
158 - --text-primary: var(--content);
159 - --text-secondary: var(--content-secondary);
160 - --text-muted: var(--content-muted);
161 - --accent-red: var(--category-one);
162 - --accent-green: var(--category-two);
163 - --accent-blue: var(--category-three);
164 - --accent-yellow: var(--category-four);
165 - --accent-purple: var(--category-five);
166 - --accent-cyan: var(--category-six);
167 - --accent-blue-hover: var(--action-hover);
168 - --accent-red-hover: color-mix(in oklch, var(--accent-red) 90%, white); /* .btn-danger:hover */
169 - --accent-yellow-light: color-mix(in oklch, var(--accent-yellow) 82%, white);
170 - --border-dark: var(--border-strong);
171 - --text-on-accent: var(--content-on-action);
149 + --overlay: rgba(3, 2, 0, 0.5);
150 +
151 + /* === APP-LOCAL — derived from intents (color-mix) or non-color invariants ===
152 + BB's component CSS references the intent tokens directly. These few are
153 + BB-specific: the danger-button hover and yellow highlight (no universal
154 + intent), the neobrute shadow color, and the neobrute geometry. All derive
155 + from intents — no color literals. */
156 + --accent-red-hover: color-mix(in oklch, var(--danger) 90%, white); /* .btn-danger:hover */
157 + --accent-yellow-light: color-mix(in oklch, var(--category-four) 82%, white);
172 158 --shadow-color: color-mix(in oklch, var(--content-muted) 88%, black);
173 159 --border-width: 2px; /* invariant */
174 160 --radius-sm: 5px; /* invariant */
@@ -188,8 +174,8 @@
188 174 /* === 4. Layout shell — body & #app ==================================== */
189 175 body {
190 176 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
191 - background-color: var(--bg-primary);
192 - color: var(--text-primary);
177 + background-color: var(--surface-page);
178 + color: var(--content);
193 179 line-height: 1.5;
194 180 overflow: hidden;
195 181 }
@@ -206,7 +192,7 @@ body {
206 192 justify-content: space-between;
207 193 align-items: center;
208 194 padding: 0.75rem 1.25rem;
209 - background-color: var(--bg-secondary);
195 + background-color: var(--surface-overlay);
210 196 border-bottom: var(--border-width) solid var(--border);
211 197 box-shadow: 0 var(--shadow-offset) 0 var(--shadow-color);
212 198 -webkit-user-select: none;
@@ -217,7 +203,7 @@ body {
217 203 .header h1 {
218 204 font-size: 1.25rem;
219 205 font-weight: 700;
220 - color: var(--accent-blue);
206 + color: var(--action);
221 207 letter-spacing: -0.5px;
222 208 }
223 209
@@ -231,8 +217,8 @@ body {
231 217 padding: 0.4rem 0.75rem;
232 218 border: var(--border-width) solid var(--border);
233 219 border-radius: var(--radius-sm);
234 - background-color: var(--bg-primary);
235 - color: var(--text-primary);
220 + background-color: var(--surface-page);
221 + color: var(--content);
236 222 width: 180px;
237 223 font-size: 0.875rem;
238 224 transition: border-color 0.2s, box-shadow 0.2s;
@@ -244,7 +230,7 @@ body {
244 230 width: 14px;
245 231 height: 14px;
246 232 border: 2px solid var(--border);
247 - border-top-color: var(--accent-yellow);
233 + border-top-color: var(--category-four);
248 234 border-radius: 50%;
249 235 animation: spin 0.6s linear infinite;
250 236 }
@@ -254,23 +240,23 @@ body {
254 240 to { transform: rotate(360deg); }
255 241 }
256 242
257 - .search-input::placeholder { color: var(--text-muted); }
243 + .search-input::placeholder { color: var(--content-muted); }
258 244 .search-input:focus {
259 245 outline: none;
260 - border-color: var(--accent-yellow);
261 - box-shadow: 0 0 0 2px var(--accent-yellow);
246 + border-color: var(--category-four);
247 + box-shadow: 0 0 0 2px var(--category-four);
262 248 }
263 249
264 250 .sort-select {
265 251 padding: 0.4rem 0.75rem;
266 252 border: var(--border-width) solid var(--border);
267 253 border-radius: var(--radius-sm);
268 - background-color: var(--bg-primary);
269 - color: var(--text-primary);
254 + background-color: var(--surface-page);
255 + color: var(--content);
270 256 cursor: pointer;
271 257 font-size: 0.875rem;
272 258 }
273 - .sort-select:focus { outline: none; border-color: var(--accent-yellow); }
259 + .sort-select:focus { outline: none; border-color: var(--category-four); }
274 260
275 261 /* === 9. Buttons ========================================================
276 262 Canonical: .btn (+ .btn-primary, .btn-success, .btn-small).
@@ -281,52 +267,52 @@ body {
281 267 padding: 0.4rem 0.75rem;
282 268 border: var(--border-width) solid var(--border);
283 269 border-radius: var(--radius-sm);
284 - background-color: var(--bg-primary);
285 - color: var(--text-primary);
270 + background-color: var(--surface-page);
271 + color: var(--content);
286 272 cursor: pointer;
287 273 font-weight: 600;
288 274 font-size: 0.875rem;
289 275 transition: background-color 0.2s, border-color 0.2s;
290 276 }
291 277 .btn:hover {
292 - background-color: var(--bg-tertiary);
293 - border-color: var(--border-dark);
278 + background-color: var(--surface-sunken);
279 + border-color: var(--border-strong);
294 280 }
295 281 .btn-primary {
296 - background-color: var(--accent-blue);
297 - border-color: var(--accent-blue);
298 - color: var(--text-on-accent);
282 + background-color: var(--action);
283 + border-color: var(--action);
284 + color: var(--content-on-action);
299 285 }
300 - .btn-primary:hover { background-color: var(--accent-blue-hover); border-color: var(--accent-blue-hover); }
286 + .btn-primary:hover { background-color: var(--action-hover); border-color: var(--action-hover); }
301 287 .btn-success {
302 - background-color: var(--accent-yellow);
303 - border-color: var(--accent-yellow);
304 - color: var(--text-on-accent);
288 + background-color: var(--category-four);
289 + border-color: var(--category-four);
290 + color: var(--content-on-action);
305 291 }
306 292 .btn-success:hover { background-color: var(--accent-yellow-light); border-color: var(--accent-yellow-light); }
307 293 .btn-small { padding: 0.2rem 0.5rem; font-size: 0.8rem; }
308 - .btn-small.active { background: var(--accent-yellow); color: var(--text-on-accent); border-color: var(--accent-yellow); }
294 + .btn-small.active { background: var(--category-four); color: var(--content-on-action); border-color: var(--category-four); }
309 295
310 296 /* F2 additions (2026-06-02) — see design-system.md §Button.
311 297 .btn-sm is the parity name; .btn-small kept as alias until F5 dedup. */
312 298 .btn-sm { padding: 0.2rem 0.5rem; font-size: 0.8rem; }
313 - .btn-sm.active { background: var(--accent-yellow); color: var(--text-on-accent); border-color: var(--accent-yellow); }
299 + .btn-sm.active { background: var(--category-four); color: var(--content-on-action); border-color: var(--category-four); }
314 300
315 301 /* .btn-secondary — explicit neutral; today's default `.btn` already plays this
316 302 role, but having a named modifier lets future surfaces declare intent. */
317 303 .btn-secondary {
318 - background-color: var(--bg-secondary);
304 + background-color: var(--surface-overlay);
319 305 border-color: var(--border);
320 - color: var(--text-primary);
306 + color: var(--content);
321 307 }
322 - .btn-secondary:hover { background-color: var(--bg-tertiary); border-color: var(--border-dark); }
308 + .btn-secondary:hover { background-color: var(--surface-sunken); border-color: var(--border-strong); }
323 309
324 310 /* .btn-danger — destructive action. Consumes the previously-unused
325 311 --accent-red-hover token. Replaces .hp-action-danger in F5. */
326 312 .btn-danger {
327 - background-color: var(--accent-red);
328 - border-color: var(--accent-red);
329 - color: var(--text-on-accent);
313 + background-color: var(--danger);
314 + border-color: var(--danger);
315 + color: var(--content-on-action);
330 316 }
331 317 .btn-danger:hover { background-color: var(--accent-red-hover); border-color: var(--accent-red-hover); }
332 318
@@ -348,21 +334,21 @@ body {
348 334 background: none;
349 335 border: none;
350 336 padding: 0.25rem 0.5rem;
351 - color: var(--text-primary);
337 + color: var(--content);
352 338 }
353 - .btn-text:hover { background-color: var(--bg-secondary); border-color: transparent; }
339 + .btn-text:hover { background-color: var(--surface-overlay); border-color: transparent; }
354 340
355 341 /* .btn-link — looks like an anchor, behaves like a button. */
356 342 .btn-link {
357 343 background: none;
358 344 border: none;
359 345 padding: 0;
360 - color: var(--accent-blue);
346 + color: var(--action);
361 347 font-weight: 600;
362 348 text-decoration: underline;
363 349 text-underline-offset: 2px;
364 350 }
365 - .btn-link:hover { color: var(--accent-blue-hover); background: none; border-color: transparent; }
351 + .btn-link:hover { color: var(--action-hover); background: none; border-color: transparent; }
366 352
367 353 /* .btn-loading — spinner-replacement state. Pair with disabled attr. */
368 354 .btn-loading {
@@ -380,12 +366,12 @@ body {
380 366 border-top-color: transparent;
381 367 border-radius: 50%;
382 368 animation: spin 0.6s linear infinite;
383 - color: var(--text-primary);
369 + color: var(--content);
384 370 opacity: 0.6;
385 371 }
386 372 .btn-primary.btn-loading::after,
387 373 .btn-success.btn-loading::after,
388 - .btn-danger.btn-loading::after { color: var(--text-on-accent); opacity: 1; }
374 + .btn-danger.btn-loading::after { color: var(--content-on-action); opacity: 1; }
389 375
390 376 /* === N. Card (F2 addition, 2026-06-02) ================================
391 377 Canonical: .card. Charter target — F5 collapses .source-item,
@@ -399,7 +385,7 @@ body {
399 385 Container: .cards-grid for grid layouts.
400 386 ========================================================================== */
401 387 .card {
402 - background-color: var(--bg-primary);
388 + background-color: var(--surface-page);
403 389 border: var(--border-width) solid var(--border);
404 390 border-radius: var(--radius-sm);
405 391 padding: 0.75rem;
@@ -421,14 +407,14 @@ body {
421 407 box-shadow: none;
422 408 }
423 409 .card--muted {
424 - background-color: var(--bg-secondary);
410 + background-color: var(--surface-overlay);
425 411 }
426 412 .card--clickable {
427 413 cursor: pointer;
428 414 transition: transform 0.15s, box-shadow 0.15s, border-color 0.15s;
429 415 }
430 416 .card--clickable:hover {
431 - border-color: var(--accent-yellow);
417 + border-color: var(--category-four);
432 418 transform: translate(-1px, -1px);
433 419 box-shadow: calc(var(--shadow-offset) + 1px) calc(var(--shadow-offset) + 1px) 0 var(--shadow-color);
434 420 }
@@ -442,12 +428,12 @@ body {
442 428 .card-title {
443 429 font-size: 0.95rem;
444 430 font-weight: 700;
445 - color: var(--text-primary);
431 + color: var(--content);
446 432 margin: 0;
447 433 }
448 434 .card-description {
449 435 font-size: 0.85rem;
450 - color: var(--text-secondary);
436 + color: var(--content-secondary);
451 437 line-height: 1.4;
452 438 }
453 439 .card-meta {
@@ -456,7 +442,7 @@ body {
456 442 gap: 0.4rem;
457 443 margin-top: 0.5rem;
458 444 font-size: 0.75rem;
459 - color: var(--text-muted);
445 + color: var(--content-muted);
460 446 }
461 447 .card-badge {
462 448 flex-shrink: 0;
@@ -494,7 +480,7 @@ body {
494 480 }
495 481 .row-primary {
496 482 font-weight: 600;
497 - color: var(--text-primary);
483 + color: var(--content);
498 484 font-size: 0.9rem;
499 485 line-height: 1.3;
500 486 display: flex;
@@ -504,14 +490,14 @@ body {
504 490 }
505 491 .row-secondary {
506 492 font-size: 0.8rem;
507 - color: var(--text-secondary);
493 + color: var(--content-secondary);
508 494 line-height: 1.4;
509 495 margin-top: 0.15rem;
510 496 }
511 497 .row-meta {
512 498 flex-shrink: 0;
513 499 font-size: 0.75rem;
514 - color: var(--text-muted);
500 + color: var(--content-muted);
515 501 align-self: center;
516 502 }
517 503 .row-actions {
@@ -535,7 +521,7 @@ body {
535 521 /* F2-sub addition: form-hint error variant (used by renderFormField when
536 522 passed a `field.error`). */
537 523 .form-hint--error {
538 - color: var(--accent-red);
524 + color: var(--danger);
539 525 font-weight: 600;
540 526 }
541 527
@@ -550,7 +536,7 @@ body {
550 536 .sidebar {
551 537 width: 220px;
552 538 min-width: 220px;
553 - background-color: var(--bg-secondary);
539 + background-color: var(--surface-overlay);
554 540 border-right: var(--border-width) solid var(--border);
555 541 display: flex;
556 542 flex-direction: column;
@@ -562,7 +548,7 @@ body {
562 548 padding: 0.75rem 1rem;
563 549 font-size: 0.75rem;
564 550 font-weight: 700;
565 - color: var(--text-muted);
551 + color: var(--content-muted);
566 552 text-transform: uppercase;
567 553 letter-spacing: 0.5px;
568 554 border-bottom: var(--border-width) solid var(--border);
@@ -583,10 +569,10 @@ body {
583 569 min-height: 2.5rem;
584 570 border-left: 3px solid transparent;
585 571 }
586 - .source-item:hover { background-color: var(--bg-tertiary); }
572 + .source-item:hover { background-color: var(--surface-sunken); }
587 573 .source-item.active {
588 - background-color: var(--bg-tertiary);
589 - border-left-color: var(--accent-yellow);
574 + background-color: var(--surface-sunken);
575 + border-left-color: var(--category-four);
590 576 }
591 577
592 578 .source-name {
@@ -605,31 +591,31 @@ body {
605 591 }
606 592
607 593 .source-count {
608 - background-color: var(--bg-primary);
594 + background-color: var(--surface-page);
609 595 padding: 0.1rem 0.4rem;
610 596 border-radius: var(--radius-xl);
611 597 font-size: 0.7rem;
612 - color: var(--text-muted);
598 + color: var(--content-muted);
613 599 border: var(--border-width) solid var(--border);
614 600 min-width: 1.8rem;
615 601 text-align: center;
616 602 }
617 603 .source-count.all-read {
618 - color: var(--accent-green);
619 - border-color: var(--accent-green);
604 + color: var(--success);
605 + border-color: var(--success);
620 606 }
621 607
622 608 .source-delete {
623 609 display: none;
624 610 background: none;
625 611 border: none;
626 - color: var(--text-muted);
612 + color: var(--content-muted);
627 613 cursor: pointer;
628 614 font-size: 0.9rem;
629 615 padding: 0 0.2rem;
630 616 line-height: 1;
631 617 }
632 - .source-delete:hover { color: var(--accent-red); }
618 + .source-delete:hover { color: var(--danger); }
633 619 .source-item:hover .source-delete { display: inline; }
634 620
635 621 /* === 7. Items panel ==================================================== */
@@ -638,7 +624,7 @@ body {
638 624 display: flex;
639 625 flex-direction: column;
640 626 overflow-y: auto;
641 - background-color: var(--bg-primary);
627 + background-color: var(--surface-page);
642 628 }
643 629
644 630 .items-list { list-style: none; }
@@ -651,23 +637,23 @@ body {
651 637 cursor: pointer;
652 638 transition: background-color 0.15s;
653 639 }
654 - .item:hover { background-color: var(--bg-secondary); }
640 + .item:hover { background-color: var(--surface-overlay); }
655 641 .item.unread {
656 - border-left-color: var(--accent-red);
657 - background-color: color-mix(in srgb, var(--accent-red) 6%, var(--bg-primary));
642 + border-left-color: var(--danger);
643 + background-color: color-mix(in srgb, var(--danger) 6%, var(--surface-page));
658 644 }
659 - .item.read { color: var(--text-secondary); }
645 + .item.read { color: var(--content-secondary); }
660 646 .item.selected {
661 - background-color: var(--bg-tertiary);
662 - border-left-color: var(--accent-yellow);
647 + background-color: var(--surface-sunken);
648 + border-left-color: var(--category-four);
663 649 }
664 650 .item.unread.selected {
665 - border-left-color: var(--accent-red);
666 - background-color: color-mix(in srgb, var(--accent-red) 12%, var(--bg-primary));
651 + border-left-color: var(--danger);
652 + background-color: color-mix(in srgb, var(--danger) 12%, var(--surface-page));
667 653 }
668 654 .item.empty-state {
669 655 text-align: center;
670 - color: var(--text-muted);
656 + color: var(--content-muted);
671 657 padding: 3rem 2rem;
672 658 cursor: default;
673 659 line-height: 1.6;
@@ -696,7 +682,7 @@ body {
696 682 justify-content: center;
697 683 text-align: center;
698 684 padding: 3rem 2rem;
699 - color: var(--text-muted);
685 + color: var(--content-muted);
700 686 line-height: 1.6;
701 687 gap: 0.75rem;
702 688 }
@@ -706,7 +692,7 @@ body {
706 692 }
707 693 .empty-state-text {
708 694 font-size: 0.9rem;
709 - color: var(--text-secondary);
695 + color: var(--content-secondary);
710 696 margin: 0;
711 697 }
712 698 .empty-state--compact {
@@ -728,14 +714,14 @@ body {
728 714 border: none;
729 715 cursor: pointer;
730 716 font-size: 1.1rem;
731 - color: var(--text-muted);
717 + color: var(--content-muted);
732 718 transition: color 0.2s, transform 0.15s;
733 719 padding: 0.25rem 0.4rem;
734 720 margin: -0.25rem -0.4rem;
735 721 line-height: 1;
736 722 }
737 - .star-btn:hover { color: var(--accent-yellow); transform: scale(1.1); }
738 - .star-btn.starred { color: var(--accent-yellow); }
723 + .star-btn:hover { color: var(--category-four); transform: scale(1.1); }
724 + .star-btn.starred { color: var(--category-four); }
739 725
740 726 .read-indicator {
741 727 width: 7px;
@@ -743,7 +729,7 @@ body {
743 729 border-radius: 50%;
744 730 margin-top: 0.4rem;
745 731 }
746 - .read-indicator.unread { background-color: var(--accent-red); }
732 + .read-indicator.unread { background-color: var(--danger); }
747 733
748 734 .item-content { flex: 1; min-width: 0; }
749 735
@@ -756,14 +742,14 @@ body {
756 742
757 743 .item-author {
758 744 font-weight: 700;
759 - color: var(--accent-red);
745 + color: var(--danger);
760 746 font-size: 0.8rem;
761 747 }
762 748
763 - .item-time { color: var(--text-muted); font-size: 0.7rem; }
749 + .item-time { color: var(--content-muted); font-size: 0.7rem; }
764 750
765 751 .item-text {
766 - color: var(--text-primary);
752 + color: var(--content);
767 753 display: -webkit-box;
768 754 -webkit-line-clamp: 2;
769 755 -webkit-box-orient: vertical;
@@ -773,7 +759,7 @@ body {
773 759 }
774 760
775 761 .item-secondary {
776 - color: var(--text-secondary);
762 + color: var(--content-secondary);
777 763 font-size: 0.8rem;
778 764 margin-top: 0.15rem;
779 765 }
@@ -806,7 +792,7 @@ body {
806 792 .detail-panel {
807 793 width: 400px;
808 794 min-width: 400px;
809 - background-color: var(--bg-secondary);
795 + background-color: var(--surface-overlay);
810 796 border-left: var(--border-width) solid var(--border);
811 797 display: none;
812 798 flex-direction: column;
@@ -819,7 +805,7 @@ body {
819 805 gap: 0.3rem;
820 806 padding: 0.4rem;
821 807 border-bottom: var(--border-width) solid var(--border);
822 - background-color: var(--bg-tertiary);
808 + background-color: var(--surface-sunken);
823 809 }
824 810
825 811 .item-detail { padding: 1.25rem; }
@@ -827,7 +813,7 @@ body {
827 813 .detail-title {
828 814 font-size: 1.15rem;
829 815 margin-bottom: 0.75rem;
830 - color: var(--text-primary);
816 + color: var(--content);
831 817 font-weight: 700;
832 818 line-height: 1.4;
833 819 }
@@ -837,7 +823,7 @@ body {
837 823 flex-wrap: wrap;
838 824 gap: 0.6rem;
839 825 margin-bottom: 0.75rem;
840 - color: var(--text-secondary);
826 + color: var(--content-secondary);
Lines truncated
@@ -9,9 +9,9 @@
9 9 escapeHtml: BB.utils.escapeHtml,
10 10 bannerClass: 'update-banner',
11 11 cssVars: {
12 - accent: 'var(--accent)',
12 + accent: 'var(--action)',
13 13 border: 'var(--border)',
14 - textSecondary: 'var(--text-secondary)',
14 + textSecondary: 'var(--content-secondary)',
15 15 },
16 16 });
17 17 })();