diff design_system/src/styles/components.css @ 252:7a7581f040e8

[ui] Add scoped notification component Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Tue, 04 Aug 2026 11:57:16 -0700
parents 117c4d53c9a4
children fdf3816959cb
line wrap: on
line diff
--- a/design_system/src/styles/components.css	Tue Aug 04 09:14:57 2026 -0700
+++ b/design_system/src/styles/components.css	Tue Aug 04 11:57:16 2026 -0700
@@ -1,4 +1,11 @@
-:where(zen-button, zen-card, zen-alert, zen-field, zen-stack) {
+:where(
+  zen-button,
+  zen-card,
+  zen-alert,
+  zen-field,
+  zen-stack,
+  zen-notifications
+) {
   box-sizing: border-box;
   font-family: var(--zen-font-sans);
   color: var(--zen-color-text);
@@ -198,6 +205,136 @@
 zen-stack[gap="tight"] { --zen-stack-gap: var(--zen-space-2); }
 zen-stack[gap="loose"] { --zen-stack-gap: var(--zen-space-6); }
 
+zen-notifications > [data-zen-notification-stack] {
+  position: fixed;
+  z-index: var(--zen-notification-z-index, 1000);
+  inset-inline-end: var(--zen-notification-inset, var(--zen-space-4));
+  inset-block-end: var(
+    --zen-notification-bottom-inset,
+    var(--zen-space-4)
+  );
+  display: grid;
+  inline-size: min(24rem, calc(100vi - 2rem));
+  gap: var(--zen-space-3);
+  pointer-events: none;
+}
+
+zen-notifications > [data-zen-notification-stack] > article {
+  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);
+  pointer-events: auto;
+  animation: zen-notification-enter 160ms ease-out;
+}
+
+.zen-notification-tone {
+  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;
+}
+
+[data-tone="success"] > .zen-notification-tone {
+  background: var(--zen-color-success);
+  color: var(--zen-color-on-success);
+}
+
+[data-tone="warning"] > .zen-notification-tone {
+  background: var(--zen-color-warning);
+  color: var(--zen-color-on-warning);
+}
+
+[data-tone="error"] > .zen-notification-tone {
+  background: var(--zen-color-danger);
+  color: var(--zen-color-on-danger);
+}
+
+.zen-notification-content {
+  display: grid;
+  gap: var(--zen-space-1);
+}
+
+.zen-notification-message,
+.zen-notification-description {
+  margin: 0;
+}
+
+.zen-notification-message {
+  font-weight: 800;
+}
+
+.zen-notification-description {
+  color: var(--zen-color-text-muted);
+  font-size: var(--zen-font-size-sm);
+}
+
+.zen-notification-action,
+.zen-notification-dismiss {
+  border: 0;
+  background: transparent;
+  color: inherit;
+  cursor: pointer;
+}
+
+.zen-notification-action {
+  justify-self: start;
+  margin-top: var(--zen-space-2);
+  padding: 0;
+  color: var(--zen-color-info);
+  font-weight: 800;
+  text-decoration: underline;
+  text-underline-offset: 0.2em;
+}
+
+.zen-notification-dismiss {
+  width: 2rem;
+  height: 2rem;
+  padding: 0;
+  border-radius: 50%;
+  font-size: 1.25rem;
+}
+
+.zen-notification-action:focus-visible,
+.zen-notification-dismiss:focus-visible {
+  outline: 3px solid var(--zen-color-focus);
+  outline-offset: 2px;
+}
+
+.zen-visually-hidden {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  overflow: hidden;
+  clip: rect(0 0 0 0);
+  clip-path: inset(50%);
+  white-space: nowrap;
+}
+
+@keyframes zen-notification-enter {
+  from {
+    opacity: 0;
+    transform: translateY(0.75rem);
+  }
+}
+
+@media (forced-colors: active) {
+  zen-notifications > [data-zen-notification-stack] > article,
+  .zen-notification-tone {
+    border: 1px solid CanvasText;
+  }
+}
+
 @media (prefers-reduced-motion: reduce) {
   zen-button > :where(button, a),
   zen-card > :where(article, section) {
@@ -207,4 +344,8 @@
   zen-button[loading] > :where(button, a)::before {
     animation-duration: 1400ms;
   }
+
+  zen-notifications > [data-zen-notification-stack] > article {
+    animation: none;
+  }
 }