Skip to main content

max / goingson

Platinum wave 2: bevel the primitives Buttons, cards, tags and badges take --bevel-raised; text inputs, selects and textareas take --bevel-inset so they read as wells. Pressed states invert the bevel instead of translating the element, which retires the offset-shadow lift on .btn and .card along with the transitions that animated it. Two per-component press hacks go with it: .focus-section .btn scaled itself on :active, and .btn-link carried a transform reset for a transform that no longer exists. A checked recurrence weekday now latches with the pressed bevel. Borderless variants (.btn-link, .btn-icon, .form-input--ghost, .badge--filled) opt out explicitly, and the duplicated .card:hover in the touch-hover block collapses to one rule.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-26 22:40 UTC
Signed with PGP, not checked
Commit: 0dd9176322524a752dfcf106905566e156b28123
Parent: dbc33c0
1 file changed, +47 insertions, -49 deletions
@@ -643,37 +643,37 @@
643 643 font-size: 0.9rem;
644 644 font-weight: 600;
645 645 cursor: pointer;
646 - transition: background-color 0.15s ease, transform 0.1s ease, box-shadow 0.1s ease;
647 646 text-decoration: none;
648 647 background: var(--surface-raised);
649 648 color: var(--content);
650 - box-shadow: var(--shadow-brutal-xs);
649 + box-shadow: var(--bevel-raised);
651 650 }
652 651
653 652 .btn:hover {
654 653 background: var(--surface-overlay);
655 - transform: translate(-1px, -1px);
656 - box-shadow: var(--shadow-offset-md) var(--shadow-offset-md) 0 var(--border);
657 654 }
658 655
656 + /* Pressed inverts the bevel and drops the fill a step, which is the whole
657 + idiom: no offset, no translate, the button reads as pushed into the page. */
659 658 .btn:active {
660 659 background: var(--surface-sunken);
661 - transform: translate(1px, 1px);
662 - box-shadow: none;
660 + box-shadow: var(--bevel-inset);
663 661 }
664 662
663 + /* Disabled keeps its bevel. The object is still there, its label is just
664 + unavailable, which is what greying the content says. */
665 665 .btn:disabled {
666 - background: transparent;
667 666 color: var(--content-muted);
668 667 border-color: var(--content-muted);
669 668 cursor: not-allowed;
670 - box-shadow: none;
671 669 }
672 670
673 671 .btn:disabled:hover {
674 - background: transparent;
675 - transform: none;
676 - box-shadow: none;
672 + background: var(--surface-raised);
673 + }
674 +
675 + .btn:disabled:active {
676 + box-shadow: var(--bevel-raised);
677 677 }
678 678
679 679 .btn-primary {
@@ -742,22 +742,24 @@
742 742 border: var(--border-width) solid var(--border);
743 743 border-radius: var(--radius-md);
744 744 padding: 1.25rem;
745 - box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border);
746 - transition: transform 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
745 + box-shadow: var(--bevel-raised);
747 746 cursor: pointer;
748 747 }
749 748
750 749 .card:hover {
751 750 background-color: var(--surface-overlay);
752 - transform: translate(-2px, -2px);
753 - box-shadow: calc(var(--shadow-offset) + 2px) calc(var(--shadow-offset) + 2px) 0 var(--border);
751 + }
752 +
753 + /* A clickable card presses like a button. */
754 + .card:active {
755 + background-color: var(--surface-sunken);
756 + box-shadow: var(--bevel-inset);
754 757 }
755 758
756 759 /* Compact card for use in dense vertical lists (e.g. Project Dashboard).
757 - Tighter padding, square corners, adds list spacing. */
760 + Tighter padding, adds list spacing. */
758 761 .card--list-item {
759 762 padding: 0.75rem;
760 - border-radius: 0;
761 763 margin-bottom: 0.5rem;
762 764 }
763 765
@@ -771,10 +773,10 @@
771 773 flex-direction: column;
772 774 overflow: hidden;
773 775 }
774 - .card--shell:hover {
776 + .card--shell:hover,
777 + .card--shell:active {
775 778 background-color: var(--surface-raised);
776 - transform: none;
777 - box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border);
779 + box-shadow: var(--bevel-raised);
778 780 }
779 781
780 782 /* Muted info card, bg-secondary, no border/shadow/cursor/hover. For
@@ -785,9 +787,9 @@
785 787 box-shadow: none;
786 788 cursor: default;
787 789 }
788 - .card--muted:hover {
790 + .card--muted:hover,
791 + .card--muted:active {
789 792 background: var(--surface-overlay);
790 - transform: none;
791 793 box-shadow: none;
792 794 }
793 795
@@ -797,10 +799,10 @@
797 799 .card--static {
798 800 cursor: default;
799 801 }
800 - .card--static:hover {
802 + .card--static:hover,
803 + .card--static:active {
801 804 background-color: var(--surface-raised);
802 - transform: none;
803 - box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border);
805 + box-shadow: var(--bevel-raised);
804 806 }
805 807
806 808 .card-header {
@@ -859,6 +861,7 @@
859 861 font-weight: 600;
860 862 background: var(--surface-raised);
861 863 color: var(--content);
864 + box-shadow: var(--bevel-raised);
862 865 }
863 866
864 867 /* Color variants using data attributes */
@@ -895,8 +898,10 @@
895 898 .badge--xs { padding: 0.1rem 0.5rem; font-size: 0.75rem; }
896 899 .badge--sm { padding: 0.15rem 0.55rem; font-size: 0.78rem; }
897 900
898 - /* Filled intent, solid accent fill, no border. Pairs with data-color. */
899 - .badge--filled { border: none; }
901 + /* Filled intent, solid accent fill, no border. Flat by intent, so no bevel
902 + either: the fill is the signal and an edge would fight it. Pairs with
903 + data-color. */
904 + .badge--filled { border: none; box-shadow: none; }
900 905 .badge--filled[data-color="green"] { background: var(--category-two); color: var(--content-on-action); }
901 906 .badge--filled[data-color="yellow"] { background: var(--category-four); color: var(--content); }
902 907 .badge--filled[data-color="red"] { background: var(--category-one); color: var(--content-on-action); }
@@ -1959,15 +1964,19 @@
1959 1964 gap: 0.25rem;
1960 1965 padding: 0.25rem 0.5rem;
1961 1966 font-size: 0.8rem;
1962 - border: 1px solid var(--border);
1967 + border: var(--border-width) solid var(--border);
1963 1968 border-radius: var(--radius-sm);
1969 + background: var(--surface-raised);
1970 + box-shadow: var(--bevel-raised);
1964 1971 cursor: pointer;
1965 1972 }
1966 1973
1974 + /* A checked weekday is a latched button, so it takes the pressed bevel. */
1967 1975 .recurrence-weekday-label:has(input:checked) {
1968 1976 background: var(--action);
1969 1977 color: var(--surface-page);
1970 1978 border-color: var(--action);
1979 + box-shadow: var(--bevel-inset);
1971 1980 }
1972 1981
1973 1982 .recurrence-weekday-label input[type="checkbox"] {
@@ -1998,15 +2007,16 @@
1998 2007 background-color: var(--surface-raised);
1999 2008 color: var(--content);
2000 2009 font-size: 1rem;
2001 - box-shadow: none;
2010 + box-shadow: var(--bevel-inset);
2002 2011 }
2003 2012
2013 + /* The focus ring sits outside the well, so the inset bevel stays under it. */
2004 2014 .form-input:focus,
2005 2015 .form-select:focus,
2006 2016 .form-textarea:focus {
2007 2017 outline: none;
2008 2018 background-color: var(--surface-raised);
2009 - box-shadow: 0 0 0 2px var(--action);
2019 + box-shadow: var(--bevel-inset), 0 0 0 2px var(--action);
2010 2020 }
2011 2021
2012 2022 .form-textarea {
@@ -2019,6 +2029,7 @@
2019 2029 .form-input--ghost {
2020 2030 background: transparent;
2021 2031 border: none;
2032 + box-shadow: none;
2022 2033 padding: 0.5rem;
2023 2034 font-size: 0.875rem;
2024 2035 }
@@ -2041,13 +2052,13 @@
2041 2052 .form-select[aria-invalid="true"],
2042 2053 .form-textarea[aria-invalid="true"] {
2043 2054 border-color: var(--danger);
2044 - box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 30%, transparent);
2055 + box-shadow: var(--bevel-inset), 0 0 0 2px color-mix(in srgb, var(--danger) 30%, transparent);
2045 2056 }
2046 2057
2047 2058 .form-input[aria-invalid="true"]:focus,
2048 2059 .form-select[aria-invalid="true"]:focus,
2049 2060 .form-textarea[aria-invalid="true"]:focus {
2050 - box-shadow: 0 0 0 2px var(--danger);
2061 + box-shadow: var(--bevel-inset), 0 0 0 2px var(--danger);
2051 2062 }
2052 2063
2053 2064 .form-error {
@@ -3464,7 +3475,6 @@
3464 3475
3465 3476 .btn-link:hover {
3466 3477 box-shadow: none;
3467 - transform: none;
3468 3478 color: var(--content);
3469 3479 }
3470 3480
@@ -5310,6 +5320,7 @@
5310 5320 .btn-icon {
5311 5321 background: none;
5312 5322 border: none;
5323 + box-shadow: none;
5313 5324 color: var(--content-muted);
5314 5325 cursor: pointer;
5315 5326 padding: 0.25rem;
@@ -6585,14 +6596,8 @@
6585 6596 outline-offset: 2px;
6586 6597 }
6587 6598
6588 - /* Suggested task buttons transitions */
6589 - .focus-section .btn {
6590 - transition: transform 0.15s ease-out, opacity 0.15s ease-out;
6591 - }
6592 -
6593 - .focus-section .btn:active {
6594 - transform: scale(0.97);
6595 - }
6599 + /* Suggested task buttons carry no press hack of their own: .btn:active
6600 + inverts its bevel, which is the pressed state for every button. */
6596 6601
6597 6602 /* §52 vacated 2026-05-24, weekly-review print styles consolidated into §48. */
6598 6603
@@ -8438,8 +8443,7 @@
8438 8443
8439 8444 .card:hover {
8440 8445 background-color: var(--surface-raised);
8441 - transform: none;
8442 - box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border);
8446 + box-shadow: var(--bevel-raised);
8443 8447 }
8444 8448
8445 8449 .btn:hover {
@@ -8454,12 +8458,6 @@
8454 8458 background-color: var(--danger);
8455 8459 }
8456 8460
8457 - .card:hover {
8458 - background-color: var(--surface-raised);
8459 - transform: none;
8460 - box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border);
8461 - }
8462 -
8463 8461 .kanban-card:hover {
8464 8462 background: var(--surface-raised);
8465 8463 transform: none;