Skip to content

feat(ui): add Modal component#128

Merged
kylengn merged 10 commits intomainfrom
feat/add-modal-refactor
Nov 15, 2025
Merged

feat(ui): add Modal component#128
kylengn merged 10 commits intomainfrom
feat/add-modal-refactor

Conversation

@kylengn
Copy link
Contributor

@kylengn kylengn commented Nov 13, 2025

Summary by CodeRabbit

  • New Features

    • Added responsive modal component with desktop and mobile variants.
    • Updated code block footer with refined styling and layout improvements.
  • Style

    • Changed backdrop overlay appearance in light mode to enhance visual hierarchy.
  • Chores

    • Added new dependency to support enhanced UI components.

@kylengn kylengn self-assigned this Nov 13, 2025
Copilot AI review requested due to automatic review settings November 13, 2025 12:00
@vercel
Copy link

vercel bot commented Nov 13, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
react-ui Ready Ready Preview Comment Nov 14, 2025 11:17am

@coderabbitai
Copy link

coderabbitai bot commented Nov 13, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR introduces a responsive modal component with breakpoint-aware desktop/mobile rendering using Radix Dialog and Vaul Drawer, implements a useMediaQuery hook for breakpoint detection, updates code-block footer components with typography-based properties, adds the vaul dependency, and exposes new components through barrel exports.

Changes

Cohort / File(s) Summary
Modal Implementation
libs/react/ui/src/components/modal/modal.tsx, libs/react/ui/src/components/modal/index.ts, libs/react/ui/src/components/modal/modal.stories.tsx
Introduces comprehensive modal component with breakpoint-aware rendering (Radix Dialog on desktop, Vaul Drawer on mobile). Includes ModalTrigger, ModalPortal, ModalClose, ModalContent, ModalHeader, ModalBody, ModalFooter, ModalTitle, ModalDescription, and styled variants (modalContentVariants, modalOverlayVariants, modalDefaultTransition). Exports all public types and components. Provides three story variants: Default, ImportForm, and GithubActions demonstrating modal configurations.
Responsive Utilities
libs/react/ui/src/hooks/useMediaQuery.ts, libs/react/ui/src/hooks/index.ts
Introduces useMediaQuery hook with MediaQueryManager for SSR-safe media query subscriptions and cached listeners. Exports hook from hooks barrel.
Code-Block Footer Refactoring
libs/react/ui/src/components/code-block/code-block-footer.tsx
Updates CodeBlockFooterMessage and CodeBlockFooterDescription from div-based props to Text-based props (ComponentProps). Introduces CodeBlockFooterIcon and CodeBlockFooterContent wrappers. Adds Slot rendering support with data-slot attributes.
Dependencies & Exports
libs/react/ui/package.json, libs/react/ui/index.css, libs/react/ui/src/components/index.ts, libs/react/ui/src/components/moving-border/index.ts, libs/react/ui/src/components/dynamic-item/dynamic-item.stories.tsx
Adds vaul (^1.1.1) runtime dependency. Changes backdrop CSS variable from opaque white to semi-transparent black in light mode. Exports modal component from main index. Adds moving-border barrel export. Replaces relative import with absolute import path in dynamic-item stories.

Sequence Diagram

sequenceDiagram
    participant User
    participant Modal as Modal Component
    participant Breakpoint as useMediaQuery
    participant Desktop as Radix Dialog
    participant Mobile as Vaul Drawer

    User->>Modal: Render modal
    Modal->>Breakpoint: Check breakpoint (< md?)
    
    alt Desktop (≥ md)
        Breakpoint-->>Modal: true
        Modal->>Desktop: Render DialogRoot + DialogOverlay
        User->>Desktop: Interact with dialog
        Desktop-->>Modal: Handle dialog events
    else Mobile (< md)
        Breakpoint-->>Modal: false
        Modal->>Mobile: Render Drawer.Root + Drawer.Overlay
        User->>Mobile: Interact with drawer
        Mobile-->>Modal: Handle drawer events
    end
    
    Modal-->>User: Close with consistent API
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Modal implementation (modal.tsx): ~30 min — Comprehensive new component with dual rendering paths, multiple sub-components, animation support, and TypeScript types requiring careful review of breakpoint logic and component composition patterns.
  • Code-block footer refactoring: ~15 min — Type signature changes from div to Text-based props; requires verifying backward compatibility and slot rendering implementation.
  • useMediaQuery hook: ~10 min — SSR handling, MediaQueryManager caching logic, and event listener lifecycle management.
  • Export/integration changes: ~5 min — Straightforward barrel exports and dependency additions.

Areas requiring extra attention:

  • Modal's breakpoint-aware rendering switching logic and component prop propagation between Radix and Vaul APIs
  • TypeScript prop types consistency across ModalContent, ModalHeader, ModalOverlay, etc.
  • Code-block footer's Text-based prop types and Slot rendering backward compatibility
  • useMediaQuery hook's SSR safety and memory management in the MediaQueryManager

Possibly related PRs

Suggested reviewers

  • EnzalRad
  • dvxam
  • noe-charmet

Poem

🐰 A drawer and dialog dance in harmony,
Breakpoints whisper which form they'll be,
useMediaQuery hops through the view,
Footer messages wrapped in Text anew—
Responsive modals, responsive cheer,
The UI spring is finally here!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(ui): add Modal component' directly and accurately describes the main change—introduction of a new Modal component to the UI library.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@argos-ci
Copy link

argos-ci bot commented Nov 13, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) 👍 Changes approved 2 changed, 8 added Nov 14, 2025, 11:20 AM

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces a new Modal component that provides adaptive behavior between desktop dialog and mobile drawer implementations based on screen size. The component leverages Radix UI's Dialog primitive for desktop and the Vaul library for mobile drawer functionality.

Key Changes:

  • Implemented a responsive Modal component with automatic desktop/mobile adaptation
  • Added useMediaQuery hook with singleton manager pattern for efficient media query handling
  • Integrated Vaul library for drawer functionality on mobile devices

Reviewed Changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
pnpm-lock.yaml Added vaul@1.1.2 dependency and updated biomejs package CPU constraints
libs/react/ui/package.json Added vaul ^1.1.1 to dependencies
libs/react/ui/src/hooks/useMediaQuery.ts New hook implementing MediaQueryManager class for shared media query handling
libs/react/ui/src/hooks/index.ts Exported new useMediaQuery hook
libs/react/ui/src/components/modal/modal.tsx Main Modal component with subcomponents for header, body, footer, and responsive breakpoint handling
libs/react/ui/src/components/modal/modal.stories.tsx Storybook examples demonstrating Modal usage patterns
libs/react/ui/src/components/modal/index.ts Barrel export file for Modal component and types
libs/react/ui/src/components/index.ts Added Modal to main components export
libs/react/ui/src/components/moving-border/index.ts Added barrel export for moving-border component
libs/react/ui/src/components/dynamic-item/dynamic-item.stories.tsx Updated import path for MovingBorder to use new barrel export
libs/react/ui/src/components/code-block/code-block-footer.tsx Refactored to use Text component and improved component composition with better prop types
libs/react/ui/index.css Updated backdrop color from neutral-0 to alpha-black-64 for better visual consistency
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f07910b and 5d60705.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • libs/react/ui/index.css (1 hunks)
  • libs/react/ui/package.json (1 hunks)
  • libs/react/ui/src/components/code-block/code-block-footer.tsx (5 hunks)
  • libs/react/ui/src/components/dynamic-item/dynamic-item.stories.tsx (1 hunks)
  • libs/react/ui/src/components/index.ts (1 hunks)
  • libs/react/ui/src/components/modal/index.ts (1 hunks)
  • libs/react/ui/src/components/modal/modal.stories.tsx (1 hunks)
  • libs/react/ui/src/components/modal/modal.tsx (1 hunks)
  • libs/react/ui/src/components/moving-border/index.ts (1 hunks)
  • libs/react/ui/src/hooks/index.ts (1 hunks)
  • libs/react/ui/src/hooks/useMediaQuery.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*

⚙️ CodeRabbit configuration file

We handle errors at the edge of our applications in most cases. Do not recommend to add error handling around every single function. We prefer them to bubble up and be handled at upper layers.

Files:

  • libs/react/ui/src/hooks/index.ts
  • libs/react/ui/src/components/moving-border/index.ts
  • libs/react/ui/src/components/modal/index.ts
  • libs/react/ui/src/components/index.ts
  • libs/react/ui/package.json
  • libs/react/ui/src/hooks/useMediaQuery.ts
  • libs/react/ui/index.css
  • libs/react/ui/src/components/dynamic-item/dynamic-item.stories.tsx
  • libs/react/ui/src/components/modal/modal.stories.tsx
  • libs/react/ui/src/components/code-block/code-block-footer.tsx
  • libs/react/ui/src/components/modal/modal.tsx
🧬 Code graph analysis (2)
libs/react/ui/src/components/modal/modal.stories.tsx (10)
libs/react/ui/src/components/button/button.tsx (1)
  • Button (41-65)
libs/react/ui/src/components/label/label.tsx (1)
  • Label (13-15)
libs/react/ui/src/components/input/input.tsx (1)
  • Input (24-43)
libs/react/ui/src/components/moving-border/moving-border.tsx (1)
  • MovingBorder (18-67)
libs/react/ui/src/components/dynamic-item/dynamic-item.tsx (1)
  • DynamicItem (23-68)
libs/react/ui/src/components/icon/icon.tsx (1)
  • Icon (71-74)
libs/react/ui/src/components/item/item.tsx (1)
  • ItemTitle (156-169)
libs/react/ui/src/components/button/button-link.tsx (1)
  • ButtonLink (46-76)
libs/react/ui/src/components/code-block/code-block.tsx (8)
  • CodeBlock (38-63)
  • CodeBlockHeader (67-77)
  • CodeBlockFiles (83-93)
  • CodeBlockFilename (99-117)
  • CodeBlockCopyButton (125-148)
  • CodeBlockBody (182-192)
  • CodeBlockItem (199-236)
  • CodeBlockContent (248-283)
libs/react/ui/src/components/code-block/code-block-footer.tsx (1)
  • CodeBlockFooter (15-66)
libs/react/ui/src/components/modal/modal.tsx (3)
libs/react/ui/src/hooks/useMediaQuery.ts (1)
  • useMediaQuery (71-87)
libs/react/ui/src/components/button/button.tsx (1)
  • Button (41-65)
libs/react/ui/src/components/icon/icon.tsx (1)
  • Icon (71-74)
🔇 Additional comments (10)
libs/react/ui/src/components/code-block/code-block-footer.tsx (1)

103-107: Solid layout improvement

Adding min-w-0 flex-1 inside the content wrapper fixes the flex truncation edge case so the message copy now ellipsizes instead of overflowing. Nice catch.

libs/react/ui/src/components/moving-border/index.ts (1)

1-1: LGTM!

Standard barrel export following the established pattern in the codebase.

libs/react/ui/src/components/dynamic-item/dynamic-item.stories.tsx (1)

4-4: LGTM!

Import path updated to use the centralized barrel export, aligning with the new module resolution pattern.

libs/react/ui/index.css (1)

195-195: LGTM - Backdrop visual updated for better modal contrast.

The backdrop color in light mode changed from opaque white to semi-transparent black, providing better visual separation for modal overlays. This aligns with common modal design patterns.

libs/react/ui/src/hooks/useMediaQuery.ts (2)

3-67: LGTM - Well-structured media query manager with proper resource cleanup.

The MediaQueryManager correctly:

  • Caches MediaQueryList instances per query
  • Manages per-query listener subscriptions with proper add/remove semantics
  • Attaches DOM event listeners only when the first subscriber registers and removes them when the last unsubscribes
  • Cleans up all Maps to prevent memory leaks

71-87: LGTM - Hook properly integrates with the manager and handles SSR.

The hook correctly:

  • Uses lazy initialization to avoid unnecessary work
  • Subscribes to query changes via the manager
  • Calls updateMatches() after subscription to ensure sync
  • Cleans up on unmount or query change
  • Works safely in SSR contexts (returns false when window is undefined)
libs/react/ui/src/components/index.ts (1)

13-13: LGTM!

Modal components now exposed through the public API via the central barrel export.

libs/react/ui/src/hooks/index.ts (1)

2-2: LGTM!

useMediaQuery hook now exposed through the public API via the central barrel export.

libs/react/ui/src/components/modal/index.ts (1)

1-23: LGTM!

Comprehensive barrel export for the Modal component API, properly separating type exports from value exports and including component primitives, variants, and transitions.

libs/react/ui/package.json (1)

46-46: No action required — vaul version is secure and up-to-date.

The latest stable version of the vaul npm package is 1.1.2, and the specified version ^1.1.1 will allow updates to patch versions including 1.1.2. No security vulnerabilities were detected for the vaul package.

…' for props and enhance ModalHeader with Text component
@noe-charmet
Copy link
Contributor

Now we have screenshot matching, would be great to have a story with the modal open to detect changes in the modal

@kylengn
Copy link
Contributor Author

kylengn commented Nov 14, 2025

@noe-charmet All good, ready to approve and merge

@kylengn kylengn merged commit 3a95f26 into main Nov 15, 2025
5 checks passed
@kylengn kylengn deleted the feat/add-modal-refactor branch November 15, 2025 03:17
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.

2 participants