max / balanced_breakfast
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
2 files changed,
+129 insertions,
-67 deletions
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | #!/bin/bash | |
| 2 | 2 | # Frontend design-system lint guards. | |
| 3 | - | # See _private/docs/balanced_breakfast/design-system.md "Inline-style rules" | |
| 4 | - | # and "Cross-cutting rules" for the source of truth. | |
| 3 | + | # See the wiki note `bb-design-system` ("Inline-style rules" and | |
| 4 | + | # "Cross-cutting rules") for the source of truth. | |
| 5 | 5 | # Exit 0 = clean. Exit non-zero = violations found (printed with file:line). | |
| 6 | 6 | ||
| 7 | 7 | set -u |
| @@ -108,17 +108,24 @@ | |||
| 108 | 108 | 42. Webkit scrollbar | |
| 109 | 109 | ============================================================================ */ | |
| 110 | 110 | ||
| 111 | - | /* === 1. Design System Variables ========================================= | |
| 112 | - | Color tokens are themed via `js/themes.js` (loads shared TOML palettes | |
| 113 | - | from MNW/shared/themes/). The fallback hex values below are the | |
| 114 | - | `flatwhite` light theme palette, used only for initial render before | |
| 111 | + | /* === 1. Design System Variables (Platinum-informed) ===================== | |
| 112 | + | Mac OS 8 Platinum, informed rather than copied: two-tone bevels, square | |
| 113 | + | corners, instant state changes, native window chrome. Grew out of the | |
| 114 | + | neobrute-lite system it replaces, which is why the token shapes (border | |
| 115 | + | width, radius scale, offset shadow) carry over intact. Direction and the | |
| 116 | + | conversion spec: wiki note `platinum-appearance`. | |
| 117 | + | ||
| 118 | + | Color tokens are themed via `js/themes.js`, which applies the intent set | |
| 119 | + | makeover resolved for the active theme. The fallback hex values below are | |
| 120 | + | the `flatwhite` light theme palette, used only for initial render before | |
| 115 | 121 | `themes.js` runs. Annotation legend: | |
| 116 | 122 | ||
| 117 | - | themed: set by themes.js COLOR_MAP from TOML | |
| 118 | - | derived: computed in themes.js applyTheme() from a themed token | |
| 119 | - | invariant: fixed; never changes with theme | |
| 120 | - | unused: declared but no CSS rule consumes it; see design-system.md | |
| 121 | - | token audit. Phase F1 cleanup: consume or remove. | |
| 123 | + | themed: applied at runtime by themes.js applyTheme() | |
| 124 | + | composition: CSS color-mix / var() derived from other tokens | |
| 125 | + | invariant: fixed; never changes with theme | |
| 126 | + | ||
| 127 | + | The annotation is the contract the `theme-token-coverage` lint rule | |
| 128 | + | checks; the primitive inventory lives in wiki note `bb-design-system`. | |
| 122 | 129 | ========================================================================== */ | |
| 123 | 130 | :root { | |
| 124 | 131 | /* === INTENT LAYER (themeable) === | |
| @@ -155,19 +162,42 @@ | |||
| 155 | 162 | /* === APP-LOCAL: derived from intents (color-mix) or non-color invariants === | |
| 156 | 163 | BB's component CSS references the intent tokens directly. These few are | |
| 157 | 164 | BB-specific: the danger-button hover and yellow highlight (no universal | |
| 158 | - | intent), the neobrute shadow color, and the neobrute geometry. All derive | |
| 159 | - | from intents, no color literals. */ | |
| 165 | + | intent), the drop-shadow color, and the geometry. All derive from | |
| 166 | + | intents, no color literals. | |
| 167 | + | ||
| 168 | + | Geometry is app-local by contract: a theme overrides color only, so | |
| 169 | + | border width, bevel thickness and radius cannot be reached from a | |
| 170 | + | theme file. */ | |
| 160 | 171 | --accent-red-hover: color-mix(in oklch, var(--danger) 90%, white); /* composition: .btn-danger:hover */ | |
| 161 | 172 | --accent-yellow-light: color-mix(in oklch, var(--category-four) 82%, white); /* composition */ | |
| 162 | 173 | --shadow-color: color-mix(in oklch, var(--content-muted) 88%, black); /* composition */ | |
| 163 | - | --border-width: 2px; /* invariant */ | |
| 164 | - | --radius-xs: 3px; /* invariant: scrollbars, tiny elements */ | |
| 165 | - | --radius-sm: 5px; /* invariant */ | |
| 166 | - | --radius-lg: 10px; /* invariant */ | |
| 167 | - | --radius-xl: 20px; /* invariant */ | |
| 174 | + | --border-width: 1px; /* invariant */ | |
| 175 | + | ||
| 176 | + | /* Bevel tones, named for the intents makeover emits. Until BB picks up a | |
| 177 | + | makeover that ships them, color-mix derives the same two colors from | |
| 178 | + | whatever surface the active theme resolved; adopting the crate version | |
| 179 | + | is deleting these two lines, because every consumer already reads the | |
| 180 | + | right name. */ | |
| 181 | + | --bevel-light: color-mix(in oklab, var(--surface-raised) 48%, #FFFFFF); /* composition */ | |
| 182 | + | --bevel-dark: color-mix(in oklab, var(--surface-raised) 58%, #000000); /* composition */ | |
| 183 | + | ||
| 184 | + | /* The two-tone bevel. Raised is lit from the top left; inset is the same | |
| 185 | + | bevel inverted, which is also the pressed state for anything raised. | |
| 186 | + | One token swap per component, which is what makes the idiom cheap. */ | |
| 187 | + | --bevel-raised: inset 1px 1px 0 var(--bevel-light), inset -1px -1px 0 var(--bevel-dark); /* composition */ | |
| 188 | + | --bevel-inset: inset 1px 1px 0 var(--bevel-dark), inset -1px -1px 0 var(--bevel-light); /* composition */ | |
| 189 | + | ||
| 190 | + | --radius-xs: 0; /* invariant: scrollbars, tiny elements */ | |
| 191 | + | --radius-sm: 4px; /* invariant: the one place a corner survives */ | |
| 192 | + | --radius-lg: 0; /* invariant */ | |
| 193 | + | --radius-xl: 0; /* invariant: was pills and capsules */ | |
| 168 | 194 | --radius-full: 50%; /* invariant: circles */ | |
| 169 | - | --shadow-offset: 3px; /* invariant: BB neobrute signature */ | |
| 170 | - | --shadow-brutal: var(--shadow-offset) var(--shadow-offset) 0 var(--shadow-color); /* invariant composition */ | |
| 195 | + | ||
| 196 | + | /* Drop shadows, floating surfaces only. A raised object is read by its | |
| 197 | + | bevel now; the offset shadow says "above the page", not "raised". */ | |
| 198 | + | --shadow-offset: 3px; /* invariant */ | |
| 199 | + | --shadow-brutal: var(--shadow-offset) var(--shadow-offset) 0 var(--shadow-color); /* composition */ | |
| 200 | + | --shadow-brutal-lg: calc(var(--shadow-offset) * 2) calc(var(--shadow-offset) * 2) 0 var(--shadow-color); /* composition: modals */ | |
| 171 | 201 | ||
| 172 | 202 | /* Invariant scales (never themed). Per-line `invariant` markers satisfy | |
| 173 | 203 | the lint theme-token-coverage rule. radius-md intentionally absent | |
| @@ -266,7 +296,6 @@ | |||
| 266 | 296 | padding: var(--gap-peer) var(--gap-section); | |
| 267 | 297 | background-color: var(--surface-overlay); | |
| 268 | 298 | border-bottom: var(--border-width) solid var(--border); | |
| 269 | - | box-shadow: 0 var(--shadow-offset) 0 var(--shadow-color); | |
| 270 | 299 | -webkit-user-select: none; | |
| 271 | 300 | user-select: none; | |
| 272 | 301 | position: relative; | |
| @@ -293,7 +322,7 @@ | |||
| 293 | 322 | color: var(--content); | |
| 294 | 323 | width: 180px; | |
| 295 | 324 | font-size: 0.875rem; | |
| 296 | - | transition: border-color 0.2s, box-shadow 0.2s; | |
| 325 | + | box-shadow: var(--bevel-inset); | |
| 297 | 326 | } | |
| 298 | 327 | ||
| 299 | 328 | /* Search spinner */ | |
| @@ -301,9 +330,9 @@ | |||
| 301 | 330 | display: none; | |
| 302 | 331 | width: 14px; | |
| 303 | 332 | height: 14px; | |
| 304 | - | border: 2px solid var(--border); | |
| 333 | + | border: 2px solid var(--border); /* spinner ring thickness, not a frame */ | |
| 305 | 334 | border-top-color: var(--category-four); | |
| 306 | - | border-radius: 50%; | |
| 335 | + | border-radius: var(--radius-full); | |
| 307 | 336 | animation: spin 0.6s linear infinite; | |
| 308 | 337 | } | |
| 309 | 338 | .search-spinner.active { display: inline-block; } | |
| @@ -316,7 +345,7 @@ | |||
| 316 | 345 | .search-input:focus { | |
| 317 | 346 | outline: none; | |
| 318 | 347 | border-color: var(--category-four); | |
| 319 | - | box-shadow: 0 0 0 2px var(--category-four); | |
| 348 | + | box-shadow: var(--bevel-inset), 0 0 0 2px var(--category-four); | |
| 320 | 349 | } | |
| 321 | 350 | ||
| 322 | 351 | .sort-select { | |
| @@ -327,6 +356,7 @@ | |||
| 327 | 356 | color: var(--content); | |
| 328 | 357 | cursor: pointer; | |
| 329 | 358 | font-size: 0.875rem; | |
| 359 | + | box-shadow: var(--bevel-inset); | |
| 330 | 360 | } | |
| 331 | 361 | .sort-select:focus { outline: none; border-color: var(--category-four); } | |
| 332 | 362 | ||
| @@ -368,10 +398,10 @@ | |||
| 368 | 398 | read/unread/selected/pinned state. Replaces the legacy .item-* layout. */ | |
| 369 | 399 | .row--article { | |
| 370 | 400 | padding: var(--gap-peer) var(--gap-section); | |
| 371 | - | border-bottom: 1px solid var(--border); | |
| 401 | + | border-bottom: var(--border-width) solid var(--border); | |
| 402 | + | /* 3px, wider than a frame on purpose: this edge is the state marker. */ | |
| 372 | 403 | border-left: 3px solid transparent; | |
| 373 | 404 | cursor: pointer; | |
| 374 | - | transition: background-color 0.15s; | |
| 375 | 405 | } | |
| 376 | 406 | .row--article:hover { background-color: var(--surface-overlay); } | |
| 377 | 407 | .row--article.is-unread { | |
| @@ -445,7 +475,6 @@ | |||
| 445 | 475 | cursor: pointer; | |
| 446 | 476 | font-size: 1.1rem; | |
| 447 | 477 | color: var(--content-muted); | |
| 448 | - | transition: color 0.2s, transform 0.15s; | |
| 449 | 478 | padding: var(--step-tight) var(--step-snug); | |
| 450 | 479 | margin: calc(-1 * var(--step-tight)) calc(-1 * var(--step-snug)); | |
| 451 | 480 | line-height: 1; | |
| @@ -456,7 +485,7 @@ | |||
| 456 | 485 | .read-indicator { | |
| 457 | 486 | width: 7px; | |
| 458 | 487 | height: 7px; | |
| 459 | - | border-radius: 50%; | |
| 488 | + | border-radius: var(--radius-full); | |
| 460 | 489 | margin-top: var(--gap-bound); | |
| 461 | 490 | } | |
| 462 | 491 | .read-indicator.unread { background-color: var(--danger); } | |
| @@ -559,12 +588,27 @@ | |||
| 559 | 588 | cursor: pointer; | |
| 560 | 589 | font-weight: 600; | |
| 561 | 590 | font-size: 0.875rem; | |
| 562 | - | transition: background-color 0.2s, border-color 0.2s; | |
| 591 | + | box-shadow: var(--bevel-raised); | |
| 563 | 592 | } | |
| 564 | 593 | .btn:hover { | |
| 565 | 594 | background-color: var(--surface-sunken); | |
| 566 | 595 | border-color: var(--border-strong); | |
| 567 | 596 | } | |
| 597 | + | /* Pressed inverts the bevel and drops the fill a step. No offset, no | |
| 598 | + | translate: the button reads as pushed into the page, and every modifier | |
| 599 | + | below inherits the state for free. */ | |
| 600 | + | .btn:active { | |
| 601 | + | background-color: var(--surface-sunken); | |
| 602 | + | box-shadow: var(--bevel-inset); | |
| 603 | + | } | |
| 604 | + | /* Disabled keeps its bevel. The object is still there; greying the content | |
| 605 | + | is what says the action is unavailable. */ | |
| 606 | + | .btn:disabled { | |
| 607 | + | color: var(--content-muted); | |
| 608 | + | border-color: var(--content-muted); | |
| 609 | + | cursor: not-allowed; | |
| 610 | + | } | |
| 611 | + | .btn:disabled:active { box-shadow: var(--bevel-raised); } | |
| 568 | 612 | .btn-primary { | |
| 569 | 613 | background-color: var(--action); | |
| 570 | 614 | border-color: var(--action); | |
| @@ -577,7 +621,7 @@ | |||
| 577 | 621 | color: var(--content-on-action); | |
| 578 | 622 | } | |
| 579 | 623 | .btn-success:hover { background-color: var(--accent-yellow-light); border-color: var(--accent-yellow-light); } | |
| 580 | - | /* .btn-sm: canonical small-button size (see design-system.md §Button). */ | |
| 624 | + | /* .btn-sm: canonical small-button size (see wiki note `bb-design-system`). */ | |
| 581 | 625 | .btn-sm { padding: var(--gap-bound) var(--gap-peer); font-size: 0.8rem; } | |
| 582 | 626 | .btn-sm.active { background: var(--category-four); color: var(--content-on-action); border-color: var(--category-four); } | |
| 583 | 627 | ||
| @@ -618,8 +662,10 @@ | |||
| 618 | 662 | border: none; | |
| 619 | 663 | padding: var(--gap-bound) var(--gap-peer); | |
| 620 | 664 | color: var(--content); | |
| 665 | + | box-shadow: none; | |
| 621 | 666 | } | |
| 622 | 667 | .btn-text:hover { background-color: var(--surface-overlay); border-color: transparent; } | |
| 668 | + | .btn-text:active { box-shadow: none; } | |
| 623 | 669 | ||
| 624 | 670 | /* .btn-link: looks like an anchor, behaves like a button. */ | |
| 625 | 671 | .btn-link { | |
| @@ -630,8 +676,10 @@ | |||
| 630 | 676 | font-weight: 600; | |
| 631 | 677 | text-decoration: underline; | |
| 632 | 678 | text-underline-offset: 2px; | |
| 679 | + | box-shadow: none; | |
| 633 | 680 | } | |
| 634 | 681 | .btn-link:hover { color: var(--action-hover); background: none; border-color: transparent; } | |
| 682 | + | .btn-link:active { box-shadow: none; } | |
| 635 | 683 | ||
| 636 | 684 | /* .btn-loading: spinner-replacement state. Pair with disabled attr. */ | |
| 637 | 685 | .btn-loading { | |
| @@ -645,9 +693,9 @@ | |||
| 645 | 693 | top: 50%; left: 50%; | |
| 646 | 694 | width: 0.875rem; height: 0.875rem; | |
| 647 | 695 | margin: -0.4375rem 0 0 -0.4375rem; /* half the spinner, not a gap */ | |
| 648 | - | border: 2px solid currentColor; | |
| 696 | + | border: 2px solid currentColor; /* spinner ring thickness, not a frame */ | |
| 649 | 697 | border-top-color: transparent; | |
| 650 | - | border-radius: 50%; | |
| 698 | + | border-radius: var(--radius-full); | |
| 651 | 699 | animation: spin 0.6s linear infinite; | |
| 652 | 700 | color: var(--content); | |
| 653 | 701 | opacity: 0.6; | |
| @@ -660,11 +708,11 @@ | |||
| 660 | 708 | Canonical: .card. Charter target: F5 collapses .source-item, | |
| 661 | 709 | .plugin-item, .sidebar-saved, .bookmark-item onto .card with the | |
| 662 | 710 | modifier variants below. | |
| 663 | - | .card: default: white-ish surface, 2px border, 3px brutal shadow | |
| 664 | - | .card--row: row-shaped, no shadow, used in sidebars and lists | |
| 711 | + | .card: default: page surface, 1px border, raised bevel | |
| 712 | + | .card--row: row-shaped, no bevel, used in sidebars and lists | |
| 665 | 713 | .card--shell: frame-only, no fill (for grouping) | |
| 666 | 714 | .card--muted: secondary-surface variant | |
| 667 | - | .card--clickable: adds hover lift | |
| 715 | + | .card--clickable: pointer + frame recolor on hover, pressed bevel | |
| 668 | 716 | Container: .cards-grid for grid layouts. | |
| 669 | 717 | ========================================================================== */ | |
| 670 | 718 | .card { | |
| @@ -672,7 +720,7 @@ | |||
| 672 | 720 | border: var(--border-width) solid var(--border); | |
| 673 | 721 | border-radius: var(--radius-sm); | |
| 674 | 722 | padding: var(--gap-group); | |
| 675 | - | box-shadow: var(--shadow-brutal); | |
| 723 | + | box-shadow: var(--bevel-raised); | |
| 676 | 724 | } | |
| 677 | 725 | .card--row { | |
| 678 | 726 | border-radius: 0; | |
| @@ -694,12 +742,15 @@ | |||
| 694 | 742 | } | |
| 695 | 743 | .card--clickable { | |
| 696 | 744 | cursor: pointer; | |
| 697 | - | transition: transform 0.15s, box-shadow 0.15s, border-color 0.15s; | |
| 698 | 745 | } | |
| 746 | + | /* Hover recolors the frame; it does not lift. The lift was the neobrute | |
| 747 | + | signal and the bevel replaces it, so a clickable card that also floated | |
| 748 | + | on hover would read as two objects. */ | |
| 699 | 749 | .card--clickable:hover { | |
| 700 | 750 | border-color: var(--category-four); | |
| 701 | - | transform: translate(-1px, -1px); | |
| 702 | - | box-shadow: calc(var(--shadow-offset) + 1px) calc(var(--shadow-offset) + 1px) 0 var(--shadow-color); | |
| 751 | + | } | |
| 752 | + | .card--clickable:active { | |
| 753 | + | box-shadow: var(--bevel-inset); | |
| 703 | 754 | } | |
| 704 | 755 | ||
| 705 | 756 | .card-header { | |
| @@ -794,7 +845,6 @@ | |||
| 794 | 845 | keeps them visible. */ | |
| 795 | 846 | .row .row-actions:not(.always) { | |
| 796 | 847 | opacity: 0; | |
| 797 | - | transition: opacity 0.15s; | |
| 798 | 848 | } | |
| 799 | 849 | .row:hover .row-actions:not(.always), | |
| 800 | 850 | .row:focus-within .row-actions:not(.always) { | |
| @@ -831,12 +881,12 @@ | |||
| 831 | 881 | background-color: var(--surface-page); | |
| 832 | 882 | color: var(--content); | |
| 833 | 883 | font-size: 0.875rem; | |
| 834 | - | transition: border-color 0.2s, box-shadow 0.2s; | |
| 884 | + | box-shadow: var(--bevel-inset); | |
| 835 | 885 | } | |
| 836 | 886 | .form-input:focus { | |
| 837 | 887 | outline: none; | |
| 838 | 888 | border-color: var(--category-four); | |
| 839 | - | box-shadow: 0 0 0 2px var(--category-four); | |
| 889 | + | box-shadow: var(--bevel-inset), 0 0 0 2px var(--category-four); | |
| 840 | 890 | } | |
| 841 | 891 | ||
| 842 | 892 | /* F2 additions (2026-06-02): split .form-input into kind-specific | |
| @@ -860,12 +910,12 @@ | |||
| 860 | 910 | color: var(--content); | |
| 861 | 911 | font-size: 0.875rem; | |
| 862 | 912 | cursor: pointer; | |
| 863 | - | transition: border-color 0.2s, box-shadow 0.2s; | |
| 913 | + | box-shadow: var(--bevel-inset); | |
| 864 | 914 | } | |
| 865 | 915 | .form-select:focus { | |
| 866 | 916 | outline: none; | |
| 867 | 917 | border-color: var(--category-four); | |
| 868 | - | box-shadow: 0 0 0 2px var(--category-four); | |
| 918 | + | box-shadow: var(--bevel-inset), 0 0 0 2px var(--category-four); | |
| 869 | 919 | } | |
| 870 | 920 | .form-textarea { | |
| 871 | 921 | width: 100%; | |
| @@ -878,12 +928,12 @@ | |||
| 878 | 928 | font-family: inherit; | |
| 879 | 929 | resize: vertical; | |
| 880 | 930 | min-height: 4rem; | |
| 881 | - | transition: border-color 0.2s, box-shadow 0.2s; | |
| 931 | + | box-shadow: var(--bevel-inset); | |
| 882 | 932 | } | |
| 883 | 933 | .form-textarea:focus { | |
| 884 | 934 | outline: none; | |
| 885 | 935 | border-color: var(--category-four); | |
| 886 | - | box-shadow: 0 0 0 2px var(--category-four); | |
| 936 | + | box-shadow: var(--bevel-inset), 0 0 0 2px var(--category-four); | |
| 887 | 937 | } | |
| 888 | 938 | /* .form-row: horizontal grouping of two related fields (e.g. label + button) */ | |
| 889 | 939 | .form-row { | |
| @@ -927,7 +977,9 @@ | |||
| 927 | 977 | max-width: 500px; | |
| 928 | 978 | max-height: 80vh; | |
| 929 | 979 | overflow-y: auto; | |
| 930 | - | box-shadow: 6px 6px 0 var(--shadow-color); | |
| 980 | + | /* Floating surfaces carry both: the bevel says the panel is a lit | |
| 981 | + | object, the drop shadow says it is above the page. */ | |
| 982 | + | box-shadow: var(--bevel-raised), var(--shadow-brutal-lg); | |
| 931 | 983 | } | |
| 932 | 984 | ||
| 933 | 985 | .modal-header { | |
| @@ -965,14 +1017,15 @@ | |||
| 965 | 1017 | border-radius: var(--radius-sm); | |
| 966 | 1018 | margin-bottom: var(--gap-peer); | |
| 967 | 1019 | cursor: pointer; | |
| 968 | - | transition: all 0.2s; | |
| 969 | 1020 | background-color: var(--surface-page); | |
| 1021 | + | box-shadow: var(--bevel-raised); | |
| 970 | 1022 | } | |
| 971 | 1023 | .plugin-item:hover { | |
| 972 | 1024 | border-color: var(--category-four); | |
| 973 | 1025 | background-color: var(--surface-overlay); | |
| 974 | - | box-shadow: var(--shadow-brutal); | |
| 975 | - | transform: translate(-1px, -1px); | |
| 1026 | + | } | |
| 1027 | + | .plugin-item:active { | |
| 1028 | + | box-shadow: var(--bevel-inset); | |
| 976 | 1029 | } | |
| 977 | 1030 | ||
| 978 | 1031 | /* F5 (2026-06-02): dropped .plugin-name and .plugin-desc; plugin-list | |
| @@ -1089,7 +1142,7 @@ | |||
| 1089 | 1142 | border-radius: var(--radius-sm); | |
| 1090 | 1143 | padding: var(--gap-peer) var(--gap-group); | |
| 1091 | 1144 | margin-top: var(--gap-peer); | |
| 1092 | - | box-shadow: var(--shadow-brutal); | |
| 1145 | + | box-shadow: var(--bevel-raised), var(--shadow-brutal); | |
| 1093 | 1146 | animation: slideIn 0.3s ease-out; | |
| 1094 | 1147 | font-weight: 500; | |
| 1095 | 1148 | font-size: 0.875rem; | |
| @@ -1150,6 +1203,7 @@ | |||
| 1150 | 1203 | padding: var(--step-hair) var(--step-base); | |
| 1151 | 1204 | font-size: 0.75rem; | |
| 1152 | 1205 | color: var(--content-secondary); | |
| 1206 | + | box-shadow: var(--bevel-raised); | |
| 1153 | 1207 | } | |
| 1154 | 1208 | ||
| 1155 | 1209 | /* F2 additions (2026-06-02): color variants via [data-color] attribute. | |
| @@ -1179,9 +1233,11 @@ | |||
| 1179 | 1233 | color: var(--content-secondary); | |
| 1180 | 1234 | line-height: 1.3; | |
| 1181 | 1235 | white-space: nowrap; | |
| 1236 | + | box-shadow: var(--bevel-raised); | |
| 1182 | 1237 | } | |
| 1183 | 1238 | .badge--xs { font-size: 0.6rem; padding: 0 var(--step-snug); } | |
| 1184 | 1239 | .badge--pill { border-radius: var(--radius-xl); } | |
| 1240 | + | .badge--filled { border: none; box-shadow: none; } | |
| 1185 | 1241 | .badge--filled[data-color="green"] { background-color: var(--category-two); color: var(--content-on-action); } | |
| 1186 | 1242 | .badge--filled[data-color="yellow"] { background-color: var(--category-four); color: var(--content); } | |
| 1187 | 1243 | .badge--filled[data-color="red"] { background-color: var(--category-one); color: var(--content-on-action); } | |
| @@ -1199,7 +1255,7 @@ | |||
| 1199 | 1255 | white-space: pre-wrap; | |
| 1200 | 1256 | background-color: var(--surface-sunken); | |
| 1201 | 1257 | padding: var(--gap-peer); | |
| 1202 | - | border-radius: 4px; | |
| 1258 | + | border-radius: var(--radius-sm); | |
| 1203 | 1259 | overflow-x: auto; | |
| 1204 | 1260 | } | |
| 1205 | 1261 | ||
| @@ -1222,7 +1278,7 @@ | |||
| 1222 | 1278 | background: var(--surface-page); | |
| 1223 | 1279 | border: var(--border-width) solid var(--border); | |
| 1224 | 1280 | border-radius: var(--radius-sm); | |
| 1225 | - | box-shadow: var(--shadow-brutal); | |
| 1281 | + | box-shadow: var(--bevel-raised), var(--shadow-brutal); | |
| 1226 | 1282 | padding: var(--gap-bound); | |
| 1227 | 1283 | min-width: 140px; | |
| 1228 | 1284 | } | |
| @@ -1250,7 +1306,7 @@ | |||
| 1250 | 1306 | display: inline-block; | |
| 1251 | 1307 | width: 8px; | |
| 1252 | 1308 | height: 8px; | |
| 1253 | - | border-radius: 50%; | |
| 1309 | + | border-radius: var(--radius-full); | |
| 1254 | 1310 | margin-right: var(--gap-bound); | |
| 1255 | 1311 | flex-shrink: 0; | |
| 1256 | 1312 | } | |
| @@ -1301,14 +1357,14 @@ | |||
| 1301 | 1357 | .skeleton-item { | |
| 1302 | 1358 | display: flex; | |
| 1303 | 1359 | padding: var(--gap-peer) var(--gap-section); | |
| 1304 | - | border-bottom: 1px solid var(--border); | |
| 1360 | + | border-bottom: var(--border-width) solid var(--border); | |
| 1305 | 1361 | animation: shimmer 1.5s infinite; | |
| 1306 | 1362 | } | |
| 1307 | 1363 | .skeleton-item .skeleton-indicators { width: 24px; margin-right: var(--gap-bound); } | |
| 1308 | 1364 | .skeleton-item .skeleton-content { flex: 1; } | |
| 1309 | 1365 | .skeleton-line { | |
| 1310 | 1366 | height: 0.75rem; | |
| 1311 | - | border-radius: 4px; | |
| 1367 | + | border-radius: var(--radius-sm); | |
| 1312 | 1368 | background: linear-gradient(90deg, var(--surface-sunken) 25%, var(--border) 50%, var(--surface-sunken) 75%); | |
| 1313 | 1369 | background-size: 200% 100%; | |
| 1314 | 1370 | animation: shimmer 1.5s infinite; | |
| @@ -1330,12 +1386,15 @@ | |||
| 1330 | 1386 | right: 0; | |
| 1331 | 1387 | height: 3px; | |
| 1332 | 1388 | background-color: var(--border); | |
| 1389 | + | box-shadow: var(--bevel-inset); | |
| 1333 | 1390 | overflow: hidden; | |
| 1334 | 1391 | } | |
| 1392 | + | /* Kept motion: the fill moves through space, which is period-correct. State | |
| 1393 | + | feedback that only recolors is not, and was dropped across the file. */ | |
| 1335 | 1394 | .progress-bar { | |
| 1336 | 1395 | height: 100%; | |
| 1337 | 1396 | background-color: var(--category-four); | |
| 1338 | - | transition: width 0.3s ease; | |
| 1397 | + | transition: width var(--transition-slow) ease; | |
| 1339 | 1398 | } | |
| 1340 | 1399 | ||
| 1341 | 1400 | /* Toast with action */ | |
| @@ -1343,8 +1402,8 @@ | |||
| 1343 | 1402 | display: inline-block; | |
| 1344 | 1403 | margin-left: var(--gap-bound); | |
| 1345 | 1404 | background: none; | |
| 1346 | - | border: 1px solid currentColor; | |
| 1347 | - | border-radius: 4px; | |
| 1405 | + | border: var(--border-width) solid currentColor; | |
| 1406 | + | border-radius: var(--radius-sm); | |
| 1348 | 1407 | color: inherit; | |
| 1349 | 1408 | cursor: pointer; | |
| 1350 | 1409 | padding: var(--step-hair) var(--step-base); | |
| @@ -1379,7 +1438,7 @@ | |||
| 1379 | 1438 | padding: var(--gap-peer) var(--gap-group); | |
| 1380 | 1439 | z-index: 1200; | |
| 1381 | 1440 | max-width: 320px; | |
| 1382 | - | box-shadow: var(--shadow-brutal); | |
| 1441 | + | box-shadow: var(--bevel-raised), var(--shadow-brutal); | |
| 1383 | 1442 | font-size: 0.875rem; | |
| 1384 | 1443 | } | |
| 1385 | 1444 | ||
| @@ -1393,7 +1452,7 @@ | |||
| 1393 | 1452 | background: var(--surface-overlay); | |
| 1394 | 1453 | border: var(--border-width) solid var(--border); | |
| 1395 | 1454 | border-radius: var(--radius-sm); | |
| 1396 | - | box-shadow: var(--shadow-brutal); | |
| 1455 | + | box-shadow: var(--bevel-raised), var(--shadow-brutal); | |
| 1397 | 1456 | padding: var(--gap-group); | |
| 1398 | 1457 | min-width: 220px; | |
| 1399 | 1458 | max-width: 320px; | |
| @@ -1403,7 +1462,7 @@ | |||
| 1403 | 1462 | font-weight: 700; | |
| 1404 | 1463 | margin-bottom: var(--gap-peer); | |
| 1405 | 1464 | padding-bottom: var(--gap-peer); | |
| 1406 | - | border-bottom: 1px solid var(--border); | |
| 1465 | + | border-bottom: var(--border-width) solid var(--border); | |
| 1407 | 1466 | } | |
| 1408 | 1467 | .hp-row { | |
| 1409 | 1468 | display: flex; | |
| @@ -1456,8 +1515,8 @@ | |||
| 1456 | 1515 | align-items: center; | |
| 1457 | 1516 | padding: var(--gap-peer) var(--gap-group); | |
| 1458 | 1517 | cursor: pointer; | |
| 1459 | - | transition: background-color 0.15s; | |
| 1460 | 1518 | min-height: 2.5rem; | |
| 1519 | + | /* 3px, wider than a frame on purpose: this edge is the state marker. */ | |
| 1461 | 1520 | border-left: 3px solid transparent; | |
| 1462 | 1521 | } | |
| 1463 | 1522 | .source-item:hover { background-color: var(--surface-sunken); } | |
| @@ -1488,6 +1547,7 @@ | |||
| 1488 | 1547 | font-size: 0.7rem; | |
| 1489 | 1548 | color: var(--content-muted); | |
| 1490 | 1549 | border: var(--border-width) solid var(--border); | |
| 1550 | + | box-shadow: var(--bevel-raised); | |
| 1491 | 1551 | min-width: 1.8rem; | |
| 1492 | 1552 | text-align: center; | |
| 1493 | 1553 | } | |
| @@ -1526,15 +1586,17 @@ | |||
| 1526 | 1586 | font-size: 0.7rem; | |
| 1527 | 1587 | color: var(--content-secondary); | |
| 1528 | 1588 | cursor: pointer; | |
| 1529 | - | transition: all 0.15s; | |
| 1530 | 1589 | white-space: nowrap; | |
| 1590 | + | box-shadow: var(--bevel-raised); | |
| 1531 | 1591 | } | |
| 1532 | 1592 | .tag-chip:hover { border-color: var(--category-four); color: var(--content); } | |
| 1593 | + | /* An active filter chip is latched down, same inversion as a pressed button. */ | |
| 1533 | 1594 | .tag-chip.active { | |
| 1534 | 1595 | background-color: var(--category-four); | |
| 1535 | 1596 | border-color: var(--category-four); | |
| 1536 | 1597 | color: var(--content); | |
| 1537 | 1598 | font-weight: 600; | |
| 1599 | + | box-shadow: var(--bevel-inset); | |
| 1538 | 1600 | } | |
| 1539 | 1601 | ||
| 1540 | 1602 | /* Source tag chips (small pills under source name) */ | |
| @@ -1655,7 +1717,7 @@ | |||
| 1655 | 1717 | gap: var(--gap-peer); | |
| 1656 | 1718 | margin-top: var(--gap-section); | |
| 1657 | 1719 | padding-top: var(--gap-section); | |
| 1658 | - | border-top: 1px solid var(--border); | |
| 1720 | + | border-top: var(--border-width) solid var(--border); | |
| 1659 | 1721 | } | |
| 1660 | 1722 | ||
| 1661 | 1723 | /* === 24. Sidebar saved articles ======================================== */ | |
| @@ -1666,8 +1728,8 @@ | |||
| 1666 | 1728 | padding: var(--gap-peer) var(--gap-group); | |
| 1667 | 1729 | cursor: pointer; | |
| 1668 | 1730 | border-top: var(--border-width) solid var(--border); | |
| 1731 | + | /* 3px, wider than a frame on purpose: this edge is the state marker. */ | |
| 1669 | 1732 | border-left: 3px solid transparent; | |
| 1670 | - | transition: background-color 0.15s; |
Lines truncated