diff design_system/src/styles/components.css @ 258:60a876c4587a

[ui] Add semantic primitive ownership Build a layered Zenbu token and sizing system, make authored controls use native-underneath primitives, migrate mrjunejune without imposing visual surfaces, and document/enforce HTML ownership in the catalog and wiki. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Wed, 05 Aug 2026 05:25:40 -0700
parents 2b6e732087ff
children
line wrap: on
line diff
--- a/design_system/src/styles/components.css	Tue Aug 04 16:49:11 2026 -0700
+++ b/design_system/src/styles/components.css	Wed Aug 05 05:25:40 2026 -0700
@@ -1,4 +1,4 @@
-@import url("./shadcn.css");
+@import url("./elements.css");
 
 :where(
   zen-button,
@@ -9,78 +9,128 @@
   zen-notifications
 ) {
   box-sizing: border-box;
-  font-family: var(--zen-font-sans);
-  color: var(--zen-color-text);
+  font-family: var(--zenbu-sys-font-family-ui);
+  color: var(--zenbu-sys-color-text-primary);
 }
 
 zen-button {
   display: inline-flex;
 }
 
-zen-button > :where(button, a) {
+zen-button:not([appearance="plain"]) > :where(button, a) {
+  --zenbu-button-background:
+    var(--zenbu-sys-color-action-primary-background);
+  --zenbu-button-background-hover:
+    var(--zenbu-sys-color-action-primary-background-hover);
+  --zenbu-button-background-pressed:
+    var(--zenbu-sys-color-action-primary-background-pressed);
+  --zenbu-button-border:
+    var(--zenbu-sys-color-action-primary-border);
+  --zenbu-button-foreground:
+    var(--zenbu-sys-color-action-primary-foreground);
   appearance: none;
+  box-sizing: border-box;
   display: inline-flex;
   align-items: center;
   justify-content: center;
-  gap: var(--zen-space-2);
-  min-height: 2.75rem;
-  padding: var(--zen-space-2) var(--zen-space-4);
-  border: var(--zen-border-width) solid var(--zen-color-border);
-  border-radius: var(--zen-radius-md);
-  box-shadow: var(--zen-shadow-sm);
-  background: var(--zen-color-accent);
-  color: var(--zen-color-on-accent);
-  font: 700 var(--zen-font-size-md)/1 var(--zen-font-sans);
+  gap: var(--zenbu-control-gap);
+  min-height: var(--zenbu-control-height);
+  padding:
+    var(--zenbu-control-padding-block)
+    var(--zenbu-control-padding-inline);
+  border: var(--zenbu-sys-stroke-width) solid var(--zenbu-button-border);
+  border-radius: var(--zenbu-sys-radius-control);
+  box-shadow: var(--zenbu-sys-shadow-subtle);
+  background: var(--zenbu-button-background);
+  color: var(--zenbu-button-foreground);
+  font:
+    var(--zenbu-sys-font-weight-strong)
+    var(--zenbu-control-font-size) /
+    var(--zenbu-control-line-height)
+    var(--zenbu-sys-font-family-ui);
   text-decoration: none;
   cursor: pointer;
   transition:
-    background var(--zen-motion-fast) ease,
-    box-shadow var(--zen-motion-fast) ease,
-    transform var(--zen-motion-fast) ease;
+    background var(--zenbu-sys-motion-duration-state)
+      var(--zenbu-sys-motion-ease-state),
+    border-color var(--zenbu-sys-motion-duration-state)
+      var(--zenbu-sys-motion-ease-state),
+    box-shadow var(--zenbu-sys-motion-duration-feedback)
+      var(--zenbu-sys-motion-ease-state),
+    transform var(--zenbu-sys-motion-duration-feedback)
+      var(--zenbu-sys-motion-ease-state);
 }
 
-zen-button > :where(button, a):hover {
-  background: var(--zen-color-accent-hover);
+zen-button:not([appearance="plain"]) > :where(button, a):hover:not(
+  :disabled,
+  [aria-disabled="true"]
+) {
+  background: var(--zenbu-button-background-hover);
   transform: translate(-1px, -1px);
-  box-shadow: var(--zen-shadow-hover);
+  box-shadow: var(--zenbu-sys-shadow-raised);
 }
 
-zen-button > :where(button, a):active {
+zen-button:not([appearance="plain"]) > :where(button, a):active:not(
+  :disabled,
+  [aria-disabled="true"]
+) {
+  background: var(--zenbu-button-background-pressed);
   transform: translate(2px, 2px);
   box-shadow: none;
 }
 
-zen-button > :where(button, a):focus-visible,
+zen-button:not([appearance="plain"]) > :where(button, a):focus-visible,
 zen-alert [data-zen-dismiss]:focus-visible {
-  outline: 3px solid var(--zen-color-focus);
-  outline-offset: 3px;
+  outline: var(--zenbu-sys-focus-width) solid
+    var(--zenbu-sys-color-focus-outer);
+  outline-offset: var(--zenbu-sys-focus-offset);
 }
 
-zen-field :where(input, select, textarea):focus-visible {
+zen-field:not([appearance="plain"]) :where(
+  input,
+  select,
+  textarea
+):focus-visible {
   outline: none;
 }
 
-zen-button[variant="quiet"] > :where(button, a) {
-  background: var(--zen-color-surface);
-  color: var(--zen-color-text);
+zen-button:not([appearance="plain"])[variant="quiet"] > :where(button, a) {
+  --zenbu-button-background:
+    var(--zenbu-sys-color-action-neutral-background);
+  --zenbu-button-background-hover:
+    var(--zenbu-sys-color-action-neutral-background-hover);
+  --zenbu-button-background-pressed:
+    var(--zenbu-sys-color-action-neutral-background-pressed);
+  --zenbu-button-border:
+    var(--zenbu-sys-color-action-neutral-border);
+  --zenbu-button-foreground:
+    var(--zenbu-sys-color-action-neutral-foreground);
 }
 
-zen-button[variant="danger"] > :where(button, a) {
-  background: var(--zen-color-danger);
-  color: var(--zen-color-on-danger);
+zen-button:not([appearance="plain"])[variant="danger"] > :where(button, a) {
+  --zenbu-button-background:
+    var(--zenbu-sys-color-action-danger-background);
+  --zenbu-button-background-hover:
+    var(--zenbu-sys-color-action-danger-background-hover);
+  --zenbu-button-background-pressed:
+    var(--zenbu-sys-color-action-danger-background-pressed);
+  --zenbu-button-border:
+    var(--zenbu-sys-color-action-danger-border);
+  --zenbu-button-foreground:
+    var(--zenbu-sys-color-action-danger-foreground);
 }
 
-zen-button[disabled] > :where(button, a),
-zen-button[loading] > :where(button, a) {
-  opacity: 0.55;
+zen-button:not([appearance="plain"])[disabled] > :where(button, a),
+zen-button:not([appearance="plain"])[loading] > :where(button, a) {
+  opacity: var(--zenbu-sys-opacity-disabled);
   box-shadow: none;
   cursor: not-allowed;
   transform: none;
 }
 
-zen-button[loading] > :where(button, a)::before {
-  width: 0.85rem;
-  height: 0.85rem;
+zen-button:not([appearance="plain"])[loading] > :where(button, a)::before {
+  width: var(--zenbu-control-icon-size);
+  height: var(--zenbu-control-icon-size);
   border: 2px solid currentColor;
   border-right-color: transparent;
   border-radius: 50%;
@@ -96,24 +146,30 @@
   display: block;
 }
 
-zen-card > :where(article, section) {
+zen-card:not([appearance="plain"]) > :where(article, section) {
+  --zenbu-card-background: var(--zenbu-sys-color-surface-raised);
+  --zenbu-card-border: var(--zenbu-sys-color-border-subtle);
+  --zenbu-card-foreground: var(--zenbu-sys-color-text-primary);
   height: 100%;
-  padding: var(--zen-space-5);
-  border: var(--zen-border-width) solid var(--zen-color-border);
-  border-radius: var(--zen-radius-lg);
-  box-shadow: var(--zen-shadow-md);
-  background: var(--zen-color-surface-raised);
+  padding: var(--zenbu-sys-padding-lg);
+  border: var(--zenbu-sys-stroke-width) solid var(--zenbu-card-border);
+  border-radius: var(--zenbu-sys-radius-container);
+  box-shadow: var(--zenbu-sys-shadow-raised);
+  background: var(--zenbu-card-background);
+  color: var(--zenbu-card-foreground);
 }
 
-zen-card[interactive] > :where(article, section) {
+zen-card:not([appearance="plain"])[interactive] > :where(article, section) {
   transition:
-    box-shadow var(--zen-motion-fast) ease,
-    transform var(--zen-motion-fast) ease;
+    box-shadow var(--zenbu-sys-motion-duration-state)
+      var(--zenbu-sys-motion-ease-state),
+    transform var(--zenbu-sys-motion-duration-state)
+      var(--zenbu-sys-motion-ease-state);
 }
 
-zen-card[interactive] > :where(article, section):hover {
+zen-card:not([appearance="plain"])[interactive] > :where(article, section):hover {
   transform: translate(-2px, -2px);
-  box-shadow: var(--zen-shadow-lifted);
+  box-shadow: var(--zenbu-sys-shadow-overlay);
 }
 
 zen-card :where(h2, h3, p) {
@@ -121,85 +177,123 @@
 }
 
 zen-alert {
+  --zenbu-alert-background: var(--zenbu-sys-color-info-background);
+  --zenbu-alert-border: var(--zenbu-sys-color-info-border);
+  --zenbu-alert-foreground: var(--zenbu-sys-color-info-foreground);
   display: grid;
-  grid-template-columns: 0.35rem minmax(0, 1fr) auto;
+  --zenbu-alert-indicator-width: var(--zenbu-ref-space-1-5);
+  grid-template-columns:
+    var(--zenbu-alert-indicator-width)
+    minmax(0, 1fr)
+    auto;
   align-items: start;
-  gap: var(--zen-space-3);
-  padding: var(--zen-space-4);
-  border: var(--zen-border-width) solid var(--zen-color-border);
-  border-radius: var(--zen-radius-md);
-  background: var(--zen-color-surface-raised);
+  gap: var(--zenbu-sys-space-control);
+  padding: var(--zenbu-sys-padding-md);
+  border: var(--zenbu-sys-stroke-width) solid var(--zenbu-alert-border);
+  border-radius: var(--zenbu-sys-radius-container);
+  background: var(--zenbu-alert-background);
+  color: var(--zenbu-alert-foreground);
 }
 
 zen-alert::before {
   align-self: stretch;
-  border-radius: 999px;
-  background: var(--zen-color-info);
+  border-radius: var(--zenbu-sys-radius-pill);
+  background: var(--zenbu-alert-border);
   content: "";
 }
 
-zen-alert[tone="success"]::before { background: var(--zen-color-success); }
-zen-alert[tone="warning"]::before { background: var(--zen-color-warning); }
-zen-alert[tone="danger"]::before { background: var(--zen-color-danger); }
+zen-alert[tone="success"] {
+  --zenbu-alert-background: var(--zenbu-sys-color-success-background);
+  --zenbu-alert-border: var(--zenbu-sys-color-success-border);
+  --zenbu-alert-foreground: var(--zenbu-sys-color-success-foreground);
+}
+zen-alert[tone="warning"] {
+  --zenbu-alert-background: var(--zenbu-sys-color-warning-background);
+  --zenbu-alert-border: var(--zenbu-sys-color-warning-border);
+  --zenbu-alert-foreground: var(--zenbu-sys-color-warning-foreground);
+}
+zen-alert[tone="danger"] {
+  --zenbu-alert-background: var(--zenbu-sys-color-danger-background);
+  --zenbu-alert-border: var(--zenbu-sys-color-danger-border);
+  --zenbu-alert-foreground: var(--zenbu-sys-color-danger-foreground);
+}
 
 zen-alert > :where(p, div) {
   margin: 0;
 }
 
 zen-alert [data-zen-dismiss] {
-  min-width: 2rem;
-  min-height: 2rem;
+  min-width: var(--zenbu-sys-control-height-xs);
+  min-height: var(--zenbu-sys-control-height-xs);
   padding: 0;
   border: 0;
-  border-radius: 50%;
+  border-radius: var(--zenbu-sys-radius-pill);
   background: transparent;
   color: inherit;
-  font: 700 1.2rem/1 var(--zen-font-sans);
+  font:
+    var(--zenbu-sys-font-weight-bold)
+    var(--zenbu-sys-font-size-lg) / 1
+    var(--zenbu-sys-font-family-ui);
   cursor: pointer;
 }
 
 zen-field {
+  --zenbu-field-textarea-min-height:
+    calc(var(--zenbu-ref-space-24) + var(--zenbu-ref-space-4));
   display: grid;
-  gap: var(--zen-space-2);
+  gap: var(--zenbu-sys-space-icon-label);
 }
 
-zen-field > label {
-  font-weight: 750;
+zen-field[appearance="plain"] {
+  display: contents;
+}
+
+zen-field:not([appearance="plain"]) > label {
+  font-family: var(--zenbu-sys-type-label-family);
+  font-size: var(--zenbu-sys-type-label-size);
+  font-weight: var(--zenbu-sys-type-label-weight);
+  line-height: var(--zenbu-sys-type-label-line-height);
 }
 
-zen-field > :where(input, select, textarea) {
+zen-field:not([appearance="plain"]) > :where(input, select, textarea) {
   width: 100%;
-  min-height: 2.75rem;
+  min-height: var(--zenbu-control-height);
   box-sizing: border-box;
-  padding: var(--zen-space-3);
-  border: var(--zen-border-width) solid var(--zen-color-border);
-  border-radius: var(--zen-radius-md);
-  background: var(--zen-color-surface);
-  color: var(--zen-color-text);
-  font: inherit;
+  padding:
+    var(--zenbu-control-padding-block)
+    var(--zenbu-control-padding-inline);
+  border: var(--zenbu-sys-stroke-width) solid
+    var(--zenbu-sys-color-border-default);
+  border-radius: var(--zenbu-sys-radius-control);
+  background: var(--zenbu-sys-color-surface-default);
+  color: var(--zenbu-sys-color-text-primary);
+  font-family: inherit;
+  font-size: var(--zenbu-control-font-size);
+  line-height: var(--zenbu-control-line-height);
 }
 
-zen-field > textarea {
-  min-height: 7rem;
+zen-field:not([appearance="plain"]) > textarea {
+  min-height: var(--zenbu-field-textarea-min-height);
   resize: vertical;
 }
 
-zen-field > :where(small, [data-help]) {
-  color: var(--zen-color-text-muted);
+zen-field:not([appearance="plain"]) > :where(small, [data-help]) {
+  color: var(--zenbu-sys-color-text-secondary);
 }
 
-zen-field[data-invalid] > :where(input, select, textarea) {
-  border-color: var(--zen-color-danger);
+zen-field:not([appearance="plain"])[data-invalid] > :where(input, select, textarea) {
+  border-color: var(--zenbu-sys-color-danger-border);
 }
 
-zen-field[data-invalid] > :where(small, [data-help]) {
-  color: var(--zen-color-danger);
+zen-field:not([appearance="plain"])[data-invalid] > :where(small, [data-help]) {
+  color: var(--zenbu-sys-color-danger-foreground);
 }
 
 zen-stack {
+  --zenbu-stack-gap: var(--zenbu-sys-space-group);
   display: flex;
   flex-direction: column;
-  gap: var(--zen-stack-gap, var(--zen-space-4));
+  gap: var(--zenbu-stack-gap);
 }
 
 zen-stack[direction="row"] {
@@ -207,59 +301,89 @@
   align-items: center;
 }
 
-zen-stack[gap="tight"] { --zen-stack-gap: var(--zen-space-2); }
-zen-stack[gap="loose"] { --zen-stack-gap: var(--zen-space-6); }
+zen-stack[gap="tight"] {
+  --zenbu-stack-gap: var(--zenbu-sys-space-related);
+}
+zen-stack[gap="loose"] {
+  --zenbu-stack-gap: var(--zenbu-sys-space-container);
+}
 
 zen-notifications > [data-zen-notification-stack] {
+  --zenbu-notification-z-index: var(--zenbu-sys-z-notification);
+  --zenbu-notification-inset: var(--zenbu-sys-padding-md);
+  --zenbu-notification-bottom-inset: var(--zenbu-sys-padding-md);
+  --zenbu-notification-collapsed-height: 0;
+  --zenbu-notification-expanded-height: 0;
   position: fixed;
-  z-index: var(--zen-notification-z-index, 1000);
-  inset-inline-end: var(--zen-notification-inset, var(--zen-space-4));
+  z-index: var(
+    --zenbu-notification-z-index,
+    var(--zenbu-sys-z-notification)
+  );
+  inset-inline-end: var(
+    --zenbu-notification-inset,
+    var(--zenbu-sys-padding-md)
+  );
   inset-block-end: var(
-    --zen-notification-bottom-inset,
-    var(--zen-space-4)
+    --zenbu-notification-bottom-inset,
+    var(--zenbu-sys-padding-md)
   );
   inline-size: min(24rem, calc(100vi - 2rem));
-  height: var(--zen-notification-collapsed-height, 0);
-  --zen-notification-gap: 12px;
+  height: var(--zenbu-notification-collapsed-height, 0);
+  --zenbu-notification-gap: var(--zenbu-sys-space-control);
   pointer-events: auto;
   isolation: isolate;
-  transition: height 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
+  transition:
+    height var(--zenbu-sys-motion-duration-layout)
+      var(--zenbu-sys-motion-ease-layout);
 }
 
 zen-notifications > [data-zen-notification-stack]:is(
   :hover,
   :focus-within
 ) {
-  height: var(--zen-notification-expanded-height, 0);
+  height: var(--zenbu-notification-expanded-height, 0);
 }
 
 zen-notifications > [data-zen-notification-stack] > article {
+  --zenbu-notification-collapsed-opacity: 1;
+  --zenbu-notification-collapsed-offset: 0;
+  --zenbu-notification-offset: 0;
+  --zenbu-notification-scale: 1;
+  --zenbu-notification-background:
+    var(--zenbu-sys-color-surface-raised);
+  --zenbu-notification-border:
+    var(--zenbu-sys-color-border-subtle);
+  --zenbu-notification-foreground:
+    var(--zenbu-sys-color-text-primary);
   position: absolute;
   inset-inline: 0;
   inset-block-end: 0;
   display: grid;
   grid-template-columns: 1.75rem minmax(0, 1fr) 2rem;
   align-items: start;
-  gap: var(--zen-space-3);
-  padding: var(--zen-space-4);
-  border: var(--zen-border-width) solid var(--zen-color-border);
-  border-radius: var(--zen-radius-lg);
-  box-shadow: var(--zen-shadow-md);
-  background: var(--zen-color-surface-raised);
-  color: var(--zen-color-text);
-  opacity: var(--zen-notification-collapsed-opacity, 1);
+  gap: var(--zenbu-sys-space-control);
+  padding: var(--zenbu-sys-padding-md);
+  border: var(--zenbu-sys-stroke-width) solid
+    var(--zenbu-notification-border);
+  border-radius: var(--zenbu-sys-radius-container);
+  box-shadow: var(--zenbu-sys-shadow-overlay);
+  background: var(--zenbu-notification-background);
+  color: var(--zenbu-notification-foreground);
+  opacity: var(--zenbu-notification-collapsed-opacity, 1);
   pointer-events: none;
   transform:
     translate3d(
       0,
-      var(--zen-notification-collapsed-offset, 0),
+      var(--zenbu-notification-collapsed-offset, 0),
       0
     )
-    scale(var(--zen-notification-scale, 1));
+    scale(var(--zenbu-notification-scale, 1));
   transform-origin: center bottom;
   transition:
-    opacity 180ms ease,
-    transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
+    opacity var(--zenbu-sys-motion-duration-exit)
+      var(--zenbu-sys-motion-ease-exit),
+    transform var(--zenbu-sys-motion-duration-layout)
+      var(--zenbu-sys-motion-ease-layout);
   will-change: transform;
   animation: zen-notification-enter 160ms ease-out;
 }
@@ -277,7 +401,7 @@
   transform:
     translate3d(
       0,
-      var(--zen-notification-offset, 0px),
+      var(--zenbu-notification-offset, 0px),
       0
     )
     scale(1);
@@ -290,34 +414,38 @@
 }
 
 .zen-notification-tone {
+  --zenbu-notification-tone-background:
+    var(--zenbu-sys-color-info-foreground);
+  --zenbu-notification-tone-foreground:
+    var(--zenbu-sys-color-text-inverse);
   display: grid;
   width: 1.75rem;
   height: 1.75rem;
   place-items: center;
-  border-radius: 50%;
-  background: var(--zen-color-info);
-  color: var(--zen-color-on-info);
-  font-weight: 900;
+  border-radius: var(--zenbu-sys-radius-pill);
+  background: var(--zenbu-notification-tone-background);
+  color: var(--zenbu-notification-tone-foreground);
+  font-weight: var(--zenbu-sys-font-weight-bold);
 }
 
 [data-tone="success"] > .zen-notification-tone {
-  background: var(--zen-color-success);
-  color: var(--zen-color-on-success);
+  --zenbu-notification-tone-background:
+    var(--zenbu-sys-color-success-foreground);
 }
 
 [data-tone="warning"] > .zen-notification-tone {
-  background: var(--zen-color-warning);
-  color: var(--zen-color-on-warning);
+  --zenbu-notification-tone-background:
+    var(--zenbu-sys-color-warning-foreground);
 }
 
 [data-tone="error"] > .zen-notification-tone {
-  background: var(--zen-color-danger);
-  color: var(--zen-color-on-danger);
+  --zenbu-notification-tone-background:
+    var(--zenbu-sys-color-danger-foreground);
 }
 
 .zen-notification-content {
   display: grid;
-  gap: var(--zen-space-1);
+  gap: var(--zenbu-sys-space-icon-label);
 }
 
 .zen-notification-message,
@@ -326,12 +454,12 @@
 }
 
 .zen-notification-message {
-  font-weight: 800;
+  font-weight: var(--zenbu-sys-font-weight-bold);
 }
 
 .zen-notification-description {
-  color: var(--zen-color-text-muted);
-  font-size: var(--zen-font-size-sm);
+  color: var(--zenbu-sys-color-text-secondary);
+  font-size: var(--zenbu-sys-font-size-sm);
 }
 
 .zen-notification-action,
@@ -344,10 +472,10 @@
 
 .zen-notification-action {
   justify-self: start;
-  margin-top: var(--zen-space-2);
+  margin-top: var(--zenbu-sys-space-related);
   padding: 0;
-  color: var(--zen-color-info);
-  font-weight: 800;
+  color: var(--zenbu-sys-color-text-link);
+  font-weight: var(--zenbu-sys-font-weight-bold);
   text-decoration: underline;
   text-underline-offset: 0.2em;
 }
@@ -356,14 +484,15 @@
   width: 2rem;
   height: 2rem;
   padding: 0;
-  border-radius: 50%;
-  font-size: 1.25rem;
+  border-radius: var(--zenbu-sys-radius-pill);
+  font-size: var(--zenbu-sys-font-size-xl);
 }
 
 .zen-notification-action:focus-visible,
 .zen-notification-dismiss:focus-visible {
-  outline: 3px solid var(--zen-color-focus);
-  outline-offset: 2px;
+  outline: var(--zenbu-sys-focus-width) solid
+    var(--zenbu-sys-color-focus-outer);
+  outline-offset: var(--zenbu-sys-focus-offset);
 }
 
 .zen-visually-hidden {