Skip to main content

max / goingson

Make the top-level nav folder tabs over a single pane Two structural deltas against the specimen, both desktop-scoped. Drops the app name and subtitle from the content area. The title bar carries the app name already, and freeing the header row is what makes the tab work possible. Turns that row into the tab strip. A pressed toggle says the control is on and says nothing about what is below it, which is why the hierarchy did not read: .app-header and .main-content were two bands rather than a strip and its pane. The header now sits on sunken ground at the pane's width, its border-bottom is the pane's top line, and the selected tab fills with the pane's own surface and overlaps that line so the two read as one shape. Tabs lost their bevel for the same reason. The sub-level takes an underline strip instead of a second tab row, per the 2026-07-28 call. Two tab rows give back the ambiguity the first row resolves. Also drops .pill.active .pill-count, which existed only to survive the dark pressed fill the active pill no longer has.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-29 00:05 UTC
Signed with PGP, not checked
Commit: 306597a188d9d7fd52718380ad5828f2cb01acd5
Parent: 4f28357
2 files changed, +76 insertions, -52 deletions
@@ -38,8 +38,6 @@
38 38 <a href="#main-content" class="skip-link">Skip to main content</a>
39 39 <header class="app-header">
40 40 <div class="header-content">
41 - <h1 class="app-title">GoingsOn</h1>
42 - <span class="app-subtitle">Personal Productivity</span>
43 41 <span class="mobile-view-title" id="mobile-view-title"></span>
44 42 </div>
45 43 <nav class="tab-navigation" role="tablist" aria-label="Main navigation">
@@ -434,14 +434,22 @@
434 434
435 435 /* 6. Header (desktop UI only)
436 436 The top app header is part of desktop layout. Mobile UI hides it
437 - wholesale; rules below are scoped so they cannot leak in. */
437 + wholesale; rules below are scoped so they cannot leak in.
438 +
439 + The header is the tab strip, not a band of its own. It sits on the page
440 + ground in --surface-sunken, carries the line that the pane hangs from
441 + (border-bottom), and shares the pane's width so the two read as one
442 + object. §7 has the tab semantic; §8 has the pane. */
438 443 .ui-mode-desktop .app-header {
439 - background: var(--surface-raised);
444 + width: 100%;
445 + max-width: var(--width-container);
446 + margin: 0 auto;
447 + background: var(--surface-sunken);
440 448 border-bottom: var(--border-width) solid var(--border);
441 - padding: var(--gap-group) var(--gap-pane);
449 + padding: var(--gap-peer) var(--gap-peer) 0;
442 450 display: flex;
443 - justify-content: space-between;
444 - align-items: center;
451 + align-items: flex-end;
452 + gap: var(--gap-section);
445 453 }
446 454
447 455 .ui-mode-desktop .header-content {
@@ -450,25 +458,17 @@
450 458 gap: var(--gap-bound);
451 459 }
452 460
461 + /* Utility controls ride the strip. Lifted off the line by the same step the
462 + tabs stand on, so they do not read as sitting in the pane. */
453 463 .ui-mode-desktop .header-actions {
454 464 display: flex;
455 465 align-items: center;
456 466 gap: var(--gap-peer);
457 - }
458 -
459 - .ui-mode-desktop .app-title {
460 - font-family: var(--font-display);
461 - font-size: var(--font-size-4xl);
462 - font-weight: 700;
463 - color: var(--content);
464 - letter-spacing: -0.02em;
465 - }
466 -
467 - .ui-mode-desktop .app-subtitle {
468 - font-size: var(--font-size-base);
469 - color: var(--content-muted);
470 - font-weight: 500;
471 - line-height: 1;
467 + padding-bottom: var(--gap-peer);
468 + /* The tabs own the left edge; everything else is pushed to the far end.
469 + Not space-between: .header-content is empty on desktop and would take
470 + the left slot, floating the tabs off the pane's edge. */
471 + margin-left: auto;
472 472 }
473 473
474 474 .mobile-view-title {
@@ -476,11 +476,18 @@
476 476 }
477 477
478 478 /* 7. Tab Navigation (desktop UI only)
479 - The top tab strip is desktop-only; mobile UI uses the bottom tab bar. */
479 + The top tab strip is desktop-only; mobile UI uses the bottom tab bar.
480 +
481 + Folder tabs, not toggles. A pressed toggle says "this control is on" and
482 + says nothing about what is below it; a folder tab's selected state removes
483 + the line between itself and the pane, so the two read as one object. That
484 + is the whole semantic, and it is why the tab carries no bevel: a bevel
485 + makes it an object sitting on the strip rather than the front edge of the
486 + pane. Tabs run from the left edge, which is the pane's left edge. */
480 487 .ui-mode-desktop .tab-navigation {
481 488 display: flex;
482 - justify-content: center;
483 - gap: var(--gap-peer);
489 + align-items: flex-end;
490 + gap: var(--gap-bound);
484 491 }
485 492
486 493 .ui-mode-desktop .tab {
@@ -489,23 +496,30 @@
489 496 gap: var(--gap-bound);
490 497 padding: var(--gap-peer) var(--gap-section);
491 498 text-decoration: none;
492 - color: var(--content);
493 - background: var(--surface-raised);
499 + color: var(--content-secondary);
500 + background: var(--surface-sunken);
494 501 border: var(--border-width) solid var(--border);
495 - border-radius: var(--radius-md);
502 + border-bottom: 0;
503 + border-radius: var(--radius-sm) var(--radius-sm) 0 0;
496 504 font-weight: 600;
497 - box-shadow: var(--bevel-raised);
498 505 }
499 506
500 507 .ui-mode-desktop .tab:hover {
501 508 background: var(--surface-overlay);
509 + color: var(--content);
502 510 }
503 511
504 - /* The selected tab is latched down, same inversion as a pressed button. */
512 + /* The selected tab is filled with the pane's own surface and overlaps the
513 + strip's bottom line, breaking it: tab and pane become one shape. The
514 + specimen writes the overlap as `top: 1px` on the unselected tabs; a
515 + negative bottom margin says the same thing with the direction explicit,
516 + and the matching padding keeps every tab's label on one baseline. */
505 517 .ui-mode-desktop .tab.active {
506 - background-color: var(--action);
507 - color: var(--content-on-action);
508 - box-shadow: var(--bevel-inset);
518 + position: relative;
519 + background: var(--surface-raised);
520 + color: var(--content);
521 + margin-bottom: calc(-1 * var(--border-width));
522 + padding-bottom: calc(var(--gap-peer) + var(--border-width));
509 523 }
510 524
511 525 .ui-mode-desktop .tab-label {
@@ -519,36 +533,46 @@
519 533 }
520 534
521 535 /* --- Pill Sub-Navigation (desktop UI only, mobile uses slide menu) --- */
536 + /* An underline strip, not a second row of folder tabs. Two tab rows put two
537 + competing "this contains what follows" claims on the screen and give back
538 + the ambiguity the top row just resolved, so the sub-level says selected
539 + with a rule under the label and nothing else. Classic Platinum did nest
540 + tabs; this is a call, not a constraint (decided 2026-07-28). */
522 541 .ui-mode-desktop .pill-nav {
523 542 display: flex;
524 - align-items: center;
525 - gap: var(--gap-peer);
543 + align-items: stretch;
544 + gap: var(--gap-section);
526 545 padding: 0;
527 546 margin-bottom: var(--gap-section);
528 547 min-height: 2rem;
548 + border-bottom: var(--border-width) solid var(--border);
529 549 }
530 550
531 551 .ui-mode-desktop .pill {
532 - padding: var(--gap-bound) var(--gap-section);
533 - border-radius: var(--radius-xl);
534 - border: var(--border-width-sm) solid var(--border);
535 - background: var(--surface-raised);
552 + padding: var(--gap-bound) var(--gap-peer);
553 + border: 0;
554 + /* Sits on the strip's own line, so it has to outweigh it when lit. */
555 + border-bottom: calc(var(--border-width) * 2) solid transparent;
556 + margin-bottom: calc(-1 * var(--border-width));
557 + border-radius: 0;
558 + background: none;
559 + box-shadow: none;
560 + color: var(--content-secondary);
536 561 font-family: var(--font-sans);
537 562 font-size: var(--font-size-sm);
538 563 font-weight: 600;
539 564 cursor: pointer;
540 - box-shadow: var(--bevel-raised);
541 565 }
542 566
543 567 .ui-mode-desktop .pill:hover {
544 - background: var(--surface-sunken);
568 + background: none;
569 + color: var(--content);
545 570 }
546 571
547 572 .ui-mode-desktop .pill.active {
548 - background: var(--content);
549 - color: var(--surface-raised);
550 - border-color: var(--content);
551 - box-shadow: var(--bevel-inset);
573 + background: none;
574 + color: var(--content);
575 + border-bottom-color: var(--action);
552 576 }
553 577
554 578 /* 8. Main Content & Page Header */
@@ -560,6 +584,15 @@
560 584 padding: var(--gap-pane) var(--gap-page) var(--gap-page);
561 585 }
562 586
587 + /* The pane the tab strip belongs to. Same width as the strip (§6), so the
588 + two share a left and a right edge; no top border, because the strip's
589 + border-bottom already is that line and drawing it twice would show. */
590 + .ui-mode-desktop .main-content {
591 + background: var(--surface-raised);
592 + border: var(--border-width) solid var(--border);
593 + border-top: 0;
594 + }
595 +
563 596 /* Page Header */
564 597 .page-header {
565 598 display: flex;
@@ -9147,13 +9180,6 @@
9147 9180 text-align: center;
9148 9181 }
9149 9182
9150 - /* On the active (inverted) pill the red badge loses contrast, so flip it to the
9151 - pill's own background. */
9152 - .ui-mode-desktop .pill.active .pill-count {
9153 - background: var(--surface-raised);
9154 - color: var(--content);
9155 - }
9156 -
9157 9183 .view-hint {
9158 9184 margin: 0 0 var(--gap-section);
9159 9185 font-size: var(--font-size-sm);