Conversation
Bug Report
Comments? Email us. |
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the WalkthroughThis update migrates the codebase to Tailwind CSS v4, overhauling global styles, utility class syntax, and configuration. It removes custom Tailwind config packages, rewrites CSS imports and variables, and updates all related dependencies and usage patterns. Numerous class names and selectors are modernized for compatibility with the new Tailwind paradigm. Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant TailwindV4 as Tailwind CSS v4 Engine
participant App as Application Components
Dev->>TailwindV4: Import Tailwind v4 config and plugins
TailwindV4->>App: Provide CSS variables, utilities, and new class syntax
Dev->>App: Update classNames, selectors, and imports to v4 syntax
App->>TailwindV4: Use new utilities, variants, and theme variables
App->>App: Render UI with modernized Tailwind classes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Poem
✨ Finishing Touches🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Bug Report
Comments? Email us. |
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
Bug Report
Comments? Email us. |
There was a problem hiding this comment.
Actionable comments posted: 14
🔭 Outside diff range comments (1)
apps/mail/components/mail/mail-display.tsx (1)
1483-1489:flex-nowrap!is redundant and noisyYou’re stacking
flex-colwithflex-nowrap!.
flex-wraphas no effect in column direction, so the class adds bytes without changing physics.-<div className="text-muted-foreground mr-2 flex flex-col flex-nowrap! items-end ... +<div className="text-muted-foreground mr-2 flex flex-col items-end ...Strip the dead mass; lighter ships go faster.
| <div className="animate-in slide-in-from-bottom-4 mx-auto flex max-w-[600px] grow items-center justify-center space-y-8 px-4 duration-500 sm:px-12 md:px-0"> | ||
| <div className="w-full space-y-4"> |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Trim the className line to respect the 100-char flight ceiling
The class string on Line 137 is ~130 chars. Per repo guidelines we should stay ≤ 100 chars; wrap with template literals / clsx to keep the code breathable for future engineers.
-<div className="animate-in slide-in-from-bottom-4 mx-auto flex max-w-[600px] grow items-center justify-center space-y-8 px-4 duration-500 sm:px-12 md:px-0">
+<div
+ className={clsx(
+ 'animate-in slide-in-from-bottom-4 mx-auto flex max-w-[600px] grow',
+ 'items-center justify-center space-y-8 px-4 duration-500 sm:px-12 md:px-0',
+ )}
+>📝 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.
| <div className="animate-in slide-in-from-bottom-4 mx-auto flex max-w-[600px] grow items-center justify-center space-y-8 px-4 duration-500 sm:px-12 md:px-0"> | |
| <div className="w-full space-y-4"> | |
| <div | |
| className={clsx( | |
| 'animate-in slide-in-from-bottom-4 mx-auto flex max-w-[600px] grow', | |
| 'items-center justify-center space-y-8 px-4 duration-500 sm:px-12 md:px-0', | |
| )} | |
| > | |
| <div className="w-full space-y-4"> |
🤖 Prompt for AI Agents
In apps/mail/app/(auth)/login/login-client.tsx around lines 137 to 138, the
className string exceeds 100 characters, violating the repo style guide.
Refactor the className by splitting it into multiple lines using template
literals or the clsx utility to keep each line under 100 characters, improving
readability and maintainability.
| @plugin "tailwind-scrollbar" { | ||
| nocompatible: true; | ||
| preferredStrategy: "pseudoelements"; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Plugin-block keys trip linters but are harmless
Biome shouts about nocompatible and preferredStrategy because they’re not real CSS properties. They’re Tailwind-scrollbar plugin options, so feel free to ignore the noise; production CSS remains unaffected.
🧰 Tools
🪛 Biome (2.1.2)
[error] 14-14: Unknown property is not allowed.
See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.
(lint/correctness/noUnknownProperty)
[error] 15-15: Unknown property is not allowed.
See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.
(lint/correctness/noUnknownProperty)
🤖 Prompt for AI Agents
In apps/mail/app/globals.css around lines 13 to 16, the keys `nocompatible` and
`preferredStrategy` inside the @plugin block trigger linter warnings because
they are not standard CSS properties. Since these are valid Tailwind-scrollbar
plugin options and do not affect production CSS, you can safely ignore these
linter warnings without making any code changes.
| @custom-variant dark (&:where(.dark, .dark *)); | ||
|
|
||
| :root { |
There was a problem hiding this comment.
Aim for Mars-grade correctness: swap @custom-variant for @variant
Tailwind v4 specifies the @variant directive. Using @custom-variant risks the rule being silently skipped by the compiler — that’s like launching without telemetry.
-@custom-variant dark (&:where(.dark, .dark *));
+@variant dark (&:where(.dark, .dark *));📝 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.
| /* Custom variant for dark mode */ | |
| @custom-variant dark (&:where(.dark, .dark *)); | |
| /* Custom variant for dark mode */ | |
| @variant dark (&:where(.dark, .dark *)); |
🤖 Prompt for AI Agents
In apps/mail/app/globals.css around lines 18 to 20, replace the directive
@custom-variant with @variant as Tailwind v4 requires @variant for defining
custom variants. This change ensures the CSS rule is recognized and processed
correctly by the Tailwind compiler, preventing it from being skipped.
| <div className="fixed inset-0 z-9999 flex items-center justify-center bg-black/50 backdrop-blur-sm"> | ||
| <div className="bg-panelLight dark:bg-panelDark flex flex-col items-center gap-4 rounded-xl border p-6 shadow-xl"> |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Sanity-check z-index 9999
Tailwind v4 now supports raw numbers (z-9999), but only if they’re enabled in safelisting or JIT finds them. Make sure this class actually lands in the generated CSS; otherwise the overlay may sink behind modals in production.
Run a quick grep after build:
🏁 Script executed:
#!/bin/bash
# Verify that the generated CSS contains `.z-9999`
rg -F '.z-9999' $(fd -e css dist)Length of output: 33
Let’s dig into the Tailwind config to see if z-9999 is even being safelisted or extended:
This will confirm whether z-9999 is configured to make it into your final CSS. If you still don’t see it after that, you’ll need to add it to the safelist or extend theme.zIndex.
🏁 Script executed:
#!/bin/bash
# Locate your Tailwind config
config_file=$(fd tailwind.config.js | head -n 1)
echo "Using config: $config_file"
# Check for a safelist entry
rg -n "safelist" "$config_file" || echo "No safelist block found"
# Check for theme.extend.zIndex
rg -n "zIndex" "$config_file" || echo "No zIndex extension found"Length of output: 329
Let’s locate your Tailwind config and inspect whether z-9999 is safelisted or extended:
🏁 Script executed:
#!/bin/bash
# Find any Tailwind config file in the repo
configs=$(fd 'tailwind.config.*' -t f)
if [ -z "$configs" ]; then
echo "🚨 No Tailwind config file found"
exit 0
fi
echo "🔍 Found config(s):"
echo "$configs"
for f in $configs; do
echo
echo "=== Inspecting $f ==="
echo "- Safelist entries:"
rg -n "safelist" "$f" || echo " • No safelist block found"
echo "- zIndex extensions:"
rg -n "zIndex" "$f" || echo " • No zIndex extension found"
doneLength of output: 141
Add explicit Tailwind config for z-9999
Our checks show:
- No
tailwind.config.*file was found in the repo. .z-9999isn’t present in the generated CSS (dist/**/*.css).
By default Tailwind v4 JIT won’t emit arbitrary z-index values unless they’re safelisted or declared in theme.extend.zIndex. To fix this, add a tailwind.config.js at the project root (or update your existing config) with:
// tailwind.config.js
module.exports = {
theme: {
extend: {
zIndex: {
9999: '9999',
},
},
},
safelist: [
'z-9999',
],
};This will guarantee that the z-9999 class is generated in production.
🤖 Prompt for AI Agents
In apps/mail/components/context/loading-context.tsx around lines 25 to 26, the
Tailwind class "z-9999" is used but not generated because there is no
tailwind.config.js file or configuration for this custom z-index. To fix this,
create or update the tailwind.config.js file at the project root to extend the
theme's zIndex with 9999 and add "z-9999" to the safelist array. This ensures
the "z-9999" class is included in the generated CSS in production.
| <button | ||
| key={query} | ||
| onClick={() => onQueryClick(query)} | ||
| className="flex-shrink-0 whitespace-nowrap rounded-md bg-[#f0f0f0] p-1 px-2 text-sm text-[#555555] dark:bg-[#262626] dark:text-[#929292]"> | ||
| className="shrink-0 whitespace-nowrap rounded-md bg-[#f0f0f0] p-1 px-2 text-sm text-[#555555] dark:bg-[#262626] dark:text-[#929292]"> | ||
| {query} | ||
| </button> | ||
| ))} |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Consider adding explicit button type for enhanced reliability!
These buttons should specify type="button" to prevent accidental form submissions - it's like adding extra safety protocols to a rocket launch sequence. While not breaking anything currently, explicit typing makes the code more robust.
<button
+ type="button"
key={query}
onClick={() => onQueryClick(query)}
className="shrink-0 whitespace-nowrap rounded-md bg-[#f0f0f0] p-1 px-2 text-sm text-[#555555] dark:bg-[#262626] dark:text-[#929292]">Also applies to: 100-107
🧰 Tools
🪛 Biome (2.1.2)
[error] 87-90: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🤖 Prompt for AI Agents
In apps/mail/components/create/ai-chat.tsx around lines 87 to 93 and also lines
100 to 107, the button elements lack an explicit type attribute, which can cause
unintended form submissions when these buttons are inside a form. To fix this,
add type="button" to each button element to explicitly define their behavior and
prevent accidental form submissions.
| className="w-full max-w-none! pb-1 overflow-visible" | ||
| onSendEmail={handleSendEmail} |
There was a problem hiding this comment.
🚨 Trailing-bang class looks non-standard – risk of dropping out of orbit
max-w-none! isn’t a documented Tailwind v3 or v4 utility; the important modifier is meant to be a prefix (!max-w-none). With the bang at the tail end, the compiler will likely ignore the rule, leaving the composer width constrained when you least expect it.
- className="w-full max-w-none! pb-1 overflow-visible"
+ className="w-full !max-w-none pb-1 overflow-visible"Let’s keep the engines pointed in the right direction.
📝 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.
| className="w-full max-w-none! pb-1 overflow-visible" | |
| onSendEmail={handleSendEmail} | |
| className="w-full !max-w-none pb-1 overflow-visible" | |
| onSendEmail={handleSendEmail} |
🤖 Prompt for AI Agents
In apps/mail/components/mail/reply-composer.tsx around lines 248 to 249, the
className includes a trailing bang in "max-w-none!" which is not a valid
Tailwind CSS syntax. To fix this, move the bang to the front of the utility
class so it reads "!max-w-none" to correctly apply the important modifier and
ensure the max width is not constrained unexpectedly.
| }} | ||
| className="z-30 mt-auto inline-flex h-10 items-center justify-center gap-2.5 self-stretch overflow-hidden rounded-lg bg-[#2D2D2D] p-3 shadow shadow-black/30 outline outline-1 outline-offset-[-1px] outline-[#434343]" | ||
| className="z-30 mt-auto inline-flex h-10 items-center justify-center gap-2.5 self-stretch overflow-hidden rounded-lg bg-[#2D2D2D] p-3 shadow shadow-black/30 outline outline-1 -outline-offset-1 outline-[#434343]" | ||
| > | ||
| <div className="flex items-center justify-center gap-2.5 px-1"> |
There was a problem hiding this comment.
Outline offset changed from -1 px to -4 px – likely unintended
-outline-offset-1 equals −0.25 rem (≈ 4 px). The previous [-1px] value was a single pixel. If pixel-perfect alignment is critical, use the new arbitrary negative form:
-className="... outline outline-1 -outline-offset-1 ..."
+className="... outline outline-1 -outline-offset-[1px] ..."Same applies to the other two buttons.
Also applies to: 240-243, 288-291
🧰 Tools
🪛 Biome (2.1.2)
[error] 160-173: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🤖 Prompt for AI Agents
In apps/mail/components/pricing/pricing-card.tsx around lines 170 to 173, the
outline offset class changed from a pixel value to a rem value, causing a larger
offset than intended. Replace the current -outline-offset-1 class with the
Tailwind arbitrary value syntax -outline-offset-[-1px] to maintain the original
1px negative offset. Apply the same fix to the similar outline offset classes on
lines 240-243 and 288-291 to ensure consistent pixel-perfect alignment across
all buttons.
| <button | ||
| className="z-50 inline-flex h-24 cursor-pointer items-center justify-center gap-2.5 self-stretch overflow-hidden rounded-lg bg-white p-3 outline outline-1 outline-offset-[-1px] outline-gray-400 disabled:cursor-not-allowed disabled:opacity-50 dark:outline-[#2D2D2D]" | ||
| className="z-50 inline-flex h-24 cursor-pointer items-center justify-center gap-2.5 self-stretch overflow-hidden rounded-lg bg-white p-3 outline outline-1 -outline-offset-1 outline-gray-400 disabled:cursor-not-allowed disabled:opacity-50 dark:outline-[#2D2D2D]" | ||
| onClick={handleUpgrade} | ||
| disabled={isLoading} |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Same negative outline-offset delta here
The button now sits at −4 px instead of −1 px. Apply -outline-offset-[1px] if the single-pixel inset was intentional.
🧰 Tools
🪛 Biome (2.1.2)
[error] 176-180: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🤖 Prompt for AI Agents
In apps/mail/components/ui/pricing-dialog.tsx around lines 176 to 179, the
button's outline-offset is set to -1px but the current code applies a -4px
offset, causing the button to sit incorrectly. Adjust the outline-offset CSS
class to use '-outline-offset-[1px]' to ensure the outline is inset by exactly 1
pixel as intended.
| <div className="sticky top-0 z-15 flex items-center justify-between gap-1.5 border-b border-[#E7E7E7] p-2 px-[20px] transition-colors md:min-h-14 dark:border-[#252525]"> | ||
| <SidebarToggle className="h-fit px-2" /> |
There was a problem hiding this comment.
z-15 is not a core scale value
Tailwind’s default z-index scale is 0,10,20,30,40,50. Unless your CSS-first theme explicitly extends it, z-15 will compile to nothing, dropping the stacking context and potentially burying the sticky header.
- <div className="sticky top-0 z-15 ...
+ <div className="sticky top-0 z-[15] ...Re-introducing the arbitrary value guarantees the intended layer.
📝 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.
| <div className="sticky top-0 z-15 flex items-center justify-between gap-1.5 border-b border-[#E7E7E7] p-2 px-[20px] transition-colors md:min-h-14 dark:border-[#252525]"> | |
| <SidebarToggle className="h-fit px-2" /> | |
| <div className="sticky top-0 z-[15] flex items-center justify-between gap-1.5 border-b border-[#E7E7E7] p-2 px-[20px] transition-colors md:min-h-14 dark:border-[#252525]"> | |
| <SidebarToggle className="h-fit px-2" /> |
🤖 Prompt for AI Agents
In apps/mail/components/ui/settings-content.tsx around lines 11 to 12, the class
"z-15" is not part of Tailwind's default z-index scale and will not apply any
stacking context. Replace "z-15" with an arbitrary value using square brackets,
such as "z-[15]", to ensure the sticky header maintains the correct stacking
order.
| import tailwindcss from "@tailwindcss/vite"; | ||
| import { defineConfig } from 'vite'; |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Swap double quotes for single to stay on-brand
Line 7 breaks the house style (' over " for TS files).
-import tailwindcss from "@tailwindcss/vite";
+import tailwindcss from '@tailwindcss/vite';📝 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.
| import tailwindcss from "@tailwindcss/vite"; | |
| import { defineConfig } from 'vite'; | |
| import tailwindcss from '@tailwindcss/vite'; | |
| import { defineConfig } from 'vite'; |
🤖 Prompt for AI Agents
In apps/mail/vite.config.ts at lines 7 to 8, the import statement uses double
quotes instead of single quotes, which breaks the project's style guide. Change
the double quotes around "@tailwindcss/vite" to single quotes to maintain
consistency with the house style for TypeScript files.
There was a problem hiding this comment.
cubic analysis
40 issues found across 60 files • Review in cubic
React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.
| 'p-1', | ||
| position === 'popper' && | ||
| 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]', | ||
| 'h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)', |
There was a problem hiding this comment.
Arbitrary value syntax for custom properties is incorrect; use square brackets (e.g., h-[--radix-select-trigger-height]) instead of parentheses.
Prompt for AI agents
Address the following comment on apps/mail/components/ui/select.tsx at line 79:
<comment>Arbitrary value syntax for custom properties is incorrect; use square brackets (e.g., h-[--radix-select-trigger-height]) instead of parentheses.</comment>
<file context>
@@ -76,7 +76,7 @@ const SelectContent = React.forwardRef<
className={cn(
'p-1',
position === 'popper' &&
- 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',
+ 'h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)',
)}
>
</file context>
| 'h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)', | |
| 'h-[--radix-select-trigger-height] w-full min-w-[--radix-select-trigger-width]', |
| ref={ref} | ||
| className={cn( | ||
| 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] origin-[--radix-select-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-md border shadow-md', | ||
| 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border shadow-md', |
There was a problem hiding this comment.
Arbitrary value syntax for custom properties is incorrect; use square brackets (e.g., max-h-[--radix-select-content-available-height]) instead of parentheses.
Prompt for AI agents
Address the following comment on apps/mail/components/ui/select.tsx at line 66:
<comment>Arbitrary value syntax for custom properties is incorrect; use square brackets (e.g., max-h-[--radix-select-content-available-height]) instead of parentheses.</comment>
<file context>
@@ -63,7 +63,7 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content
ref={ref}
className={cn(
- 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] origin-[--radix-select-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-md border shadow-md',
+ 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border shadow-md',
position === 'popper' &&
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
</file context>
| 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border shadow-md', | |
| 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-[--radix-select-content-available-height] min-w-32 origin-[--radix-select-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-md border shadow-md', |
| classNames: { | ||
| title: 'title flex-1 justify-center !text-black dark:!text-white text-sm leading-none', | ||
| description: '!text-black dark:!text-white text-xs', | ||
| title: 'title flex-1 justify-center text-black! dark:text-white! text-sm leading-none', |
There was a problem hiding this comment.
Rule violated: Detect React performance bottlenecks and rule breaking
Defining the classNames object literal inline as a prop value causes a new object to be created on every render, which can lead to unnecessary re-renders and performance bottlenecks. Move the classNames object outside the component or memoize it with useMemo to comply with React performance best practices and the project's rule.
(Reflects your team's feedback about avoiding inline object literals in JSX props to prevent unnecessary re-renders.)
Prompt for AI agents
Address the following comment on apps/mail/components/ui/toast.tsx at line 23:
<comment>Defining the classNames object literal inline as a prop value causes a new object to be created on every render, which can lead to unnecessary re-renders and performance bottlenecks. Move the classNames object outside the component or memoize it with useMemo to comply with React performance best practices and the project's rule.
(Reflects your team's feedback about avoiding inline object literals in JSX props to prevent unnecessary re-renders.)</comment>
<file context>
@@ -20,15 +20,15 @@ const Toaster = () => {
}}
toastOptions={{
classNames: {
- title: 'title flex-1 justify-center !text-black dark:!text-white text-sm leading-none',
- description: '!text-black dark:!text-white text-xs',
+ title: 'title flex-1 justify-center text-black! dark:text-white! text-sm leading-none',
+ description: 'text-black! dark:text-white! text-xs',
toast: 'p-1',
</file context>
| </button> | ||
| </TooltipTrigger> | ||
| <TooltipContent className="z-[99] flex gap-1 px-1 py-1" side="top" align="end"> | ||
| <TooltipContent className="z-99 flex gap-1 px-1 py-1" side="top" align="end"> |
There was a problem hiding this comment.
Rule violated: Detect React performance bottlenecks and rule breaking
Defining an anonymous function inline for the onClick prop inside a list (via handleFilterByLabel(label)) causes a new function instance on every render, leading to potential performance issues and unnecessary re-renders. Move the function definition outside the render or memoize it with useCallback/useMemo as appropriate.
Prompt for AI agents
Address the following comment on apps/mail/components/mail/render-labels.tsx at line 58:
<comment>Defining an anonymous function inline for the onClick prop inside a list (via handleFilterByLabel(label)) causes a new function instance on every render, leading to potential performance issues and unnecessary re-renders. Move the function definition outside the render or memoize it with useCallback/useMemo as appropriate.</comment>
<file context>
@@ -55,7 +55,7 @@ export const RenderLabels = ({ count = 1, labels }: { count?: number; labels: La
+{hiddenLabels.length}
</button>
</TooltipTrigger>
- <TooltipContent className="z-[99] flex gap-1 px-1 py-1" side="top" align="end">
+ <TooltipContent className="z-99 flex gap-1 px-1 py-1" side="top" align="end">
{hiddenLabels.map((label) => (
<button
</file context>
| <NavigationMenuPrimitive.Viewport | ||
| className={cn( | ||
| 'origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]', | ||
| 'origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-md border shadow md:w-(--radix-navigation-menu-viewport-width)', |
There was a problem hiding this comment.
Arbitrary value syntax for h-(--radix-navigation-menu-viewport-height) and md:w-(--radix-navigation-menu-viewport-width) is invalid in Tailwind v4; use h-[--radix-navigation-menu-viewport-height] and md:w-[--radix-navigation-menu-viewport-width] instead.
Prompt for AI agents
Address the following comment on apps/mail/components/ui/navigation-menu.tsx at line 83:
<comment>Arbitrary value syntax for h-(--radix-navigation-menu-viewport-height) and md:w-(--radix-navigation-menu-viewport-width) is invalid in Tailwind v4; use h-[--radix-navigation-menu-viewport-height] and md:w-[--radix-navigation-menu-viewport-width] instead.</comment>
<file context>
@@ -80,7 +80,7 @@ const NavigationMenuViewport = React.forwardRef<
<div className={cn('absolute left-0 top-full flex justify-center')}>
<NavigationMenuPrimitive.Viewport
className={cn(
- 'origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]',
+ 'origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-md border shadow md:w-(--radix-navigation-menu-viewport-width)',
className,
)}
</file context>
| 'origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-md border shadow md:w-(--radix-navigation-menu-viewport-width)', | |
| 'origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[--radix-navigation-menu-viewport-height] w-full overflow-hidden rounded-md border shadow md:w-[--radix-navigation-menu-viewport-width]', |
| ref={ref} | ||
| className={cn( | ||
| 'data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden', | ||
| 'data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-1 flex h-1.5 items-end justify-center overflow-hidden', |
There was a problem hiding this comment.
z-1 is not a valid Tailwind v4 class; use z-[1] for z-index: 1 instead.
Prompt for AI agents
Address the following comment on apps/mail/components/ui/navigation-menu.tsx at line 100:
<comment>z-1 is not a valid Tailwind v4 class; use z-[1] for z-index: 1 instead.</comment>
<file context>
@@ -97,7 +97,7 @@ const NavigationMenuIndicator = React.forwardRef<
<NavigationMenuPrimitive.Indicator
ref={ref}
className={cn(
- 'data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden',
+ 'data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-1 flex h-1.5 items-end justify-center overflow-hidden',
className,
)}
</file context>
| 'data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-1 flex h-1.5 items-end justify-center overflow-hidden', | |
| 'data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden', |
| <div className="flex-1 overflow-y-auto"> | ||
| <div className="mx-auto max-w-[1600px] p-4 md:p-6 lg:p-8"> | ||
| <div className="bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-10 mb-8 backdrop-blur"> | ||
| <div className="bg-background/95 supports-backdrop-filter:bg-background/60 sticky top-0 z-10 mb-8 backdrop-blur"> |
There was a problem hiding this comment.
The supports variant syntax is incorrect for Tailwind v4. Use supports-[backdrop-filter]:bg-background/60 instead of supports-backdrop-filter:bg-background/60.
Prompt for AI agents
Address the following comment on apps/mail/app/(routes)/developer/page.tsx at line 76:
<comment>The supports variant syntax is incorrect for Tailwind v4. Use supports-[backdrop-filter]:bg-background/60 instead of supports-backdrop-filter:bg-background/60.</comment>
<file context>
@@ -73,7 +73,7 @@ export default function DeveloperPage() {
<div className="bg-background flex min-h-screen w-full flex-col">
<div className="flex-1 overflow-y-auto">
<div className="mx-auto max-w-[1600px] p-4 md:p-6 lg:p-8">
- <div className="bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-10 mb-8 backdrop-blur">
+ <div className="bg-background/95 supports-backdrop-filter:bg-background/60 sticky top-0 z-10 mb-8 backdrop-blur">
<Button
variant="ghost"
</file context>
| <div className="bg-background/95 supports-backdrop-filter:bg-background/60 sticky top-0 z-10 mb-8 backdrop-blur"> | |
| <div className="bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-10 mb-8 backdrop-blur"> |
| default: 'h-8 text-sm', | ||
| sm: 'h-7 text-xs', | ||
| lg: 'h-12 text-sm group-data-[collapsible=icon]:!p-0', | ||
| lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!', |
There was a problem hiding this comment.
Use !p-0 instead of p-0! for Tailwind v4 important modifier syntax.
Prompt for AI agents
Address the following comment on apps/mail/components/ui/sidebar.tsx at line 379:
<comment>Use !p-0 instead of p-0! for Tailwind v4 important modifier syntax.</comment>
<file context>
@@ -365,18 +365,18 @@ const SidebarMenuItem = React.forwardRef<HTMLLIElement, React.ComponentProps<'li
SidebarMenuItem.displayName = 'SidebarMenuItem';
const sidebarMenuButtonVariants = cva(
- 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
+ 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
{
variants: {
variant: {
default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
</file context>
| lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!', | |
| lg: 'h-12 text-sm group-data-[collapsible=icon]:!p-0', |
|
|
||
| const sidebarMenuButtonVariants = cva( | ||
| 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0', | ||
| 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0', |
There was a problem hiding this comment.
Use !size-8 and !p-2 instead of size-8! and p-2! for Tailwind v4 important modifier syntax.
Prompt for AI agents
Address the following comment on apps/mail/components/ui/sidebar.tsx at line 368:
<comment>Use !size-8 and !p-2 instead of size-8! and p-2! for Tailwind v4 important modifier syntax.</comment>
<file context>
@@ -365,18 +365,18 @@ const SidebarMenuItem = React.forwardRef<HTMLLIElement, React.ComponentProps<'li
SidebarMenuItem.displayName = 'SidebarMenuItem';
const sidebarMenuButtonVariants = cva(
- 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
+ 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
{
variants: {
</file context>
| 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0', | |
| 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0', |
| ref={ref} | ||
| data-sidebar="group" | ||
| className={cn('relative flex w-full min-w-0 flex-col !pt-0 md:p-2', className)} | ||
| className={cn('relative flex w-full min-w-0 flex-col pt-0! md:p-2', className)} |
There was a problem hiding this comment.
Use !pt-0 instead of pt-0! for Tailwind v4 important modifier syntax.
Prompt for AI agents
Address the following comment on apps/mail/components/ui/sidebar.tsx at line 279:
<comment>Use !pt-0 instead of pt-0! for Tailwind v4 important modifier syntax.</comment>
<file context>
@@ -276,7 +276,7 @@ const SidebarGroup = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'
<div
ref={ref}
data-sidebar="group"
- className={cn('relative flex w-full min-w-0 flex-col !pt-0 md:p-2', className)}
+ className={cn('relative flex w-full min-w-0 flex-col pt-0! md:p-2', className)}
{...props}
/>
</file context>
| className={cn('relative flex w-full min-w-0 flex-col pt-0! md:p-2', className)} | |
| className={cn('relative flex w-full min-w-0 flex-col !pt-0 md:p-2', className)} |
Bug Report
Comments? Email us. |
Bug Report
Comments? Email us. |
Bug Report
Comments? Email us. |
closes #1604
Description
This PR upgrades Mail0 (Zero) to Tailwind CSS v4.
@zero/tailwind-configpackage@plugindirective@tailwindcss/viteplugintailwindcsspluginpostcssdependency altogether (no longer used)HomeContentonmdscreenstailwindcss-animate, Jamie Kyle, about this on bluesky to pick his brain: https://bsky.app/profile/brandonmcconnell.com/post/3lvd5xtacs22zType of Change
Please delete options that are not relevant.
Areas Affected
Testing Done
Checklist
I have added tests that prove my fix/feature works(none needed)Additional Notes
As mentioned above, I am still working through one remaining change, which is why this PR is in draft status.Removed from draft status, reviewing live on X
Screenshots/Recordings
The before & after screenshots I took were identical, so I'm only including the "after" screenshot here for reference.
By submitting this pull request, I confirm that my contribution is made under the terms of the project's license.
Summary by cubic
Upgraded the app to Tailwind CSS v4, moving all configuration to CSS, removing legacy config files and packages, and updating related dependencies and utilities for full compatibility.
@zero/tailwind-configpackage and all JS/TS Tailwind config files.@tailwindcss/viteplugin and removed PostCSS.Summary by CodeRabbit
New Features
Style
Chores
Documentation