From 3638a4950305c43fea5ea3bf320322ad534a755d Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Thu, 16 Oct 2025 15:03:03 -0400 Subject: [PATCH] Add overlay menu documentation and QA demo --- docs/design-system.md | 2 +- docs/menus.md | 157 ++++++++++++++++++++ docs/qa.md | 2 +- views/dev/popover-demo.html | 285 ++++++++++++++++++++++++++++++++++++ 4 files changed, 444 insertions(+), 2 deletions(-) create mode 100644 docs/menus.md create mode 100644 views/dev/popover-demo.html diff --git a/docs/design-system.md b/docs/design-system.md index 36cde6b2..156fa42f 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -329,7 +329,7 @@ Use `.popover` as the relative anchor for floating content. Nest `.popover__pane ``` -Use the shared popover engine (`js/ui/overlay/popoverEngine.js`) instead of manual offsets. The helper attaches panels to the overlay root, flips placement when they collide with the viewport, and toggles `aria` + `data-popover-state` attributes as visibility changes. Width and height clamps can be sourced from design tokens (`maxWidthToken`, `maxHeightToken`) so surfaces inherit theme-specific limits without inline constants. 【F:js/ui/overlay/popoverEngine.js†L252-L266】【F:js/ui/overlay/popoverEngine.js†L633-L706】【F:css/tailwind.source.css†L358-L399】 +Use the shared popover engine (`js/ui/overlay/popoverEngine.js`) instead of manual offsets. The helper attaches panels to the overlay root, flips placement when they collide with the viewport, and toggles `aria` + `data-popover-state` attributes as visibility changes. Width and height clamps can be sourced from design tokens (`maxWidthToken`, `maxHeightToken`) so surfaces inherit theme-specific limits without inline constants. Consult [docs/menus.md](./menus.md) for overlay-root wiring, Tailwind overlay helpers, and the manual QA surface that exercises card and modal menus. 【F:js/ui/overlay/popoverEngine.js†L252-L266】【F:js/ui/overlay/popoverEngine.js†L633-L706】【F:css/tailwind.source.css†L358-L399】 ```js import createPopover from "../overlay/popoverEngine.js"; diff --git a/docs/menus.md b/docs/menus.md new file mode 100644 index 00000000..d9db32f8 --- /dev/null +++ b/docs/menus.md @@ -0,0 +1,157 @@ +# Menu & Popover Integration + +Menus, quick actions, and contextual overlays all flow through the shared popover +engine. This guide documents how to request the overlay root, create and manage +popovers, and apply the Tailwind overlay helpers that keep menus consistent with +the design system. + +## Request the overlay root + +Use `ensureOverlayRoot` before mounting any floating UI so the engine has a +stable portal target. The helper resolves the right `document` instance and +appends the root container when it is missing. + +```js +import ensureOverlayRoot from "../js/ui/overlay/overlayRoot.js"; + +const doc = trigger?.ownerDocument || document; +const overlayRoot = ensureOverlayRoot(doc); + +// overlayRoot is a `
` tagged with `data-overlay-root`. +// Popovers, sheets, and modals render inside this element. +``` + +`ensureOverlayRoot` is idempotent—call it during setup for each controller or +dev surface without worrying about duplicates. + +## Create a popover + +`createPopover(trigger, render, options)` wires focus management, aria +attributes, collision detection, and auto updates. The render callback receives +a portal `container` inside the overlay root. Return the panel element so the +engine can toggle `data-popover-state` values while it opens and closes. + +```js +import createPopover from "../js/ui/overlay/popoverEngine.js"; + +function attachMenu(trigger) { + const popover = createPopover( + trigger, + ({ container, close }) => { + const panel = container.ownerDocument.createElement("div"); + panel.className = "popover__panel menu space-y-1"; + + [ + { label: "Play", action: "play" }, + { label: "Save to playlist", action: "save" }, + { label: "Share", action: "share" }, + ].forEach(({ label, action }) => { + const item = container.ownerDocument.createElement("button"); + item.type = "button"; + item.className = "menu__item justify-between"; + item.dataset.action = action; + item.textContent = label; + item.addEventListener("click", () => close({ restoreFocus: true })); + panel.appendChild(item); + }); + + container.append(panel); + return panel; + }, + { + document: trigger?.ownerDocument, + placement: "bottom-end", + restoreFocusOnClose: true, + }, + ); + + trigger.addEventListener("click", async (event) => { + event.preventDefault(); + await popover.open(); + }); +} +``` + +Pass `document`, `placement`, and other floating-ui options through the third +argument. The helper exposes `open()`, `close()`, `toggle()`, `isOpen()`, and +`destroy()` for lifecycle control. + +## Tailwind overlay classes + +Tailwind ships token-backed overlay helpers that keep surfaces aligned with the +palette: + +- `popover__panel` / `popover-panel` — rounded overlay panel with opacity and + scale transitions. Add `menu` and `menu__item` classes for stacked button + menus. +- `overlay-scrim` and `overlay-panel` — lightweight surfaces for inline overlays + and frosted tooltips. Use the `data-strength` / `data-variant` modifiers to + switch tones. +- `ds-overlay-backdrop` — full-screen backdrop helper for async states. +- `bv-modal`, `bv-modal-backdrop`, and `bv-modal__panel` — modal scaffolding + used by upload/edit flows. Mobile breakpoints flatten the panel automatically. + +Avoid ad-hoc opacity or blur utilities—let the semantic classes map to overlay +color tokens so theme swaps stay centralized in +`css/tailwind.source.css`. + +## Video card actions + +Video cards lean on `MoreMenuController` to attach a popover to the card's +"More" button. The controller registers the overlay root, builds the menu panel, +and opens the popover on demand. + +```js +// js/ui/moreMenuController.js +const render = this.createPopoverRender(entry); +const popover = createPopover(trigger, render, { + document: documentRef, + placement: "bottom-end", +}); + +// Inside the render callback (createVideoMoreMenuPanel): +panel.className = "popover__panel menu space-y-1"; +button.className = "menu__item"; +``` + +Each menu button dispatches `video:context-action` events so cards, lists, and +player overlays can respond without duplicating menu markup. + +## Modal actions + +`VideoModal` follows the same pattern for its in-modal menu and zap actions. It +requests the overlay root from the modal's `ownerDocument`, builds a +`popover__panel`, and delegates action handling back to the controller. + +```js +// js/ui/components/VideoModal.js +const render = ({ document: doc, close }) => { + const panel = this.buildModalMoreMenuPanel({ document: doc, close }); + return panel; +}; + +const popover = createPopover(this.modalMoreBtn, render, { + document: documentRef, + placement: "bottom-end", + restoreFocusOnClose: true, +}); +``` + +Modal action buttons reuse the same `menu__item` styling and call `close()` to +collapse the overlay before dispatching the selected action. + +## Manual QA surface + +A lightweight QA harness lives at `views/dev/popover-demo.html`. Launch the +static dev server from the repository root: + +```bash +python -m http.server 8000 +# or +npx serve +``` + +Then open `http://localhost:8000/views/dev/popover-demo.html` to interact with a +sample video card and modal wired to the popover engine. The page ensures the +overlay root exists, demonstrates Tailwind overlay helpers, and exercises both +card and modal action menus without relying on production data. diff --git a/docs/qa.md b/docs/qa.md index ef6f1bbc..f57861cf 100644 --- a/docs/qa.md +++ b/docs/qa.md @@ -4,7 +4,7 @@ Run this checklist before publishing releases or merging changes that touch uplo > **Note:** Align every UI styling review with the token-first rules in [`AGENTS.md`](../AGENTS.md) and the [README CSS build pipeline](../README.md#css-build-pipeline). When QA covers styling changes, run `npm run lint:css` (and any related build/test scripts) to catch regressions early. -Before starting, open `docs/kitchen-sink.html` in a browser to review the modal surface reference. With that page as your visual baseline, perform this quick accessibility spot check in the running app: +Before starting, open `docs/kitchen-sink.html` in a browser to review the modal surface reference. Spin up the static dev server (`python -m http.server 8000` or `npx serve`) and load `views/dev/popover-demo.html` to sanity-check overlay menus following [docs/menus.md](./menus.md). With those pages as your visual baseline, perform this quick accessibility spot check in the running app: Automated regression coverage now exercises these flows on the unified design-system variant: diff --git a/views/dev/popover-demo.html b/views/dev/popover-demo.html new file mode 100644 index 00000000..8564aca0 --- /dev/null +++ b/views/dev/popover-demo.html @@ -0,0 +1,285 @@ + + + + + + + Popover & Menu Demo • bitvid + + + +
+
+

Popover & Menu Demo

+

+ This manual QA page mounts a sample video card and modal so you can verify + overlay menus with the shared popover engine. Review the implementation notes in + docs/menus.md + for architectural guidance. +

+
+ +
+

Video card

+
+
+
+
+ + Preview thumbnail + +
+
+ +
+
+

Understanding overlay primitives

+

+ Tap the more button to open an actions menu rendered through + createPopover. +

+
+
+
+ +
+
+
+

Modal actions

+

+ Launch the demo modal to test menus layered above a backdrop. +

+
+ +
+
+
+ + + + + +