From d5bb8d93e267e2b611b83089192d75f38e8dd2a9 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Fri, 10 Oct 2025 15:54:27 -0400 Subject: [PATCH] Add popover and menu primitives --- css/tailwind.source.css | 58 ++++++++++++++++++++++++++++++++++++++++- docs/design-system.md | 37 ++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/css/tailwind.source.css b/css/tailwind.source.css index 8270f50c..ec894bfb 100644 --- a/css/tailwind.source.css +++ b/css/tailwind.source.css @@ -9,6 +9,18 @@ @apply outline-none focus-visible:ring-2 focus-visible:ring-info focus-visible:ring-opacity-70 focus-visible:ring-offset-2 focus-visible:ring-offset-surface focus-visible:transition-shadow focus-visible:duration-fast focus-visible:ease-out; } + .popover { + @apply relative inline-flex; + } + + .popover__panel { + @apply focus-ring absolute left-0 top-full z-[70] mt-2 min-w-[12rem] origin-top rounded-lg border border-border border-opacity-40 bg-panel px-4 py-3 text-sm text-text shadow-lg transition-transform transition-opacity duration-[var(--motion-duration-base)] ease-[var(--motion-ease-out)] opacity-0 scale-95 pointer-events-none; + } + + .popover__panel[data-state="open"] { + @apply pointer-events-auto opacity-100 scale-100; + } + .btn { @apply focus-ring inline-flex items-center justify-center gap-2 rounded-lg border border-transparent bg-primary px-4 py-2 text-sm font-semibold uppercase tracking-wide text-white shadow-md transition duration-base ease-in-out; } @@ -57,6 +69,48 @@ @apply bg-border bg-opacity-40 text-muted-strong; } + .menu { + @apply flex flex-col gap-1 py-2; + } + + .menu__heading { + @apply px-4 pb-2 text-xs font-semibold uppercase tracking-wide text-muted; + } + + .menu__separator { + @apply my-2 h-px bg-border bg-opacity-20; + } + + .menu__item { + @apply focus-ring relative flex w-full items-center gap-3 rounded-md px-4 py-2 text-sm font-medium text-text transition-colors duration-[var(--motion-duration-fast)] ease-[var(--motion-ease-in-out)]; + } + + .menu__item:hover { + @apply bg-panel-hover text-text; + } + + .menu__item[data-state="active"] { + @apply bg-info bg-opacity-20 text-info-strong; + } + + .menu__item[data-variant="critical"] { + @apply text-critical-strong; + } + + .menu__item[data-variant="critical"]:hover { + @apply bg-critical bg-opacity-20; + } + + .menu__item:disabled, + .menu__item[aria-disabled="true"] { + @apply cursor-not-allowed bg-transparent text-muted opacity-60; + } + + .menu__item:disabled:hover, + .menu__item[aria-disabled="true"]:hover { + @apply bg-transparent text-muted; + } + .card { @apply focus-ring relative rounded-lg border border-transparent bg-card text-text shadow-md transition duration-base ease-in-out; } @@ -193,7 +247,9 @@ .checkbox, .checkbox::after, .switch, - .switch::before { + .switch::before, + .popover__panel, + .menu__item { transition: none !important; } } diff --git a/docs/design-system.md b/docs/design-system.md index 3d01dce8..85e9bd5a 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -75,6 +75,43 @@ For toggles: Compose them together when mounting new modal portals. If you need a drawer or sheet, start from these primitives and adjust spacing/positioning via utilities. +### Popovers + +Use `.popover` as the relative anchor for floating content. Nest `.popover__panel` for the floating surface—it’s positioned absolutely below the trigger, inherits the shared focus ring, and animates with opacity/scale tokens. Toggle `[data-state="open"]` on the panel to switch between the collapsed (`opacity-0 scale-95`) and expanded (`opacity-100 scale-100`) states. + +```html +
+ + +
+``` + +Mix utilities such as `right-0` or `translate-x-1/2` on `.popover__panel` when you need alternative alignments. The motion helpers defer to the global motion tokens so Task 5 can adjust timing globally without revisiting individual components. + +### Menus + +`.menu` normalises list-based command menus with consistent padding. Pair it with `.menu__heading` for optional section labels, `.menu__separator` for dividers, and `.menu__item` for actionable rows. Each item supports hover, active (`data-state="active"`), and critical (`data-variant="critical"`) palettes plus disabled fallbacks through either `disabled` or `aria-disabled="true"`. + +```html +
+ + +
+``` + +The helpers rely on layout utilities (`flex`, `gap-*`) so you can safely inject icons, shortcuts, or nested badges without restyling the base tokens. + ### Grid Stack `.grid-stack` offers gap tokens for dense layouts. Add `data-variant="cards"` for responsive card grids (`grid-cols-1` → `sm:grid-cols-2` → `xl:grid-cols-3`). Switch to `data-orientation="vertical"` for stacked flex columns that reuse the same spacing tokens.