-
Notifications
You must be signed in to change notification settings - Fork 0
fix: zindexes #200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix: zindexes #200
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis pull request migrates the codebase from hard-coded numeric z-index values to a centralized, typed design token system. A new z-index token module with 21 named levels is introduced and integrated across components, CSS utilities, and design systems. Additionally, some components receive layout refinements and improved data handling. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @apps/web/components/widgets/now-playing-widget.tsx:
- Around line 35-65: The outer full-viewport <button> (with onClick=handleClick)
is semantically and accessibly wrong; replace it with a non-interactive Box (or
div) that uses the component's css prop for the overlay positioning/zIndex
instead of inline styles, move the onClick/handleClick and focusable behavior to
the inner interactive container that wraps NowPlaying (give that wrapper
tabIndex=0 or make it a real button/anchor and keep pointerEvents enabled),
ensure only the NowPlaying area is keyboard-focusable (no giant focus ring on
the viewport), preserve zIndex/inset positioning and the NowPlaying props
(title/artist/artwork/isPlaying/isLoading/autoExtractColor/vibrantColor/textColor)
and remove pointerEvents: "none" from the outer element.
In @packages/ui/src/components/work-card/work-card.tsx:
- Around line 320-322: The Box currently uses a hardcoded zIndex value instead
of the centralized token; import zIndex from "@httpjpg/tokens" and replace the
hardcoded value in the Box css prop with zIndex.docked (update the import
section and change the Box css={{ zIndex: 20 }} to use zIndex.docked) so the
component (work-card.tsx, Box) uses the tokenized value.
In @packages/ui/src/components/work-list/work-list.tsx:
- Around line 127-132: The fragment returned around each mapped item currently
uses the shorthand <>...</> while the key is applied to the WorkCard; move the
key to the top-level fragment by replacing the shorthand with an explicit
React.Fragment and give that fragment the key (e.g., React.Fragment
key={work.slug || index}); keep WorkCard and Divider props unchanged and remove
the key from WorkCard.
🧹 Nitpick comments (8)
packages/ui/src/components/mouse-trail/mouse-trail.tsx (1)
130-130: Use imported zIndex token for type safety.The component uses a string literal
"mouseEffects"instead of importing the token from@httpjpg/tokens. This bypasses TypeScript's type checking and could lead to runtime errors if the key is mistyped.♻️ Recommended refactor for type safety
+"use client"; + +import { useEffect, useRef, useState } from "react"; +import type { SystemStyleObject } from "styled-system/types"; +import { Box } from "../box/box"; +import { zIndex } from "@httpjpg/tokens"; export function MouseTrail({ character = "✦", count = 20, lifetime = 1000, size = "24px", color = "black", css: cssProp, }: MouseTrailProps) { // ... return ( <Box css={{ position: "fixed", top: 0, left: 0, w: "100%", h: "100%", pointerEvents: "none", - zIndex: "mouseEffects", + zIndex: zIndex.mouseEffects, ...cssProp, }} >This matches the pattern used in
custom-cursor.tsxand provides autocomplete and compile-time validation.packages/ui/src/components/image-preview/image-preview.tsx (1)
117-117: Use imported zIndex token for type safety.Similar to mouse-trail.tsx, this component uses a string literal
"preview"instead of importing the token. This bypasses type checking and increases the risk of errors.♻️ Recommended refactor for type safety
Add the import at the top of the file:
"use client"; import { useEffect, useRef, useState } from "react"; +import { zIndex } from "@httpjpg/tokens";Then use the imported token:
return ( <div ref={containerRef} style={{ position: "fixed", top: 0, left: 0, width: `${width}px`, height: `${height}px`, transform: "translate(-1000px, -1000px)", pointerEvents: "none", - zIndex: "preview", + zIndex: zIndex.preview, }} >This provides type safety and matches the pattern used in
custom-cursor.tsx.packages/ui/src/components/nav-link/nav-link.tsx (1)
101-106: Inconsistent with token migration goals.The PR introduces a centralized z-index token system to replace hardcoded values, but this change adds a new hardcoded
zIndex: 1. While this might be intentional for local component-level stacking (ensuring nav links render above their siblings), it creates inconsistency in the codebase.Consider using a token like
zIndex.base + 1or documenting why this local z-index is appropriate and shouldn't use the token system.♻️ Option: Use a token for consistency
If this is truly meant to be a minimal local z-index, you could define a token for it or use an existing one. For now, adding a comment would help:
style={{ position: "relative", - zIndex: 1, + zIndex: 1, // Local stacking context for nav link pseudo-elements cursor:packages/tokens/src/z-index.ts (2)
50-54:floatingtoken is out of order and may cause confusion.The
floating: 10token is placed afterheader: 80in the source but has a lower z-index value. This makes the scale harder to read and maintain. Consider reordering tokens by their numeric values for clarity, or grouping related tokens together with clear section comments.
1-11: JSDoc mentions "20-step increments" but values don't follow this pattern.The header comment states the scale "Uses 20-step increments for clear hierarchy," but many values deviate from this (e.g., 10, 300, 320, 340, 360, 380). Consider updating the documentation to accurately describe the actual scale strategy, or reorganize values to match the documented pattern.
packages/storyblok-ui/src/components/work-list/SbWorkList.tsx (3)
60-60: Consider replacingany[]with a specific type.Using
any[]forlastWorkItemsRefbypasses TypeScript's type checking. Define a type for the work item shape to catch potential type mismatches early.♻️ Suggested type definition
interface TransformedWorkItem { id: string; slug: string; title: string; description: ReactNode; images: Array<{ url: string; alt: string; copyright?: string; focus?: string; videoUrl?: string; }>; date: string; baseUrl: string; } // Then use: const lastWorkItemsRef = useRef<TransformedWorkItem[]>([]);
130-137: Silent error handling may hide issues during development.The empty catch block silently swallows errors from
renderStoryblokRichText. Consider logging in development mode to help debug content issues.♻️ Suggested improvement
try { description = renderStoryblokRichText(item.content.description); - } catch { + } catch (error) { // Silently fail - description is optional + if (process.env.NODE_ENV === "development") { + console.warn(`Failed to render description for ${item.slug}:`, error); + } }
81-99: DuplicatedividerPropsconstruction.The
dividerPropsobject is constructed identically in two places (lines 87-96 and 190-199). Consider extracting this to a variable to reduce duplication and ensure consistency.♻️ Suggested refactor
+ const dividerPropsConfig = showDividersValue + ? { + variant: "ascii" as const, + pattern: dividerPattern || "⋆。°✩ ・ ✦ ・ ✧ ・ ✦ ・ ✩°。⋆", + color: mapColorToToken(dividerColor) || "neutral.200", + spacing: dividerSpacing || "3", + } + : undefined; // Then use dividerPropsConfig in both WorkList instancesAlso applies to: 186-200
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
apps/web/components/ui/preview-notification.tsxapps/web/components/widgets/now-playing-widget.tsxapps/web/components/widgets/psn-card.tsxpackages/consent/src/components/cookie-banner.tsxpackages/now-playing/src/now-playing-loading.tsxpackages/now-playing/src/now-playing.tsxpackages/storyblok-ui/src/components/work-list/SbWorkList.tsxpackages/tokens/src/index.tspackages/tokens/src/z-index.tspackages/ui/panda.config.tspackages/ui/src/components/custom-cursor/custom-cursor.tsxpackages/ui/src/components/header/header.tsxpackages/ui/src/components/header/mobile-menu-button.tsxpackages/ui/src/components/header/mobile-menu-content.tsxpackages/ui/src/components/image-preview/image-preview.tsxpackages/ui/src/components/mouse-trail/mouse-trail.tsxpackages/ui/src/components/nav-link/nav-link.tsxpackages/ui/src/components/work-card/work-card.tsxpackages/ui/src/components/work-list/work-list.tsxpackages/ui/styles.css
💤 Files with no reviewable changes (2)
- packages/now-playing/src/now-playing.tsx
- packages/now-playing/src/now-playing-loading.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
⚙️ CodeRabbit configuration file
Review the Typescript and React code for conformity with best practices in React, and Typescript. Highlight any deviations.
Files:
packages/ui/src/components/work-card/work-card.tsxpackages/ui/src/components/nav-link/nav-link.tsxapps/web/components/widgets/psn-card.tsxpackages/tokens/src/z-index.tspackages/tokens/src/index.tspackages/ui/src/components/header/mobile-menu-content.tsxpackages/ui/src/components/custom-cursor/custom-cursor.tsxpackages/ui/panda.config.tsapps/web/components/widgets/now-playing-widget.tsxpackages/consent/src/components/cookie-banner.tsxpackages/ui/src/components/header/header.tsxpackages/ui/src/components/work-list/work-list.tsxapps/web/components/ui/preview-notification.tsxpackages/storyblok-ui/src/components/work-list/SbWorkList.tsxpackages/ui/src/components/header/mobile-menu-button.tsxpackages/ui/src/components/image-preview/image-preview.tsxpackages/ui/src/components/mouse-trail/mouse-trail.tsx
🧬 Code graph analysis (8)
packages/ui/src/components/nav-link/nav-link.tsx (5)
packages/ui/src/components/link/link.tsx (2)
css(77-145)LinkProps(8-33)apps/storybook/.storybook/next-link-mock.tsx (2)
props(21-41)LinkProps(8-18)apps/storybook/stories/Link.stories.tsx (1)
Box(124-148)apps/storybook/stories/NavLink.stories.tsx (2)
Box(142-164)Box(171-193)packages/ui/src/components/header/navigation.tsx (1)
work(89-107)
apps/web/components/widgets/psn-card.tsx (3)
packages/ui/src/components/stack/stack.tsx (1)
css(82-118)apps/storybook/stories/AnimateInView.stories.tsx (1)
Box(218-263)packages/ui/src/components/box/box.tsx (1)
C(142-165)
packages/tokens/src/z-index.ts (1)
packages/tokens/src/index.ts (3)
zIndex(25-25)ZIndexKey(25-25)ZIndexValue(25-25)
packages/tokens/src/index.ts (1)
packages/tokens/scripts/generate-css-vars.ts (1)
generateCSSVariables(31-52)
apps/web/components/widgets/now-playing-widget.tsx (1)
packages/tokens/src/z-index.ts (1)
zIndex(13-139)
packages/consent/src/components/cookie-banner.tsx (2)
packages/tokens/src/index.ts (1)
zIndex(25-25)packages/tokens/src/z-index.ts (1)
zIndex(13-139)
packages/ui/src/components/work-list/work-list.tsx (2)
packages/ui/src/components/work-card/work-card.tsx (1)
WorkCard(280-345)packages/ui/src/components/work-card/index.ts (1)
WorkCard(2-2)
packages/storyblok-ui/src/components/work-list/SbWorkList.tsx (3)
packages/ui/src/components/work-list/work-list.tsx (1)
WorkList(108-139)packages/storyblok-ui/src/lib/token-mapping.ts (1)
mapColorToToken(19-62)packages/storyblok-richtext/src/richtext.tsx (1)
renderStoryblokRichText(253-286)
🔇 Additional comments (18)
packages/tokens/src/index.ts (1)
25-25: LGTM! Clean token export integration.The zIndex token is properly exported with its types and integrated into the consolidated tokens object, following the established pattern used by other design tokens.
Also applies to: 35-35, 55-55
packages/ui/styles.css (1)
783-803: LGTM! Clear z-index hierarchy established.The CSS variables create a well-organized stacking context with semantic naming and appropriate spacing between levels. The range from -1 (hide) to 380 (cursor) provides sufficient granularity for UI layering.
packages/ui/src/components/custom-cursor/custom-cursor.tsx (1)
3-3: LGTM! Exemplary token usage pattern.This component correctly imports and uses the zIndex token, providing type safety and autocomplete. This pattern should be followed by other components in the codebase.
Also applies to: 203-203, 229-229
packages/ui/src/components/header/header.tsx (1)
97-97: LGTM!The z-index has been correctly migrated to use the
"header"token from the centralized z-index scale, replacing the hardcoded value.packages/consent/src/components/cookie-banner.tsx (1)
3-3: LGTM!The z-index has been correctly migrated from the hardcoded value
99999to use the centralized tokenzIndex.cookieBanner(360), with the proper import added. This follows the PR's objective of standardizing z-index values across the codebase.Also applies to: 121-121
apps/web/components/ui/preview-notification.tsx (1)
38-38: LGTM!The z-index has been correctly migrated from the hardcoded value
9999to use the token"preview"(340), aligning with the centralized z-index system.Note: This file uses the string token pattern (
zIndex: "preview"), whilecookie-banner.tsxuses the imported object pattern (zIndex: zIndex.cookieBanner). Both approaches are valid, but consider standardizing the pattern across the codebase for consistency.apps/web/components/widgets/psn-card.tsx (1)
41-41: LGTM! Clean z-index token migration.The migration from numeric
40to the"widget"token aligns with the centralized z-index system. This ensures consistent stacking order for widget-level elements across the codebase.packages/ui/src/components/header/mobile-menu-button.tsx (1)
23-23: LGTM! Z-index token migration.Successfully migrated to the
"mobileMenuButton"token, maintaining the component's fixed positioning on mobile and relative positioning on desktop.packages/ui/panda.config.ts (2)
21-21: LGTM! Z-index token import.Added
zIndexto the imports from the centralized tokens package.
254-254: LGTM! Z-index tokens integrated into Panda theme.The
zIndextokens are properly transformed and exposed in the Panda CSS theme, following the same pattern as other design tokens. This enables consistent z-index usage across all components using the Panda CSScssprop.packages/tokens/src/z-index.ts (2)
72-120: Duplicate z-index values may cause stacking conflicts.Several tokens share identical z-index values:
mobileMenu(300) andtooltip(300) at lines 72 and 114mobileMenuButton(320) andmouseEffects(320) at lines 78 and 120banner(240) andmodal(240) at lines 84 and 96If these elements appear together, their stacking order becomes unpredictable (depends on DOM order). Verify this is intentional, or adjust values to ensure distinct stacking when these elements coexist.
141-142: LGTM on type exports.The
ZIndexKeyandZIndexValuetype exports provide good type safety for consumers of this token module.packages/ui/src/components/header/mobile-menu-content.tsx (3)
29-43: Good adoption of z-index token.The migration from a hardcoded z-index to the
"mobileMenu"token aligns well with the centralized z-index scale. This improves maintainability and ensures consistent stacking across components.
78-90: Good overflow handling for scrollable content.The combination of
overflow: "hidden",overflowY: "auto", andminW: 0correctly enables vertical scrolling while preventing horizontal overflow issues in flex containers.
99-118: Text truncation applied consistently.The ellipsis truncation pattern (
overflow: "hidden",textOverflow: "ellipsis",whiteSpace: "nowrap") is correctly applied to nav links, personal work items, and client work items. This ensures long titles don't break the layout.packages/storyblok-ui/src/components/work-list/SbWorkList.tsx (3)
118-164: Good robust data transformation with fallbacks.The work item transformation includes sensible defaults: title fallback chain, comprehensive date selection, safe image filtering, and video detection. The null filtering with
NonNullabletype guard is correct.
78-115: Good caching strategy for Visual Editor stability.The
lastWorkItemsRefcaching mechanism ensures the UI remains stable during Storyblok Visual Editor reloads when stories are temporarily unresolved. The conditional checkwork && work.length > 0correctly distinguishes between "loading" and "empty" states.
152-158: This concern is unfounded. The Slideshow component checksif (image.videoUrl)before rendering, which ensures videos are rendered viaSlideshowVideoSlide(using the videoUrl property) while images are rendered via theImagecomponent (using the url property). The empty URL string for videos is intentionally set but never used—it's never passed to the Image component because the videoUrl condition is checked first. No downstream issues will occur.
| <button | ||
| type="button" | ||
| onClick={handleClick} | ||
| style={{ | ||
| all: "unset", | ||
| position: "fixed", | ||
| inset: 0, | ||
| pointerEvents: "none", | ||
| zIndex: zIndex.floating, | ||
| }} | ||
| > | ||
| <div style={{ pointerEvents: "auto" }}> | ||
| <NowPlaying | ||
| title={isLoading ? "Loading..." : data?.title || "╱╱ #welovemusic ╱╱"} | ||
| artist={ | ||
| isLoading ? "..." : data?.artist || "⋄ ⋄ ⋄ (spotify(none)) ⋄ ⋄ ⋄" | ||
| } | ||
| artwork={ | ||
| data?.artwork || | ||
| "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Crect fill='%23a3a3a3' width='100' height='100'/%3E%3Ctext x='50' y='50' font-family='monospace' font-size='40' text-anchor='middle' dy='.3em' fill='white'%3E♪%3C/text%3E%3C/svg%3E" | ||
| } | ||
| isPlaying={data?.isPlaying || false} | ||
| isLoading={isLoading} | ||
| autoExtractColor={!!data && !isLoading} | ||
| vibrantColor={ | ||
| !data || isLoading ? "rgba(163, 163, 163, 0.6)" : undefined | ||
| } | ||
| textColor={!data || isLoading ? "white" : undefined} | ||
| /> | ||
| </div> | ||
| </button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reconsider the full-viewport button element for accessibility and semantics.
Wrapping the entire viewport in a <button> element creates several concerns:
-
Semantic HTML: A button spanning the full screen with
pointerEvents: noneis semantically incorrect, even though pointer events are disabled on the outer element. -
Keyboard accessibility: The button remains focusable, which means keyboard users could tab to it and see a focus ring on the entire viewport, creating a confusing experience.
-
Code consistency: Inline styles are used here instead of the
Boxcomponent'scssprop pattern used throughout the codebase.
♿ Proposed architectural improvements
Option 1: Non-interactive outer container (recommended)
+ import { Box } from "@httpjpg/ui";
+
const content = (
- <button
- type="button"
- onClick={handleClick}
- style={{
- all: "unset",
+ <Box
+ css={{
position: "fixed",
inset: 0,
pointerEvents: "none",
zIndex: zIndex.floating,
}}
>
- <div style={{ pointerEvents: "auto" }}>
+ <Box
+ as="button"
+ type="button"
+ onClick={handleClick}
+ css={{
+ all: "unset",
+ pointerEvents: "auto",
+ cursor: "pointer",
+ }}
+ >
<NowPlaying
title={isLoading ? "Loading..." : data?.title || "╱╱ #welovemusic ╱╱"}
artist={
isLoading ? "..." : data?.artist || "⋄ ⋄ ⋄ (spotify(none)) ⋄ ⋄ ⋄"
}
{/* ...rest of props... */}
/>
- </div>
- </button>
+ </Box>
+ </Box>
);This approach:
- Uses a non-interactive
Boxfor the viewport overlay - Makes only the NowPlaying widget area interactive and focusable
- Follows the codebase's Box component pattern
- Maintains proper keyboard navigation semantics
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button | |
| type="button" | |
| onClick={handleClick} | |
| style={{ | |
| all: "unset", | |
| position: "fixed", | |
| inset: 0, | |
| pointerEvents: "none", | |
| zIndex: zIndex.floating, | |
| }} | |
| > | |
| <div style={{ pointerEvents: "auto" }}> | |
| <NowPlaying | |
| title={isLoading ? "Loading..." : data?.title || "╱╱ #welovemusic ╱╱"} | |
| artist={ | |
| isLoading ? "..." : data?.artist || "⋄ ⋄ ⋄ (spotify(none)) ⋄ ⋄ ⋄" | |
| } | |
| artwork={ | |
| data?.artwork || | |
| "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Crect fill='%23a3a3a3' width='100' height='100'/%3E%3Ctext x='50' y='50' font-family='monospace' font-size='40' text-anchor='middle' dy='.3em' fill='white'%3E♪%3C/text%3E%3C/svg%3E" | |
| } | |
| isPlaying={data?.isPlaying || false} | |
| isLoading={isLoading} | |
| autoExtractColor={!!data && !isLoading} | |
| vibrantColor={ | |
| !data || isLoading ? "rgba(163, 163, 163, 0.6)" : undefined | |
| } | |
| textColor={!data || isLoading ? "white" : undefined} | |
| /> | |
| </div> | |
| </button> | |
| <Box | |
| css={{ | |
| position: "fixed", | |
| inset: 0, | |
| pointerEvents: "none", | |
| zIndex: zIndex.floating, | |
| }} | |
| > | |
| <Box | |
| as="button" | |
| type="button" | |
| onClick={handleClick} | |
| css={{ | |
| all: "unset", | |
| pointerEvents: "auto", | |
| cursor: "pointer", | |
| }} | |
| > | |
| <NowPlaying | |
| title={isLoading ? "Loading..." : data?.title || "╱╱ #welovemusic ╱╱"} | |
| artist={ | |
| isLoading ? "..." : data?.artist || "⋄ ⋄ ⋄ (spotify(none)) ⋄ ⋄ ⋄" | |
| } | |
| artwork={ | |
| data?.artwork || | |
| "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Crect fill='%23a3a3a3' width='100' height='100'/%3E%3Ctext x='50' y='50' font-family='monospace' font-size='40' text-anchor='middle' dy='.3em' fill='white'%3E♪%3C/text%3E%3C/svg%3E" | |
| } | |
| isPlaying={data?.isPlaying || false} | |
| isLoading={isLoading} | |
| autoExtractColor={!!data && !isLoading} | |
| vibrantColor={ | |
| !data || isLoading ? "rgba(163, 163, 163, 0.6)" : undefined | |
| } | |
| textColor={!data || isLoading ? "white" : undefined} | |
| /> | |
| </Box> | |
| </Box> |
🤖 Prompt for AI Agents
In @apps/web/components/widgets/now-playing-widget.tsx around lines 35 - 65, The
outer full-viewport <button> (with onClick=handleClick) is semantically and
accessibly wrong; replace it with a non-interactive Box (or div) that uses the
component's css prop for the overlay positioning/zIndex instead of inline
styles, move the onClick/handleClick and focusable behavior to the inner
interactive container that wraps NowPlaying (give that wrapper tabIndex=0 or
make it a real button/anchor and keep pointerEvents enabled), ensure only the
NowPlaying area is keyboard-focusable (no giant focus ring on the viewport),
preserve zIndex/inset positioning and the NowPlaying props
(title/artist/artwork/isPlaying/isLoading/autoExtractColor/vibrantColor/textColor)
and remove pointerEvents: "none" from the outer element.
| <Box css={{ zIndex: 20 }}> | ||
| {" "} | ||
| {/* @httpjpg/tokens zIndex.docked */} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incomplete token migration.
The comment indicates this should use zIndex.docked from @httpjpg/tokens, but the implementation still uses the hardcoded value 20. To complete the migration to the centralized token system, import and use the token directly.
♻️ Complete the token migration
Add the import at the top of the file:
+"use client";
+
+import { zIndex } from "@httpjpg/tokens";
import type { ReactNode } from "react";Then apply this change to use the token:
- <Box css={{ zIndex: 20 }}>
- {" "}
- {/* @httpjpg/tokens zIndex.docked */}
+ <Box css={{ zIndex: zIndex.docked }}>
<Box🤖 Prompt for AI Agents
In @packages/ui/src/components/work-card/work-card.tsx around lines 320 - 322,
The Box currently uses a hardcoded zIndex value instead of the centralized
token; import zIndex from "@httpjpg/tokens" and replace the hardcoded value in
the Box css prop with zIndex.docked (update the import section and change the
Box css={{ zIndex: 20 }} to use zIndex.docked) so the component (work-card.tsx,
Box) uses the tokenized value.
| <> | ||
| <WorkCard key={work.slug || index} {...work} /> | ||
| {showDividers && index < works.length - 1 && ( | ||
| <Divider orientation="horizontal" {...dividerProps} /> | ||
| )} | ||
| </VStack> | ||
| </> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
React key must be on the fragment, not the child element.
The key prop is currently on WorkCard, but React requires the key on the top-level element returned from .map(). Since a fragment is the outer element, it needs the key. The shorthand fragment syntax <>...</> doesn't support keys, so you must use the explicit React.Fragment syntax.
🔑 Proposed fix: Move key to React.Fragment
+import { Fragment } from "react";
+
// ... rest of imports
{works.map((work, index) => (
- <>
- <WorkCard key={work.slug || index} {...work} />
+ <Fragment key={work.slug || index}>
+ <WorkCard {...work} />
{showDividers && index < works.length - 1 && (
<Divider orientation="horizontal" {...dividerProps} />
)}
- </>
+ </Fragment>
))}🤖 Prompt for AI Agents
In @packages/ui/src/components/work-list/work-list.tsx around lines 127 - 132,
The fragment returned around each mapped item currently uses the shorthand
<>...</> while the key is applied to the WorkCard; move the key to the top-level
fragment by replacing the shorthand with an explicit React.Fragment and give
that fragment the key (e.g., React.Fragment key={work.slug || index}); keep
WorkCard and Divider props unchanged and remove the key from WorkCard.
This pull request introduces a new, centralized z-index scale to the design tokens and refactors all usages of hardcoded z-index values across the codebase to use these named tokens. This ensures consistent stacking order for UI elements, improves maintainability, and enhances clarity when working with overlays, widgets, banners, and other layered components. Additionally, some improvements to widget and menu styling are included.
Design Tokens & Theming
zIndexscale to@httpjpg/tokens, providing named z-index values for all UI stacking needs (e.g.,modal,header,floating,cookieBanner, etc.), along with TypeScript types for safety.zIndexand related types from the tokens package and integrated them into the Panda CSS config and the completetokensobject. [1] [2] [3] [4] [5]Refactoring z-index Usage
Header,MobileMenuButton,MobileMenuContent,CustomCursor,PreviewNotificationContent,NowPlayingWidgetComponent,PSNCard,CookieBanner) with the new named tokens fromzIndex, ensuring consistent stacking order throughout the app. [1] [2] [3] [4] [5] [6] [7] [8] [9]Widget and Menu Styling Improvements
NowPlayingWidgetComponentto use thezIndex.floatingtoken and adjusted its structure for better pointer event handling. [1] [2]MobileMenuContentwith better overflow handling, maximum width constraints, and more robust layout for mobile navigation. [1] [2] [3]Storyblok Work List Component Enhancements
SbWorkListto support new props for divider customization (gap,showDividers,dividerPattern,dividerColor,dividerSpacing), improved error handling and fallback rendering, and ensured more robust data transformation for work items. [1] [2] [3] [4]These changes collectively make the UI more maintainable, visually consistent, and easier to reason about when developing new features or debugging stacking issues.
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.