Skip to content

motion: follow-ups from the hub rollout - #1804

Merged
nadaverell merged 3 commits into
mainfrom
fix/motion-followups
Sep 17, 2026
Merged

nadaverell merged 3 commits into
mainfrom
fix/motion-followups

Conversation

@nadaverell

@nadaverell nadaverell commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Small follow-ups from the motion rollout (#1791) and the hub bump.

Overlays kept mounted through their exit stop acting like they are open. ShortcutHelpOverlay, CommandPalette and DiagnosticsOverlay are held by useAnimatedUnmount for their 160ms exit, but their Escape capture listener and backdrop click stayed live and their content stayed focusable during that window. Escape and the backdrop click now key on isOpen, the root is inert while fading, and the palette focuses its input once open (a mount-time focus would land inside the inert root during the two pre-open frames).

One straggler timing. The transcript preview clamp in diagnose/ActivityTurn.tsx (landed with the findings-story merge) animated height on its own duration-200 ease-out; it now runs the disclosure clock. parts.test.tsx pins the new classes via the token.

Disclosure moves into k8s-ui. Radar Cloud carried a copy because radar-app's wasn't exported. The k8s-ui version is the superset (controlled open / onOpenChange, inheritColor defaulting to the header's color, select-none header) with a renderToString test; radar-app imports it and deletes its copy. radar-app now imports a new k8s-ui export, so its peer floor rises to >=1.14.8. Publish k8s-ui 1.14.8 before radar-app, per the release script's order.

The omnibar's spotlight animates. Focusing the omnibar (⌘K or click) dimmed the page and dropped the results panel in on one frame, and both vanished the same way — the last hard cut on a primary path. Scrim and panel are menu-kind overlays now: presence on the menu clock, the scrim fades with the panel, the panel grows from the top of the field (140ms) and shrinks back (100ms) while inert, the anchor is kept through the exit, and what the panel showed at logical close is what fades out. The field's focus ring eases in on the same 140ms instead of popping. Verified headless in launcher and hero modes: scrim 0→1 over 140ms, panel gone ~100ms after Escape with the scrim.

Dead CSS. .issue-details-motion had no consumers left after the sweep; the class, its @starting-style block and the parity test are removed.

Testing

  • k8s-ui: tsc at baseline; vitest 191 files / 3773 tests (new Disclosure.test.tsx).
  • radar-app: tsc 0 errors; vitest 1338 tests pass (the four Playwright specs vitest picks up fail to load on main too); eslint warning count unchanged on every touched file.

Hub follow-up after publish: swap src/components/Disclosure.tsx for the k8s-ui export.


Note

Medium Risk
Touches primary search and overlay interaction paths plus a k8s-ui export bump; behavior fixes reduce exit-animation bugs but omnibar and palette timing need regression on keyboard/focus.

Overview
Follow-ups from the motion rollout: shared Disclosure in k8s-ui, overlays that stay mounted through exit no longer behave as if open, omnibar spotlight animates, one timing alignment, and dead CSS removed.

Disclosure moves into @skyhook-io/k8s-ui and is exported from the UI barrel. The library version adds controlled open / onOpenChange, inheritColor (chevron follows header color by default), and select-none on the header, with new Disclosure.test.tsx. Radar-app drops its duplicate and imports the package instead; web/package.json peer floor rises to >=1.14.8 (publish k8s-ui before the app).

Command palette, diagnostics, and shortcut-help overlays still unmount via useAnimatedUnmount, but Escape capture, backdrop click, and focus were active while fading. Roots are inert when not logically open, handlers and backdrop clicks key on isOpen, and the command palette focuses its input when isOpen turns true (not on mount inside a pre-open inert root).

Omnibar scrim and results panel use menu-kind overlay transitions (useAnimatedUnmount, fade/grow on open, shrink on close), keep the anchor through exit, freeze last panel content for the fade, and ease the field focus ring on the same 140ms clock.

Diagnose transcript preview clamp in ActivityTurn switches from local duration-200 ease-out to the shared disclosure duration + TW_EASE_UI; parts.test.tsx pins the new classes.

Removes unused .issue-details-motion from components.css and the CSS/token parity test from Collapse.test.tsx.

Reviewed by Cursor Bugbot for commit 659a74c. Bugbot is set up for automated code reviews on this repo. Configure here.

- ShortcutHelpOverlay, CommandPalette, DiagnosticsOverlay: Escape and the
  backdrop click only while logically open, and the root is inert while the
  overlay fades — a dismissed overlay no longer swallows Escape or blocks
  clicks for its exit window. The palette focuses its input once open
  rather than on mount.
- The transcript preview clamp in ActivityTurn runs the disclosure clock
  (300ms, the one curve) instead of its own 200ms ease-out.
- Disclosure moves into k8s-ui (with controlled `open`/`onOpenChange` and
  `inheritColor`) so Radar Cloud can drop its copy; radar-app's peer floor
  on k8s-ui rises to >=1.14.8, the release that ships it.
- `.issue-details-motion` had no consumers left; the class and its parity
  test are gone.
@nadaverell
nadaverell requested a review from hisco as a code owner September 17, 2026 13:47
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix overlay exits and centralize Disclosure in k8s-ui

🐞 Bug fix âœĻ Enhancement 🕐 20-40 Minutes

Grey Divider

AI Description

â€Ē Prevent exiting overlays from capturing focus, Escape, or backdrop clicks.
â€Ē Centralize Disclosure in k8s-ui and migrate Radar consumers to the shared implementation.
â€Ē Align transcript animation timing and remove unused legacy disclosure CSS.
Diagram

graph TD
  State["Logical Open State"] --> Overlays["Animated Overlays"] --> Guards["Exit Interaction Guards"]
  Tokens["Motion Tokens"] --> Overlays
  Tokens --> Preview["Transcript Preview"]
  Tokens --> Disclosure["Shared Disclosure"] --> Export["k8s-ui Export"] --> Consumers["Radar Consumers"]
Loading
High-Level Assessment

The chosen approach is appropriate: interaction is tied to logical open state while animated presence remains responsible only for visual exit, and Disclosure is centralized in the package that already owns Collapse and its motion tokens. Retaining Radar's local copy would preserve duplication and behavioral drift; immediate unmounting would avoid stale interaction but remove the intended exit animation. Publish k8s-ui 1.14.8 before radar-app because the migrated imports require the new export.

Files changed (17) +137 / -64

Enhancement (2) +70 / -0
Disclosure.tsxAdd shared controlled and uncontrolled Disclosure +69/-0

Add shared controlled and uncontrolled Disclosure

â€Ē Introduces a k8s-ui Disclosure composed from Collapse, CollapseChevron, and useDisclosure. It supports controlled or uncontrolled state, keeps closed content mounted, inherits header color by default, and exposes styling hooks.

packages/k8s-ui/src/components/ui/Disclosure.tsx

index.tsExport Disclosure from the UI barrel +1/-0

Export Disclosure from the UI barrel

â€Ē Adds Disclosure to the public k8s-ui component exports.

packages/k8s-ui/src/components/ui/index.ts

Bug fix (4) +31 / -21
ActivityTurn.tsxAlign transcript preview with disclosure motion +4/-3

Align transcript preview with disclosure motion

â€Ē Changes the reasoning preview height transition from a local 200ms ease-out to the shared 300ms UI easing curve used by Collapse.

web/src/components/diagnose/ActivityTurn.tsx

CommandPalette.tsxDisable command palette interaction during exit +12/-8

Disable command palette interaction during exit

â€Ē Registers Escape handling and backdrop closure only while logically open, marks the fading root inert, and disables pointer events after close. Input focus now occurs when the palette becomes open rather than during its pre-open mount frames.

web/src/components/ui/CommandPalette.tsx

DiagnosticsOverlay.tsxDisable diagnostics interaction during exit +7/-5

Disable diagnostics interaction during exit

â€Ē Stops the retained overlay from capturing Escape, focus, or backdrop clicks while its close animation completes.

web/src/components/ui/DiagnosticsOverlay.tsx

ShortcutHelpOverlay.tsxDisable shortcut-help interaction during exit +8/-5

Disable shortcut-help interaction during exit

â€Ē Limits Escape and backdrop handling to the logical open state and makes the fading overlay inert and pointer-transparent after dismissal.

web/src/components/ui/ShortcutHelpOverlay.tsx

Refactor (7) +6 / -28
components.cssRemove unused issue details motion CSS +0/-22

Remove unused issue details motion CSS

â€Ē Deletes the unreferenced '.issue-details-motion' rules and associated starting-style transition block now that Collapse owns disclosure animation.

packages/k8s-ui/src/theme/components.css

HelmReleaseDrawer.tsxUse the shared Disclosure in Helm releases +1/-1

Use the shared Disclosure in Helm releases

â€Ē Replaces the radar-app-local Disclosure import with the k8s-ui implementation.

web/src/components/helm/HelmReleaseDrawer.tsx

MCPSetupDialog.tsxUse the shared Disclosure in MCP setup +1/-1

Use the shared Disclosure in MCP setup

â€Ē Migrates MCP setup sections to the exported k8s-ui Disclosure component.

web/src/components/home/MCPSetupDialog.tsx

PrometheusChartsGrid.tsxUse the shared Disclosure for Prometheus charts +1/-1

Use the shared Disclosure for Prometheus charts

â€Ē Switches Prometheus chart disclosures from the local component to the k8s-ui export.

web/src/components/resource/PrometheusChartsGrid.tsx

WorkloadMetricsHelpDialog.tsxUse the shared Disclosure in metrics help +1/-1

Use the shared Disclosure in metrics help

â€Ē Imports Disclosure from k8s-ui instead of the removed radar-app copy.

web/src/components/resource/WorkloadMetricsHelpDialog.tsx

WorkloadMetricsSection.tsxUse the shared Disclosure for workload metrics +1/-1

Use the shared Disclosure for workload metrics

â€Ē Migrates workload metrics sections to the centralized k8s-ui Disclosure.

web/src/components/resource/WorkloadMetricsSection.tsx

PodFilePreview.tsxUse the shared Disclosure in pod previews +1/-1

Use the shared Disclosure in pod previews

â€Ē Replaces the local Disclosure dependency with the public k8s-ui component.

web/src/components/resources/PodFilePreview.tsx

Tests (3) +29 / -14
Collapse.test.tsxRemove obsolete legacy CSS parity coverage +0/-13

Remove obsolete legacy CSS parity coverage

â€Ē Removes filesystem-based assertions for the deleted '.issue-details-motion' stylesheet rule and its unused easing import. Existing Collapse and chevron motion tests remain intact.

packages/k8s-ui/src/components/ui/Collapse.test.tsx

Disclosure.test.tsxCover shared Disclosure rendering and state modes +26/-0

Cover shared Disclosure rendering and state modes

â€Ē Adds server-rendering tests for header-panel ARIA wiring, mounted inert content, default and controlled open states, and chevron color inheritance.

packages/k8s-ui/src/components/ui/Disclosure.test.tsx

parts.test.tsxPin transcript preview to shared motion tokens +3/-1

Pin transcript preview to shared motion tokens

â€Ē Updates the rendered-class assertion to require the disclosure duration and shared UI easing token.

web/src/components/diagnose/parts.test.tsx

Other (1) +1 / -1
package.jsonRaise the k8s-ui peer dependency floor +1/-1

Raise the k8s-ui peer dependency floor

â€Ē Requires k8s-ui 1.14.8 or newer so radar-app can import the newly published Disclosure component.

web/package.json

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Two prop comments recount old markup 📘 Rule violation ⚙ Maintainability
Description
className and summaryClassName are documented by describing what the old <details> and
<summary> elements carried. Because these comments moved into the new shared component, later
readers receive migration history rather than a description of the current wrapper and header
contract.
Code

packages/k8s-ui/src/components/ui/Disclosure.tsx[R34-36]

+  /** Wrapper (what the old <details> carried). */
+  className?: string
+  /** Header button (what the old <summary> carried). */
Evidence
PR Compliance ID 3036538 prohibits code comments containing explicit change history. The newly added
shared component describes both props in terms of what the old native elements carried.

Rule 3036538: Disallow references to tickets or PR history in code comments
packages/k8s-ui/src/components/ui/Disclosure.tsx[34-36]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The comments for `className` and `summaryClassName` refer to what old HTML elements carried, embedding implementation history in the shared component.

## Fix Focus Areas
- packages/k8s-ui/src/components/ui/Disclosure.tsx[34-36]

## Recommended Fix
Rewrite the comments to describe only the current targets, such as `Wrapper class name` and `Header button class name`, without referring to the old `<details>` or `<summary>` implementation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 44 rules
✅ Cross-repo context — repo relationships
  Explored: repo: skyhook-dev/radar-hub-web (sha: 39186c2a)
Review mode: 🧠 Deep: This spans a shared UI package/API release boundary, multiple overlay interaction paths, disclosure behavior, animation timing, and many consumers, creating several independent, easy-to-miss regression risks.

Grey Divider

Tip of the day
ðŸ’Ą Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread packages/k8s-ui/src/components/ui/Disclosure.tsx Outdated
Focusing the omnibar (⌘K or click) dimmed the page and dropped the results
panel in on one frame, and both vanished the same way. They are menu-kind
overlays: presence via useAnimatedUnmount on the menu clock, the scrim
fades on the panel's clock, the panel grows from the top of the field and
goes inert with no pointer events while it fades, the anchor is kept through
the exit, and what the panel showed at logical close is what fades out. The
field's focus ring eases in on the same 140ms instead of popping.
@nadaverell
nadaverell merged commit 8fba01a into main Sep 17, 2026
8 checks passed

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 659a74c. Configure here.

const portal = useAnimatedUnmount(portalShown, overlayExitMs('menu'))
const panelWasOpen = useRef(dropdownOpen)
if (open) panelWasOpen.current = dropdownOpen
const showPanel = open ? dropdownOpen : panelWasOpen.current

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exit animation swaps panel contents

Medium Severity

The exit freeze only keeps the panel mounted. execute still clears text and pills on the same render as close, and recentRows still keys off open, so the fading overlay snaps to the empty launcher (or drops recents) instead of the results that were on screen.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 659a74c. Configure here.

<SearchPillInput
className={hero
? 'min-h-14 px-5 rounded-2xl bg-theme-surface border border-theme-border shadow-theme-sm transition-colors focus-within:border-[var(--color-brand-500)] focus-within:shadow-[0_0_0_4px_color-mix(in_srgb,var(--color-brand-500)_15%,transparent)]'
: 'min-h-8 px-2.5 rounded-md bg-theme-elevated border border-transparent focus-within:border-theme-border focus-within:bg-theme-surface transition-colors'}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hero field dims during overlay exit

Medium Severity

The hero field's z-[16] lift is still tied to open, but the full-viewport scrim now stays mounted through the 100ms exit. As soon as the launcher closes, the field drops behind the still-opaque z-[15] dim and blur, then sits under the fade.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 659a74c. Configure here.

nadaverell added a commit that referenced this pull request Sep 17, 2026
â€Ķabove the fading scrim (#1806)

Two findings Bugbot raised on #1804's omnibar commit after it merged,
both real:

- **Panel content swapped mid-exit.** Picking a result clears the query
on the same render as the close, and the recent rows re-key off `open`,
so for the 100ms exit the fading panel showed a different list. The
panel's last open-frame node is now retained through the exit (the same
freeze the disclosure primitives use). Verified headless: after Enter on
a result the panel shows one and the same content until removal while
the app navigates behind it.
- **Hero field dropped behind the scrim.** The field's `z-[16]` lift was
keyed on `open`, but the scrim now stays mounted through its exit, so
the field dimmed for 100ms. The lift follows presence.

## Testing

radar-app `npm run tsc` 0 errors; vitest 1368 tests; eslint warning
count unchanged on the file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant