feat: blocks toolbar and theme#149
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (6)
💤 Files with no reviewable changes (2)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughThis PR restructures the blocks interface: theme tokens and default theme changed, a theme-toggle system with view-transition animations was added, the sidebar/topbar were reworked, a floating preview toolbar plus refactored info/preview panes were introduced, and various UI polish updates were applied. ChangesBlock UI Restructuring with Sidebar, Toolbar, and Theme Animations
Sequence Diagram(s)sequenceDiagram
participant User
participant BlockPage
participant BlockToolbar
participant BlockPreviewWithToolbar
participant ThemeToggle
participant ViewTransitions
User->>BlockPage: Open blocks page (dark mode default)
BlockPage->>BlockPage: Render BlockTopBar + BlocksSidebar + BlockPreviewWithToolbar
User->>BlockToolbar: Click expand
BlockToolbar->>BlockPreviewWithToolbar: onExpandToggle(true)
BlockPreviewWithToolbar->>BlockPreviewWithToolbar: Measure rect, animate to expanded
BlockPreviewWithToolbar->>BlockPreviewWithToolbar: Set dataset.blockPreviewExpanded
User->>BlockToolbar: Click theme toggle
BlockToolbar->>ThemeToggle: useThemeToggle()
ThemeToggle->>ViewTransitions: document.startViewTransition()
ViewTransitions->>ViewTransitions: Animate light/dark reveal
ThemeToggle->>ThemeToggle: Inject view-transition CSS
User->>BlockToolbar: Click reload
BlockToolbar->>BlockPreviewWithToolbar: onReload()
BlockPreviewWithToolbar->>BlockPreviewWithToolbar: Increment reloadKey, remount children
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
Adds a redesigned blocks browsing/preview experience with a floating toolbar, theme switching animations, updated blocks layout styling, and related tooling/dependency updates.
Changes:
- Introduces new blocks UI pieces: sidebar, top bar, preview toolbar, preview background, and info pane.
- Reworks block showcase rendering to use the toolbar-enabled preview shell.
- Updates theme tokens, lint/tooling dependencies, Node version, and several UI component styles.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
.nvmrc |
Updates the Node version. |
package.json |
Bumps root tooling dependencies. |
bun.lock |
Updates locked dependency graph. |
apps/www/eslint.config.mjs |
Updates better-tailwindcss rule name. |
apps/www/app/shadcn.css |
Adjusts theme color tokens and adds surface token. |
apps/www/app/blocks/layout.tsx |
Switches blocks sidebar import and default theme behavior. |
apps/www/app/blocks/[[...slug]]/page.tsx |
Reworks responsive blocks page layout with resizable desktop panels. |
apps/www/components/theme-toggle.tsx |
Adds animated theme toggle hook/component and transition CSS generation. |
apps/www/components/sidebar-toggle-icon.tsx |
Adds animated sidebar toggle icon. |
apps/www/components/ui/resizable.tsx |
Updates resizable handle/group styling. |
apps/www/components/blocks/block-toolbar.tsx |
Adds floating toolbar for block previews. |
apps/www/components/blocks/block-showcase.tsx |
Wraps block previews in the new toolbar preview shell. |
apps/www/components/blocks/breadcrumbs.tsx |
Adds blocks top bar and sidebar toggle breadcrumb UI. |
apps/www/components/blocks/sidebar.tsx |
Adds redesigned blocks sidebar. |
apps/www/components/blocks/preview-pane.tsx |
Adds toolbar-enabled preview container behavior. |
apps/www/components/blocks/preview-background.tsx |
Adds simplified preview background wrapper. |
apps/www/components/blocks/info-pane.tsx |
Adds simplified block documentation pane. |
apps/www/components/blocks/blocks-sidebar.tsx |
Removes previous blocks sidebar implementation. |
apps/www/components/blocks/block-preview-pane.tsx |
Removes previous preview pane implementation. |
apps/www/components/blocks/block-info-pane.tsx |
Removes previous info pane implementation. |
apps/www/registry/default/blocks/linear-player/components/media-player.tsx |
Allows children inside the media provider and adjusts fallback styling. |
apps/www/registry/default/blocks/linear-player/components/button.tsx |
Adjusts glass button foreground color. |
Comments suppressed due to low confidence (1)
apps/www/app/blocks/[[...slug]]/page.tsx:120
ResizablePanelexpects numeric percentage values, but this newminSizeis a string with a percent sign. That will fail type-checking or prevent the resize constraint from being applied correctly at runtime.
| /* eslint-disable @typescript-eslint/no-unnecessary-condition */ | ||
| "use client" | ||
|
|
||
| import { motion } from "framer-motion" |
| const switchTheme = () => { | ||
| setTheme(theme === "light" ? "dark" : "light") | ||
| } |
|
|
||
| export function BlockPreviewPane({ children }: { children: ReactNode }) { | ||
| return ( | ||
| <div className="flex size-full items-center justify-center overflow-hidden bg-muted"> |
| url={page.url} | ||
| /> | ||
| <ResizablePanelGroup orientation="horizontal"> | ||
| <ResizablePanel defaultSize={"35%"} minSize={"30%"}> |
| <div className="mx-0.5 h-5 w-px bg-border/60" /> | ||
| )} | ||
| <motion.button | ||
| animate={sharedAnimate} |
| "turbo": "^2.8.12", | ||
| "typescript": "^5.9.3", | ||
| "turbo": "^2.9.14", | ||
| "typescript": "^6.0.3", |
| return () => window.removeEventListener("resize", update) | ||
| }, []) | ||
|
|
||
| // DEV: This controls the breadcumbs to be hidden when preview is expanded |
|
|
||
| /** | ||
| * Skiper 26 Theme_buttons_002 — React + CSS + transition view api https://developer.chrome.com/docs/web-platform/view-transitions/ | ||
| * Orignal concept from rudrodip |
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (3)
apps/www/components/blocks/sidebar.tsx (1)
77-118: ⚖️ Poor tradeoffConsider performance impact of multiple backdrop filters.
The
GradualBlurcomponent applies four separatebackdrop-filter: blur()layers, each triggering GPU compositing. This can impact performance on lower-end devices or when multiple sidebars animate simultaneously.Consider:
- Adding
will-change: backdrop-filterto hint the browser for optimization (as done in breadcrumbs.tsx line 29)- Testing on mobile devices to ensure smooth 60fps animation
- Potentially reducing to 2-3 layers if visual quality remains acceptable
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/blocks/sidebar.tsx` around lines 77 - 118, The GradualBlur component applies four stacked backdrop-filter layers which can be costly; update the GradualBlur function to add a will-change hint (e.g., will-change: "backdrop-filter") to the blur elements (matching the pattern used in breadcrumbs.tsx) and reduce the number of stacked layers from four to 2–3 if visual quality is preserved (adjust the elements with style.backdropFilter and style.maskImage); after changes, test animations on mobile to confirm 60fps performance and revert layer count if visual degradation occurs.apps/www/components/blocks/breadcrumbs.tsx (1)
16-31: 💤 Low valueConsider extracting inline styles to a constant.
The backdrop overlay style object (lines 19-30) is quite large with multiple vendor-prefixed properties. For better maintainability and reusability, consider extracting it to a top-level constant or CSS module.
♻️ Example refactor
+const BACKDROP_STYLE: React.CSSProperties = { + backdropFilter: "blur(4px)", + background: + "linear-gradient(to bottom, color-mix(in oklch, var(--background) 80%, transparent) 0%, transparent 100%)", + height: "106px", + maskImage: "linear-gradient(to bottom, black 50%, transparent 100%)", + WebkitBackdropFilter: "blur(4px)", + WebkitMaskImage: "linear-gradient(to bottom, black 50%, transparent 100%)", + width: "100%", + willChange: "backdrop-filter", +} as const + export function BlockTopBar({ title }: { title: string }) { // ... return ( <div className="..."> <div aria-hidden="true" className="pointer-events-none absolute inset-0 z-10" - style={{ - backdropFilter: "blur(4px)", - background: - "linear-gradient(to bottom, color-mix(in oklch, var(--background) 80%, transparent) 0%, transparent 100%)", - height: "106px", - maskImage: "linear-gradient(to bottom, black 50%, transparent 100%)", - WebkitBackdropFilter: "blur(4px)", - WebkitMaskImage: - "linear-gradient(to bottom, black 50%, transparent 100%)", - width: "100%", - willChange: "backdrop-filter", - }} + style={BACKDROP_STYLE} />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/blocks/breadcrumbs.tsx` around lines 16 - 31, Extract the large inline style object used in the overlay div inside the Breadcrumbs component into a top-level constant (e.g., OVERLAY_STYLE) or a CSS module/class and replace the inline style prop with that constant or className; move vendor-prefixed keys (WebkitBackdropFilter, WebkitMaskImage) and the mask/backdrop values into the constant so the JSX stays concise, and export or import the new constant/CSS from the same module so Breadcrumbs still applies the same visual appearance.apps/www/components/theme-toggle.tsx (1)
47-65: ⚡ Quick winRespect reduced-motion preferences for the new theme animation.
This path always runs the reveal transition and the icon rotation. Users with
prefers-reduced-motion: reduceshould get an instant theme switch and a non-animated icon state instead.Also applies to: 192-209
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/theme-toggle.tsx` around lines 47 - 65, In toggleTheme, respect users' prefers-reduced-motion by detecting window.matchMedia('(prefers-reduced-motion: reduce)'). If reduced-motion is true, skip creating/updating animations (do not call createAnimation or updateStyles), avoid using document.startViewTransition, and immediately call setTheme(...) and setIsDark(...) so the theme and icon state change instantly without rotation/transition; otherwise preserve the existing animated path (createAnimation, updateStyles, and startViewTransition). Ensure the icon's CSS/props reflect a non-animated state when reduced-motion is active.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/www/app/blocks/`[[...slug]]/page.tsx:
- Around line 84-85: Update the react-resizable-panels usage to match the v4+
prop contract: change ResizablePanelGroup's prop from orientation to direction
(e.g., direction="horizontal") and pass numeric fraction sizes (e.g.,
defaultSize={0.35} and minSize={0.30}) to ResizablePanel instead of string
percentages; apply the same updates to the other panel instance referenced
around lines 119-120 so sizing and types align with the ResizablePanelGroup and
ResizablePanel components.
In `@apps/www/components/blocks/block-toolbar.tsx`:
- Around line 207-217: The refresh toolbar item is only handled for pointer taps
and ignored in handleItemClick, so keyboard activation (Enter/Space) doesn't
reload; modify handleItemClick (and the analogous handler used at the other
block) to handle item.id === "refresh" by invoking the same refresh behavior
(e.g., calling the reload function or window.location.reload()) instead of
returning early, and ensure the onClick/onKey handlers delegate to
handleItemClick so both pointer and keyboard activations trigger refresh
consistently; reference handleItemClick and the matching handler in the
duplicate section to apply the same change.
In `@apps/www/components/blocks/breadcrumbs.tsx`:
- Around line 33-40: Update the Tailwind class string in the Breadcrumbs
component: replace the invalid z-150 token with the arbitrary-value syntax
z-[150] and remove the unsupported in- prefix on the conditional selector so
data-[block-preview-expanded=true]:pointer-events-none and
data-[block-preview-expanded=true]:opacity-0 are used instead of
in-data-[block-preview-expanded=true]:...; update the className passed to
cn(...) in the Breadcrumbs component accordingly.
- Around line 68-76: The "Blocks" breadcrumb Link in the breadcrumbs component
currently points to "/" instead of the blocks list; update the Link's href in
apps/www/components/blocks/breadcrumbs.tsx (the Link element rendering "Blocks")
to use "/blocks" so the breadcrumb navigates to the blocks section rather than
the home page.
In `@apps/www/components/blocks/sidebar.tsx`:
- Line 131: The Tailwind class string "fixed inset-0 z-15 hidden bg-transparent"
in the Sidebar component (sidebar.tsx) uses an invalid z-index class; update
that class to a valid Tailwind token such as "z-10" or use an arbitrary value
"z-[15]" to preserve the intended stacking order—replace the "z-15" token inside
the element's className (the same string literal where "fixed inset-0 z-15
hidden bg-transparent" appears) with the chosen valid class.
In `@apps/www/components/sidebar-toggle-icon.tsx`:
- Around line 51-56: The motion.path currently sets the path twice (both animate
and the explicit d prop) which is redundant and can cause hydration/conflict
issues; remove the explicit d={isOpen ? PANEL_OPEN : PANEL_CLOSED} prop from the
motion.path so Framer Motion's animate handles the path transition using the
existing animate={{ d: isOpen ? PANEL_OPEN : PANEL_CLOSED }} expression
(references: motion.path, isOpen, PANEL_OPEN, PANEL_CLOSED).
In `@apps/www/components/theme-toggle.tsx`:
- Around line 47-76: The toggleTheme callback uses the reactive theme value to
decide the next theme, which fails when theme === "system"; change the logic
inside toggleTheme (specifically the inner switchTheme that calls setTheme(theme
=== "light" ? "dark" : "light")) to base the decision on resolvedTheme instead
of theme (i.e. use resolvedTheme === "light" ? "dark" : "light"), and update the
hook dependency array to include resolvedTheme (and remove or keep theme as
appropriate) so React re-creates toggleTheme when the resolved theme changes;
keep setIsDark behavior as-is but ensure resolvedTheme is referenced wherever
the next theme is computed.
- Around line 24-29: The theme icon flicker happens because isDark is
initialized to false and only updated after hydration (useEffect with
resolvedTheme), causing the light icon to paint on first render; fix by adding a
mounted flag (e.g., isMounted state) and only render the ThemeToggle button/icon
(or run the motion animation) after mount or when resolvedTheme is defined.
Concretely: introduce isMounted (set true in useEffect(() => setIsMounted(true),
[])), stop initializing isDark to a hardcoded false (or derive it from
resolvedTheme when available), and wrap the rendering of the motion icon/button
in a conditional like isMounted && (resolvedTheme !== undefined) so the icon
matches the actual theme on first paint; update references to isDark, setIsDark,
resolvedTheme, and the motion/icon render in ThemeToggle accordingly.
In `@apps/www/components/ui/resizable.tsx`:
- Around line 18-24: The resize handle currently strips the keyboard focus
indicator via the class "outline-none" in the JSX classes (the element that also
uses the data-panel-group-direction selectors and the nested rotate rule);
remove "outline-none" and add a visible focus style (for example replace it with
Tailwind focus-visible utilities such as "focus-visible:outline-2
focus-visible:outline-offset-2 focus-visible:outline-primary" or
"focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-primary")
on that same element so keyboard users get a clear focus cue while preserving
the existing layout/rotation rules.
---
Nitpick comments:
In `@apps/www/components/blocks/breadcrumbs.tsx`:
- Around line 16-31: Extract the large inline style object used in the overlay
div inside the Breadcrumbs component into a top-level constant (e.g.,
OVERLAY_STYLE) or a CSS module/class and replace the inline style prop with that
constant or className; move vendor-prefixed keys (WebkitBackdropFilter,
WebkitMaskImage) and the mask/backdrop values into the constant so the JSX stays
concise, and export or import the new constant/CSS from the same module so
Breadcrumbs still applies the same visual appearance.
In `@apps/www/components/blocks/sidebar.tsx`:
- Around line 77-118: The GradualBlur component applies four stacked
backdrop-filter layers which can be costly; update the GradualBlur function to
add a will-change hint (e.g., will-change: "backdrop-filter") to the blur
elements (matching the pattern used in breadcrumbs.tsx) and reduce the number of
stacked layers from four to 2–3 if visual quality is preserved (adjust the
elements with style.backdropFilter and style.maskImage); after changes, test
animations on mobile to confirm 60fps performance and revert layer count if
visual degradation occurs.
In `@apps/www/components/theme-toggle.tsx`:
- Around line 47-65: In toggleTheme, respect users' prefers-reduced-motion by
detecting window.matchMedia('(prefers-reduced-motion: reduce)'). If
reduced-motion is true, skip creating/updating animations (do not call
createAnimation or updateStyles), avoid using document.startViewTransition, and
immediately call setTheme(...) and setIsDark(...) so the theme and icon state
change instantly without rotation/transition; otherwise preserve the existing
animated path (createAnimation, updateStyles, and startViewTransition). Ensure
the icon's CSS/props reflect a non-animated state when reduced-motion is active.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 653d7565-b0d0-48c8-8f5c-4e6a3f802559
⛔ Files ignored due to path filters (4)
.nvmrcis excluded by none and included by noneapps/www/eslint.config.mjsis excluded by none and included by nonebun.lockis excluded by!**/*.lockand included by nonepackage.jsonis excluded by none and included by none
📒 Files selected for processing (18)
apps/www/app/blocks/[[...slug]]/page.tsxapps/www/app/blocks/layout.tsxapps/www/app/shadcn.cssapps/www/components/blocks/block-info-pane.tsxapps/www/components/blocks/block-preview-pane.tsxapps/www/components/blocks/block-showcase.tsxapps/www/components/blocks/block-toolbar.tsxapps/www/components/blocks/blocks-sidebar.tsxapps/www/components/blocks/breadcrumbs.tsxapps/www/components/blocks/info-pane.tsxapps/www/components/blocks/preview-background.tsxapps/www/components/blocks/preview-pane.tsxapps/www/components/blocks/sidebar.tsxapps/www/components/sidebar-toggle-icon.tsxapps/www/components/theme-toggle.tsxapps/www/components/ui/resizable.tsxapps/www/registry/default/blocks/linear-player/components/button.tsxapps/www/registry/default/blocks/linear-player/components/media-player.tsx
💤 Files with no reviewable changes (3)
- apps/www/components/blocks/block-info-pane.tsx
- apps/www/components/blocks/block-preview-pane.tsx
- apps/www/components/blocks/blocks-sidebar.tsx
Summary by CodeRabbit
New Features
Design
Chores