Skip to content

feat: light mode#179

Merged
urjitc merged 4 commits intomainfrom
feat-light-mode
Feb 7, 2026
Merged

feat: light mode#179
urjitc merged 4 commits intomainfrom
feat-light-mode

Conversation

@urjitc
Copy link
Member

@urjitc urjitc commented Feb 7, 2026

Important

Introduces light mode with dynamic theming, updating styles and components for theme adaptability and visual consistency.

  • Behavior:
    • Introduces light mode with system-aware dynamic theming across the app, including editor, diagrams, and notifications.
    • Updates MermaidDiagram and BlockNoteEditor to adapt to active theme.
  • Style:
    • Refreshes visual elements with unified color tokens and improved dark-mode variants.
    • Theme-aware styling for scrollbars, modals, tooltips, badges, buttons, and cards in globals.css.
    • Adjusts bg-foreground and text-foreground classes for theme consistency.
  • Components:
    • Updates FlashcardContent, QuizContent, WorkspaceCard, and YouTubeCardContent to support theme changes.
    • Modifies TooltipContent in tooltip.tsx for theme-aware text color.
    • Ensures ImageCardContent and ItemHeader adapt to theme changes.
  • Misc:
    • Removes hardcoded dark mode in layout.tsx and global-error.tsx.
    • Adds useTheme hook to various components for theme resolution.

This description was created by Ellipsis for 9a32afb. You can customize this summary. It will automatically update as commits are pushed.


Summary by CodeRabbit

  • New Features

    • System-aware dynamic theming applied broadly (app UI, editor, diagrams, notifications, toasts)
  • Style

    • Large visual refresh: unified foreground-based color tokens, improved dark-mode variants, theme-aware scrollbars, modals, tooltips, badges, buttons, cards, and editor/preview visuals
    • Conditional dark-mode effects (e.g., ambient glow) and theme-aware image/upload toast styling
  • Bug Fixes

    • Reduced visual inconsistencies when switching or initializing themes

@vercel
Copy link
Contributor

vercel bot commented Feb 7, 2026

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

Project Deployment Actions Updated (UTC)
thinkex Ready Ready Preview, Comment Feb 7, 2026 7:27pm

@coderabbitai
Copy link

coderabbitai bot commented Feb 7, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds system-driven theming: wraps the app with next-themes, removes hard-coded root dark class and suppresses hydration warnings on root HTML, and converts many components and global CSS to theme-aware variables and conditional rendering (including BlockNote/Mermaid theme handling).

Changes

Cohort / File(s) Summary
Root & Layout
src/app/layout.tsx, src/app/global-error.tsx
Remove hard-coded dark root class and add suppressHydrationWarning on <html>.
Theme Provider & App bootstrap
src/components/providers.tsx, src/components/providers/AppProviders.tsx
Wrap app with ThemeProvider (next-themes) and set SonnerToaster theme to system.
Global CSS & Theming variables
src/app/globals.css
Large theme-aware styling sweep: introduce/extend CSS variables, add dark-mode overrides, and update editor/BlockNote, popovers, scrollbars, backdrops, and UI chrome to use theme tokens.
Editor / Preview / Diagrams
src/components/editor/BlockNoteEditor.tsx, src/components/editor/BlockNotePreview.tsx, src/components/assistant-ui/mermaid-diagram.tsx
Use useTheme/resolvedTheme to pick BlockNote and Mermaid themes dynamically; replace hard-coded colors in preview/toasts with theme-aware tokens.
Math / KaTeX styling
src/components/editor/blocks/math-block.css
Replace hard-coded white color values with var(--foreground) to align KaTeX/math rendering with theme tokens.
Home & Landing UI
src/components/home/*, src/components/landing/*
Multiple components updated for theme-aware colors: conditional dark-mode rendering (e.g., HeroGlow), typography tweak, and replacement of white/gray tokens with text-foreground / muted-foreground variants.
Workspace & Canvas UI
src/components/workspace-canvas/*, src/components/workspace/*
Wide styling sweep: cards, flashcards, quiz, item panel, selection bar, sources, and modals adopt foreground/muted tokens; several components add useTheme for conditional styles and adjust panel/backdrop variables.
Modals / Dialogs
src/components/modals/CardDetailModal.tsx, src/components/workspace/ShareWorkspaceDialog.tsx, src/components/workspace/WorkspaceSettingsModal.tsx
Replace hard-coded backdrop/background classes with CSS-variable-driven or muted backdrops; remove some backdrop-blur/shadow classes.
UI Primitives & Tooling
src/components/ui/*, src/components/tool-ui/progress-tracker/*
Update badge/button/tooltip/destructive variants and focus rings to use text-foreground and add explicit dark:text-white variants.
Small styling adjustments
src/components/home/DynamicTagline.tsx, src/components/workspace-canvas/FolderCard.tsx, src/components/workspace-canvas/ImageCardContent.tsx, ...
Minor typography and single-line class swaps to foreground/muted tokens across several files.

Sequence Diagram(s)

(Skipped — changes are primarily styling and theme propagation without new multi-party control flow requiring sequential visualization.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • feat: light mode #179 — Implements similar theming changes (removing root dark class, adding suppressHydrationWarning, and introducing ThemeProvider/useTheme across the same files).
  • minor quiz UI changes #175 — Modifies QuizContent.tsx; overlaps with theming/styling edits in this component.
  • Main pr jan20 #86 — Affects the BlockNote editor and global styles; likely to overlap with BlockNoteEditor and globals.css edits.

Poem

🐇 I hopped through classes, tokens in paw,
swapped hard white hues for variables that draw.
A ThemeProvider nest keeps day and night right,
mermaid and blocks now match dark or light.
A little hop — the UI’s snug and bright. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: light mode' directly describes the main objective of the PR: adding light mode support with theme-aware styling across the app.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-light-mode

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

❤️ Share

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

Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed everything up to 29dde44 in 21 seconds. Click for details.
  • Reviewed 2093 lines of code in 36 files
  • Skipped 1 files when reviewing.
  • Skipped posting 0 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.

Workflow ID: wflow_00zGvZERL9Ee0fGY

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.


export function HomePromptInput({ shouldFocus }: HomePromptInputProps) {
const router = useRouter();
const { resolvedTheme } = useTheme();
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 7, 2026

Greptile Overview

Greptile Summary

  • Adds light mode support by removing the globally-forced dark class and introducing next-themes ThemeProvider for system theme selection.
  • Updates global CSS variables and component-level classes to render correctly in both light and dark modes (BlockNote editor, scrollbars, toasts, workspace cards, etc.).
  • Adjusts a few UI components to read resolvedTheme for theme-dependent inline styling (e.g., WorkspaceCard, HeroGlow).

Confidence Score: 3/5

  • This PR is close to merge-ready but has a couple of concrete issues to fix before merging.
  • Most changes are styling/theme variable updates with limited behavioral impact, but there is one definite className composition bug in WorkspaceCard and a couple of hygiene issues (unused hook import and missing EOF newline) that can break builds in stricter CI setups.
  • src/components/workspace-canvas/WorkspaceCard.tsx, src/components/home/HomePromptInput.tsx, src/components/providers/AppProviders.tsx

Important Files Changed

Filename Overview
public/newlogothinkex.svg Updated SVG asset for new logo (no code-path impact).
src/app/global-error.tsx Removed forced dark class and added suppressHydrationWarning to support theme switching; no functional error-handling logic changes.
src/app/globals.css Added light/dark theme-aware CSS variables and component overrides (BlockNote, scrollbars, overlays) to support light mode.
src/app/layout.tsx Removed forced dark class and added suppressHydrationWarning on <html> for theme hydration consistency.
src/components/assistant-ui/mermaid-diagram.tsx Adjusted mermaid diagram styling for theme awareness (no logic changes observed).
src/components/editor/BlockNoteEditor.tsx Tweaked editor styles/classes for light mode support; no editor behavior changes.
src/components/editor/BlockNotePreview.tsx Adjusted preview styling for theme-aware colors; no rendering logic changes.
src/components/editor/blocks/math-block.css Updated math block CSS to be theme-aware; no functional JS changes.
src/components/home/DynamicTagline.tsx Minor class changes for light mode support; no logic change.
src/components/home/HeroGlow.tsx Conditionally renders hero glow only when resolvedTheme === 'dark' using next-themes.
src/components/home/HomeContent.tsx Adjusted home content classes to align with new theme variables.
src/components/home/HomePromptInput.tsx Theme-aware class tweaks; introduces unused useTheme/resolvedTheme hook that should be removed or used.
src/components/home/ParallaxBentoBackground.tsx Class adjustments for theme; no logic change.
src/components/home/WorkspaceGrid.tsx Theme-related class changes; no behavioral change.
src/components/landing/FloatingCard.tsx Updated floating card styling for light mode; no data/logic changes.
src/components/landing/FloatingWorkspaceCards.tsx Minor class tweaks for theme compatibility.
src/components/modals/CardDetailModal.tsx Theme-aware class updates for modal visuals; logic unchanged.
src/components/providers.tsx Updated Sonner toaster theme to system to match app theming.
src/components/providers/AppProviders.tsx Wrapped app with next-themes ThemeProvider; file currently missing trailing newline at EOF.
src/components/tool-ui/progress-tracker/progress-tracker.tsx Theme-related color/class updates; progress logic unchanged.
src/components/ui/badge.tsx Badge variant styling updated to use theme variables.
src/components/ui/button.tsx Button styling updated for light/dark theme variables; behavior unchanged.
src/components/ui/highlight-tooltip.tsx Tooltip highlight classes updated for theme.
src/components/ui/tooltip.tsx Tooltip styling adjusted to be theme-aware.
src/components/workspace-canvas/FlashcardContent.tsx Theme-aware text/background adjustments for flashcard content.
src/components/workspace-canvas/FlashcardWorkspaceCard.tsx Updated flashcard workspace card styling for light mode.
src/components/workspace-canvas/FolderCard.tsx Minor theme styling updates for folder card.
src/components/workspace-canvas/ImageCardContent.tsx Theme-aware class changes for image card content.
src/components/workspace-canvas/ItemHeader.tsx Item header classes updated for theme variables.
src/components/workspace-canvas/ItemPanelContent.tsx Item panel styling updated for light mode; logic unchanged.
src/components/workspace-canvas/QuizContent.tsx Quiz UI text colors updated for theme support; quiz logic unchanged.
src/components/workspace-canvas/SelectionActionBar.tsx Theme-aware class updates for selection action bar.
src/components/workspace-canvas/SourcesDisplay.tsx Theme-based color tweaks for sources display UI.
src/components/workspace-canvas/WorkspaceCard.tsx Adds next-themes resolvedTheme to compute colors; introduces incorrect cn() usage that injects 'Note/PDF/Quiz' as CSS class.
src/components/workspace-canvas/YouTubeCardContent.tsx Theme-aware styling adjustments for YouTube card content.
src/components/workspace/ShareWorkspaceDialog.tsx Updated share dialog styling for light/dark themes; behavior unchanged.
src/components/workspace/WorkspaceSettingsModal.tsx Theme-aware class changes in settings modal; logic unchanged.

Sequence Diagram

sequenceDiagram
  autonumber
  participant Browser
  participant NextApp as Next.js App (layout.tsx)
  participant AppProviders as AppProviders (ThemeProvider)
  participant nextThemes as next-themes
  participant UI as Theme-aware UI Components

  Browser->>NextApp: Request page / render SSR
  NextApp->>AppProviders: Wrap children
  AppProviders->>nextThemes: Provide ThemeProvider(attribute="class", defaultTheme="system")
  nextThemes-->>NextApp: SSR initial theme class (may differ from client)
  Browser->>Browser: Hydrate React on client
  nextThemes->>Browser: Resolve system theme (resolvedTheme)
  Browser->>UI: Render components using Tailwind theme classes
  UI->>Browser: Some components read resolvedTheme (useTheme)
  Browser-->>UI: Update DOM class on <html> (dark/light)
  UI-->>Browser: Re-render with correct theme styles
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

37 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 1 to +4
'use client';

import { type PropsWithChildren } from "react";
import { ThemeProvider } from "next-themes";
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing trailing newline

This file is missing a final newline (\ No newline at end of file). Some tooling (formatters, POSIX text processing, certain linters) will flag this; add a newline at EOF.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 7, 2026

Additional Comments (2)

src/components/workspace-canvas/WorkspaceCard.tsx
Broken className composition

This className is using cn() with a first argument that is the label text ('Note' | 'PDF' | 'Quiz'). That string will be emitted as a CSS class (invalid / unintended) and can also break styling depending on Tailwind compilation. The text is already rendered inside the <span>.

<span
  className={cn(
    "text-[10px] uppercase tracking-wider mt-auto",
    resolvedTheme === 'dark' ? "text-muted-foreground/60" : "text-muted-foreground/40"
  )}
>

src/components/home/HomePromptInput.tsx
Unused theme hook import

useTheme / resolvedTheme is imported/initialized but never used in this component, which will fail linting in strict setups (and is dead code). Either remove the hook, or actually use resolvedTheme for theme-specific styling.

import { useState, useRef, useMemo, useEffect } from "react";
import { useRouter } from "next/navigation";

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (11)
src/components/workspace-canvas/SelectionActionBar.tsx (1)

26-32: ⚠️ Potential issue | 🟡 Minor

Container background and border still use hardcoded white — likely invisible in light mode.

The child text/icon/separator classes were updated to foreground tokens, but the container itself still uses bg-white/5 and border-white/10. On a light background this bar will be nearly invisible.

Proposed fix
-        "bg-white/5 border border-white/10",
+        "bg-foreground/5 border border-foreground/10",
+        "dark:bg-white/5 dark:border-white/10",
src/components/workspace-canvas/FlashcardContent.tsx (1)

155-161: ⚠️ Potential issue | 🟠 Major

Same light-mode contrast concern on the delete button.

bg-black/60 paired with text-foreground/80 will have poor contrast in light mode for the same reason as the badge. Use text-white/80 hover:text-white unconditionally since the background is always dark, or switch the background to a theme-aware token.

Proposed fix
-                                        className="p-2 rounded-full bg-black/60 text-foreground/80 hover:text-foreground hover:bg-red-500/80 transition-colors shadow-md cursor-pointer dark:text-white/80 dark:hover:text-white"
+                                        className="p-2 rounded-full bg-black/60 text-white/80 hover:text-white hover:bg-red-500/80 transition-colors shadow-md cursor-pointer"
src/components/workspace-canvas/FolderCard.tsx (1)

242-242: ⚠️ Potential issue | 🟡 Minor

Selected border color uses hard-coded white — invisible in light mode.

rgba(255, 255, 255, 0.8) will be nearly invisible against a light background when the folder is selected. Consider using a theme-aware token or a higher-contrast color derived from the folder's accent color.

-    const borderColor = isSelected ? 'rgba(255, 255, 255, 0.8)' : getCardAccentColor(folderColor, 0.5);
+    const borderColor = isSelected ? 'rgba(0, 0, 0, 0.8)' : getCardAccentColor(folderColor, 0.5);

A more robust fix would use a CSS variable or conditionally pick the color based on the resolved theme.

src/components/tool-ui/progress-tracker/progress-tracker.tsx (1)

64-78: ⚠️ Potential issue | 🟡 Minor

Potential contrast issue: text-foreground on bg-destructive.

In light mode, bg-destructive is typically a saturated red. Using text-foreground (near-black) instead of white may reduce readability of the X icon. White on red is the conventional high-contrast pairing. Consider using text-destructive-foreground which is designed to pair with bg-destructive:

-        className="bg-destructive dark:bg-red-600 text-foreground flex size-6 shrink-0 items-center justify-center rounded-full border border-destructive dark:border-red-600 shadow-sm motion-safe:animate-[spring-bounce_500ms_cubic-bezier(0.34,1.56,0.64,1)] dark:text-white"
+        className="bg-destructive text-destructive-foreground flex size-6 shrink-0 items-center justify-center rounded-full border border-destructive shadow-sm motion-safe:animate-[spring-bounce_500ms_cubic-bezier(0.34,1.56,0.64,1)]"

text-destructive-foreground is already used elsewhere (e.g., the delete button on line 532) and is the semantic token for text on destructive backgrounds.

src/components/ui/highlight-tooltip.tsx (1)

599-619: ⚠️ Potential issue | 🟡 Minor

Arrow color fallback and color map are hard-coded dark-theme RGB values.

The default arrow color "rgb(48, 49, 52)" (line 599) corresponds to a dark card background, which won't match bg-card in light mode. Similarly, the colorMap values are static RGB. Consider reading the computed card background color from the DOM or CSS variable, or using a CSS variable for the arrow fallback:

// e.g., read from CSS variable at render time
const cardColor = getComputedStyle(document.documentElement).getPropertyValue('--card').trim();

This is a pre-existing concern not fully addressed by this PR's theming changes.

src/components/editor/blocks/math-block.css (1)

3-25: ⚠️ Potential issue | 🟡 Minor

Wrapper and scrollbar styles remain hard-coded for dark backgrounds.

The rgba(255, 255, 255, ...) values for .math-block-wrapper background/border (lines 6-7, 18-19, 22-24) and scrollbar (lines 71-81) will be effectively invisible on a light background. Consider using CSS variables or color-mix() for these as well to complete the theming in this file.

src/components/workspace-canvas/QuizContent.tsx (1)

243-257: ⚠️ Potential issue | 🟡 Minor

Skeleton and option styles still use hard-coded dark-theme colors.

Several classes like bg-white/5, border-white/10, bg-white/10, border-white/20, and text-white/60 (e.g., lines 246, 253, 263, 372-376, 386) are not theme-aware and will appear invisible or washed out in light mode. Consider migrating these to CSS variable-based equivalents (e.g., bg-foreground/5, border-foreground/10) or adding dark: variants.

src/components/home/WorkspaceGrid.tsx (1)

270-272: ⚠️ Potential issue | 🟡 Minor

Hardcoded 'white' hover border color breaks in light mode.

Line 271 sets borderColor to 'white' on hover, which will be invisible against a light background. This directly conflicts with the light mode goal of this PR. Consider using a theme-aware color.

Proposed fix
                 onMouseEnter={(e) => {
                   // Debounce prefetching to prevent spam when sweeping across grid
                   prefetchTimeoutRef.current = setTimeout(() => {
                     router.prefetch(`/workspace/${workspace.slug || workspace.id}`);
                   }, 100); // 100ms delay

                   if (!selectedIds.has(workspace.id)) {
-                    e.currentTarget.style.borderColor = 'white';
+                    e.currentTarget.style.borderColor = 'hsl(var(--foreground))';
                   }
                 }}
src/components/workspace-canvas/FlashcardWorkspaceCard.tsx (2)

116-131: ⚠️ Potential issue | 🟠 Major

Hardcoded color: 'white' in FlashcardSideContent breaks light mode.

Line 118 sets color: 'white' as an inline style for the flashcard content area. This will render white text on a potentially light background in light mode. This should be theme-aware.


418-418: ⚠️ Potential issue | 🟡 Minor

Selected border color hardcoded to white.

'rgba(255, 255, 255, 0.8)' for the selected state border will be invisible in light mode. Consider using a theme-aware value, e.g., hsl(var(--foreground) / 0.8) or a distinct selection color.

src/app/globals.css (1)

1332-1365: 🛠️ Refactor suggestion | 🟠 Major

Duplicated @keyframes gradient-rotate and .gradient-border-animated.

These are already defined at lines 592-624. The second copy (lines 1332-1365) is an exact duplicate and should be removed.

♻️ Remove duplicated keyframes and class
-/* Animated gradient border - rotates colors around the border in a circle */
-@keyframes gradient-rotate {
-  0% {
-    background-position: 0% 0%;
-  }
-
-  25% {
-    background-position: 100% 0%;
-  }
-
-  50% {
-    background-position: 100% 100%;
-  }
-
-  75% {
-    background-position: 0% 100%;
-  }
-
-  100% {
-    background-position: 0% 0%;
-  }
-}
-
-.gradient-border-animated {
-  background: linear-gradient(90deg,
-      `#ef444480`,
-      `#eab30880`,
-      `#22c55e80`,
-      `#3b82f680`,
-      `#a855f780`,
-      `#ef444480`);
-  background-size: 200% 200%;
-  animation: gradient-rotate 10s ease-in-out infinite;
-}
🤖 Fix all issues with AI agents
In `@src/components/editor/BlockNoteEditor.tsx`:
- Line 350: Move the useTheme() hook call to the top of the BlockNoteEditor
component alongside the other React hooks (so call const { resolvedTheme } =
useTheme(); together with useState/useEffect/useRef declarations) and remove the
duplicate/late declaration currently at line 350; ensure the variable name
resolvedTheme is preserved and all references in the component now use the
top-level binding.

In `@src/components/home/HomePromptInput.tsx`:
- Line 4: Remove the unused theme hook and destructured variable: delete the
import of useTheme from "next-themes" and remove resolvedTheme from the
destructuring inside the HomePromptInput component so neither useTheme nor
resolvedTheme remain referenced; update any related const/let that currently
does "const { resolvedTheme, ..." (or similar) to only extract the used values
(or remove the entire destructuring) to eliminate the dead code.

In `@src/components/home/ParallaxBentoBackground.tsx`:
- Around line 245-247: The Play icon inside ParallaxBentoBackground is using
theme-dependent classes (text-foreground fill-foreground and dark: variants)
which renders near-black in light mode; change the Play element to use explicit
white coloring (e.g., text-white and fill-white) so the triangle is always white
on the red bg, and remove the theme-based classes (dark:text-white,
dark:fill-white) to ensure consistent appearance.
- Around line 267-269: The span showing the PDF filename in
ParallaxBentoBackground.tsx uses "text-foreground/50 truncate
dark:text-white/50", which produces low-contrast text against the hardcoded dark
header (bg-[`#525659`]); change that span's class to a white-based color such as
"text-white/50 truncate" (or "text-white/50 truncate dark:text-white/50" if you
want explicit dark-mode parity) so the label remains light against the dark PDF
header; update the span in the same component where the div with class "h-8
bg-[`#525659`] flex items-center px-3 gap-2" is defined.

In `@src/components/landing/FloatingWorkspaceCards.tsx`:
- Line 82: FloatingWorkspaceCards currently reads useTheme().resolvedTheme on
render which is undefined during SSR and causes a hydration flash; add a local
mounted state (e.g., const [mounted, setMounted] = useState(false) and
setMounted(true) in useEffect) and use mounted to avoid using resolvedTheme
until client mount — either suppress or render a neutral state (or apply
"opacity-0" as the initial class) instead of the theme-dependent ternary that
produces "opacity-50"/"opacity-30" (the ternary around the opacity classes
referenced on the element near the existing lines 123-124); update any other
spots using resolvedTheme in this component to guard on mounted before choosing
theme-specific classes.

In `@src/components/ui/button.tsx`:
- Around line 13-14: The destructive button variant currently uses
"text-foreground" which fails WCAG contrast; update the destructive variant
string in the Button component (the "destructive:" class value) to use the
foreground token for destructive states (e.g., "text-destructive-foreground") or
"text-white" for light mode instead of "text-foreground", while keeping the
existing dark-mode class ("dark:text-white") and other destructive classes
intact; modify the destructive entry in the variant mapping (the destructive key
in src/components/ui/button.tsx) so the light-mode text color matches the
defined --destructive-foreground token and passes contrast requirements.

In `@src/components/workspace-canvas/FlashcardContent.tsx`:
- Line 148: In FlashcardContent.tsx the badge divs use bg-black/70 or
bg-black/80 combined with text-foreground which resolves to a dark color in
light mode, making the text unreadable; update those badge classNames (the div
with "bg-black/70 text-foreground" and the other two occurrences with
"bg-black/80 text-foreground") to use an unconditional light text color (e.g.,
replace text-foreground with text-white or add text-white alongside it) so the
badge text remains visible in both light and dark themes; ensure you update all
three occurrences inside the FlashcardContent component.
- Line 144: The Tailwind class "bg-foreground/5/50" in the FlashcardContent
component is invalid (double opacity); update the className on the element in
FlashcardContent.tsx to use a single opacity token such as "bg-foreground/5" (to
match the adjacent "border-foreground/10") so Tailwind applies the intended 5%
background opacity; locate the offending string in the JSX className for the
element rendered by the FlashcardContent component and replace the
double-opacity token with the single-token version.

In `@src/components/workspace-canvas/FlashcardWorkspaceCard.tsx`:
- Around line 440-443: In FlashcardWorkspaceCard, update the control buttons so
hover background and text colors adapt to resolvedTheme instead of hardcoded
dark values: compute theme-aware colors (e.g., hoverBg and defaultBg based on
resolvedTheme) and use those in the style prop and in the
onMouseEnter/onMouseLeave handlers, and replace fixed classes like
"text-white/90 hover:text-white" with conditional classNames or computed
textColor variables that switch between dark-mode light text and light-mode dark
text; apply the same change to the other control buttons referenced in the
component (the repeating blocks around the other control buttons).

In `@src/components/workspace-canvas/ImageCardContent.tsx`:
- Line 29: The caption overlay in ImageCardContent uses a dark semi-transparent
background ("bg-black/60") but the text class is "text-foreground", which
becomes dark in light mode and reduces readability; update the overlay div in
ImageCardContent to use a consistent light text color (replace "text-foreground"
with "text-white") so captions remain readable regardless of theme, or
alternatively make both the background and text theme-aware (e.g., conditional
classes) if you prefer theme parity.

In `@src/components/workspace-canvas/ItemHeader.tsx`:
- Around line 225-229: The inline style color: 'inherit' in the style prop of
the ItemHeader component overrides Tailwind text color classes (e.g.,
text-foreground and focus:text-accent); remove the color: 'inherit' entry from
the style object so the className string (including textSizeClass,
fontWeightClass, text-foreground, and focus:text-accent) can control colors and
focus state as intended — update the style prop used where className is built
(referencing textSizeClass and fontWeightClass) to omit color while keeping
other style entries like width.

In `@src/components/workspace-canvas/ItemPanelContent.tsx`:
- Around line 121-127: Remove the inline backgroundColor style so dark-mode CSS
can take precedence: instead of forcing backgroundColor: 'var(--note-bg-light)'
in the ItemPanelColor/style object, only set the CSS variables --note-bg-light
and --note-bg-dark (currently assigned using getCardColorCSS(item.color, ...))
based on item.color, and let the existing .note-panel-background / .dark
.note-panel-background rules pick the appropriate variable; locate the inline
backgroundColor assignment in ItemPanelContent (the object setting
['--note-bg-light'], ['--note-bg-dark'], backgroundColor) and delete the
backgroundColor line.

In `@src/components/workspace-canvas/QuizContent.tsx`:
- Around line 350-351: Replace the unconditional "prose-invert" Tailwind class
with the dark-mode prefixed "dark:prose-invert" on the StreamdownMarkdown
wrappers and their surrounding divs (e.g., the div and StreamdownMarkdown
instances around lines where StreamdownMarkdown is rendered) so the inverted
typography only applies in dark mode; update all occurrences (the
StreamdownMarkdown component usages and their parent divs) to use
"dark:prose-invert" instead of "prose-invert" to restore correct light-mode
readability.

In `@src/components/workspace-canvas/WorkspaceCard.tsx`:
- Around line 593-594: The selected-card border uses a hardcoded white color in
WorkspaceCard (borderColor ternary when isSelected) which is invisible in light
mode; change the isSelected branch to use a theme-aware value instead — e.g.,
call getCardAccentColor(item.color, resolvedTheme === 'dark' ? 0.5 : 0.3) (or
fall back to a CSS variable like --card-accent) so selection respects
resolvedTheme and item.color rather than always using 'rgba(255, 255, 255,
0.8)'.
- Around line 800-803: The code is passing display text ('Note'/'PDF'/'Quiz') as
the first argument to the cn() call in WorkspaceCard (using item.type, cn,
shouldShowPreview, resolvedTheme); change that first argument to a real CSS
class string (or a mapping from item.type to class names) and keep the visible
label text separate. Concretely, replace the ternary used as a class name with a
proper class (e.g. item.type === 'note' ? 'note-badge' : item.type === 'pdf' ?
'pdf-badge' : 'quiz-badge') and leave the span children as the human-readable
text (Item label), so the className only contains CSS class identifiers and the
inner text remains 'Note'/'PDF'/'Quiz'.

In `@src/components/workspace-canvas/YouTubeCardContent.tsx`:
- Around line 110-113: The Play icon's color classes are using theme-dependent
utilities (text-foreground / fill-foreground) which reduce contrast against the
fixed bg-red-600; update the Play element inside YouTubeCardContent (the Play
component instance in the red circular button and the fallback play button) to
use explicit white colors (text-white and fill-white) and remove the dark:...
variants so the icon remains white regardless of theme, preserving sufficient
contrast against the red background.
- Around line 96-108: The badge's icon and label use text-foreground which
becomes dark in light mode and is unreadable on the always-dark bg
(bg-black/80); update the className for the List and Video icons and the two
span labels inside the conditional in YouTubeCardContent.tsx so they use a
persistent white color (e.g., replace "text-foreground dark:text-white" with
"text-white" or "text-white dark:text-white") so the badge remains readable
regardless of theme.
🧹 Nitpick comments (8)
src/components/home/WorkspaceGrid.tsx (1)

83-100: Dead code: handleBulkDelete is unused and contains a Rules of Hooks violation.

This function calls useWorkspaceContext() (line 89) inside a regular callback, which violates React's Rules of Hooks. It also duplicates the deleteWorkspace already destructured at line 30. The actual bulk delete logic is implemented inline at lines 398-417. This entire function appears to be leftover scaffolding and should be removed.

src/components/landing/FloatingCard.tsx (2)

75-76: Consider extracting repeated theme-conditional class logic.

The pattern resolvedTheme === 'dark' ? "text-muted-foreground" : "text-foreground/60" (and variants) appears ~10 times across card types. A small helper or mapping object would reduce duplication and make future theme adjustments easier.

const themeText = resolvedTheme === 'dark' ? 'text-muted-foreground' : 'text-foreground/60';
const themeTextDim = resolvedTheme === 'dark' ? 'text-muted-foreground/70' : 'text-foreground/40';

Also applies to: 100-101, 104-104, 120-121, 124-124, 168-168, 175-179


276-278: Mixed theming approaches: JS resolvedTheme checks vs Tailwind dark: prefix.

Line 271 uses resolvedTheme === 'dark' for conditional classes, but line 278 uses Tailwind's dark:text-white dark:fill-white. Both work with class-based theming from next-themes, but mixing them in the same component is inconsistent. Pick one approach for clarity.

src/components/workspace-canvas/WorkspaceCard.tsx (2)

611-637: Heavily duplicated theme-conditional background logic across button elements.

The pattern resolvedTheme === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.2)' (and its hover variant) is repeated across the scroll lock button (lines 613, 621, 624), selection button (lines 649, 659, 664), and options menu button (lines 687, 695, 698). Consider extracting helpers:

const overlayBg = resolvedTheme === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.2)';
const overlayBgHover = resolvedTheme === 'dark' ? 'rgba(0, 0, 0, 0.5)' : 'rgba(0, 0, 0, 0.3)';
const pdfOverlayBg = 'rgba(0, 0, 0, 0.6)';
const pdfOverlayBgHover = 'rgba(0, 0, 0, 0.8)';

This would reduce ~18 ternary expressions to simple variable references and make future tweaks easier.


636-636: Indentation is inconsistent.

Line 636 has significantly deeper indentation than the surrounding JSX. Looks like an accidental whitespace issue.

src/components/workspace/ShareWorkspaceDialog.tsx (1)

409-414: Redundant backdrop-filter: class and inline style both set blur.

backdrop-blur-2xl (Tailwind class on line 410) and the inline backdropFilter: "blur(24px)" (lines 412-413) both set the same property. The inline style will take precedence. Consider removing one for clarity — either rely on the Tailwind class or the inline style, not both.

src/components/workspace/WorkspaceSettingsModal.tsx (1)

346-348: Empty className="" is unnecessary.

This is a leftover from removing the previous styling. Remove the attribute entirely.

♻️ Proposed fix
       <AlertDialogContent
-          className=""
       >
src/components/workspace-canvas/ItemPanelContent.tsx (1)

52-54: Remove unused backgroundColor variable.

The backgroundColor const (lines 52-54) is computed but never used in the component. The CSS-variable approach via --note-bg-light / --note-bg-dark is applied directly in the style object with hardcoded values, making this variable dead code from the previous implementation.


// Renders the editor instance using a React component
const isEditable = readOnly !== undefined ? !readOnly : true;
const { resolvedTheme } = useTheme();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Move useTheme() call to the top of the component with other hooks.

While this doesn't violate Rules of Hooks (no conditional returns precede it), placing a hook call at line 350 — deep in the component body after callbacks, effects, and derived values — is unconventional and fragile. If someone adds an early return above this line in the future, it would break.

Suggested move
 export default function BlockNoteEditor({ initialContent, onChange, readOnly, cardName, cardId, lastSource, autofocus }: BlockNoteEditorProps) {
   const isInitialMount = useRef(true);
   const previousInitialContent = useRef<string | undefined>(undefined);
   const posthog = usePostHog();
   const initStartTime = useRef<number | null>(null);
+  const { resolvedTheme } = useTheme();

   // Get current workspace ID

And remove line 350.

🤖 Prompt for AI Agents
In `@src/components/editor/BlockNoteEditor.tsx` at line 350, Move the useTheme()
hook call to the top of the BlockNoteEditor component alongside the other React
hooks (so call const { resolvedTheme } = useTheme(); together with
useState/useEffect/useRef declarations) and remove the duplicate/late
declaration currently at line 350; ensure the variable name resolvedTheme is
preserved and all references in the component now use the top-level binding.

"use client";

import { useState, useRef, useMemo, useEffect } from "react";
import { useTheme } from "next-themes";
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Unused import: useTheme and resolvedTheme are imported but never consumed.

resolvedTheme (line 76) is destructured but not referenced anywhere in the component. Remove the import and the destructuring to avoid dead code.

♻️ Proposed fix
-import { useTheme } from "next-themes";
-  const { resolvedTheme } = useTheme();

Also applies to: 76-76

🤖 Prompt for AI Agents
In `@src/components/home/HomePromptInput.tsx` at line 4, Remove the unused theme
hook and destructured variable: delete the import of useTheme from "next-themes"
and remove resolvedTheme from the destructuring inside the HomePromptInput
component so neither useTheme nor resolvedTheme remain referenced; update any
related const/let that currently does "const { resolvedTheme, ..." (or similar)
to only extract the used values (or remove the entire destructuring) to
eliminate the dead code.

Comment on lines 245 to 247
<div className="w-12 h-12 rounded-full bg-red-600 flex items-center justify-center shadow-lg">
<Play className="h-5 w-5 text-white fill-white ml-0.5" />
<Play className="h-5 w-5 text-foreground fill-foreground ml-0.5 dark:text-white dark:fill-white" />
</div>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Play icon will appear dark on the red circle in light mode.

The parent is a bg-red-600 circle inside a bg-black container — the play triangle should always be white regardless of theme to match the YouTube visual. Using text-foreground fill-foreground makes the icon near-black in light mode, which looks off.

Suggested fix
-              <Play className="h-5 w-5 text-foreground fill-foreground ml-0.5 dark:text-white dark:fill-white" />
+              <Play className="h-5 w-5 text-white fill-white ml-0.5" />
📝 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.

Suggested change
<div className="w-12 h-12 rounded-full bg-red-600 flex items-center justify-center shadow-lg">
<Play className="h-5 w-5 text-white fill-white ml-0.5" />
<Play className="h-5 w-5 text-foreground fill-foreground ml-0.5 dark:text-white dark:fill-white" />
</div>
<div className="w-12 h-12 rounded-full bg-red-600 flex items-center justify-center shadow-lg">
<Play className="h-5 w-5 text-white fill-white ml-0.5" />
</div>
🤖 Prompt for AI Agents
In `@src/components/home/ParallaxBentoBackground.tsx` around lines 245 - 247, The
Play icon inside ParallaxBentoBackground is using theme-dependent classes
(text-foreground fill-foreground and dark: variants) which renders near-black in
light mode; change the Play element to use explicit white coloring (e.g.,
text-white and fill-white) so the triangle is always white on the red bg, and
remove the theme-based classes (dark:text-white, dark:fill-white) to ensure
consistent appearance.

Comment on lines 267 to +269
<div className="h-8 bg-[#525659] flex items-center px-3 gap-2">
<div className="w-3 h-3 rounded-full bg-red-500/60" />
<span className="text-xs text-white/50 truncate">Document.pdf</span>
<span className="text-xs text-foreground/50 truncate dark:text-white/50">Document.pdf</span>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same theme mismatch: dark text on a hardcoded dark-gray header.

The PDF header background is hardcoded bg-[#525659], so text-foreground/50 yields low-contrast dark-on-dark text in light mode. Since the PDF preview uses fixed dark colors throughout (lines 273-280 all use white/* classes), the label should stay white-based too.

Suggested fix
-            <span className="text-xs text-foreground/50 truncate dark:text-white/50">Document.pdf</span>
+            <span className="text-xs text-white/50 truncate">Document.pdf</span>
📝 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.

Suggested change
<div className="h-8 bg-[#525659] flex items-center px-3 gap-2">
<div className="w-3 h-3 rounded-full bg-red-500/60" />
<span className="text-xs text-white/50 truncate">Document.pdf</span>
<span className="text-xs text-foreground/50 truncate dark:text-white/50">Document.pdf</span>
<div className="h-8 bg-[`#525659`] flex items-center px-3 gap-2">
<div className="w-3 h-3 rounded-full bg-red-500/60" />
<span className="text-xs text-white/50 truncate">Document.pdf</span>
🤖 Prompt for AI Agents
In `@src/components/home/ParallaxBentoBackground.tsx` around lines 267 - 269, The
span showing the PDF filename in ParallaxBentoBackground.tsx uses
"text-foreground/50 truncate dark:text-white/50", which produces low-contrast
text against the hardcoded dark header (bg-[`#525659`]); change that span's class
to a white-based color such as "text-white/50 truncate" (or "text-white/50
truncate dark:text-white/50" if you want explicit dark-mode parity) so the label
remains light against the dark PDF header; update the span in the same component
where the div with class "h-8 bg-[`#525659`] flex items-center px-3 gap-2" is
defined.

className,
includeExtraCards = false,
}: FloatingWorkspaceCardsProps) {
const { resolvedTheme } = useTheme();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

resolvedTheme is undefined during SSR — potential hydration mismatch and flash.

useTheme().resolvedTheme is undefined on the server and until the component mounts on the client. The ternary on line 124 will always evaluate to "opacity-50" server-side, then snap to "opacity-30" for dark-mode users after hydration, causing a brief flash.

A common pattern with next-themes is to track a mounted state and suppress rendering (or use a neutral default) until the theme is known:

Proposed fix
-import { useRef, useState, useEffect } from "react";
+import { useRef, useState, useEffect, useMemo } from "react";
 import { useTheme } from "next-themes";

 ...

 export function FloatingWorkspaceCards({ ... }) {
     const { resolvedTheme } = useTheme();
+    const [mounted, setMounted] = useState(false);
+    useEffect(() => setMounted(true), []);
     ...

             <div
                 className={cn(
                     "absolute inset-0 w-[120%] -ml-[10%] -mt-[5%] columns-2 md:columns-3 lg:columns-6 gap-4 md:gap-6 lg:gap-8 transition-transform duration-800 ease-out pointer-events-none",
-                    resolvedTheme === 'dark' ? "opacity-30" : "opacity-50",
+                    !mounted ? "opacity-0" : resolvedTheme === 'dark' ? "opacity-30" : "opacity-50",
                     className
                 )}

Starting at opacity-0 avoids the flash entirely; the transition-* classes already on the element will animate the opacity in smoothly once the theme is resolved.

Also applies to: 123-124

🤖 Prompt for AI Agents
In `@src/components/landing/FloatingWorkspaceCards.tsx` at line 82,
FloatingWorkspaceCards currently reads useTheme().resolvedTheme on render which
is undefined during SSR and causes a hydration flash; add a local mounted state
(e.g., const [mounted, setMounted] = useState(false) and setMounted(true) in
useEffect) and use mounted to avoid using resolvedTheme until client mount —
either suppress or render a neutral state (or apply "opacity-0" as the initial
class) instead of the theme-dependent ternary that produces
"opacity-50"/"opacity-30" (the ternary around the opacity classes referenced on
the element near the existing lines 123-124); update any other spots using
resolvedTheme in this component to guard on mounted before choosing
theme-specific classes.

Comment on lines +593 to +594
backgroundColor: (item.type === 'youtube' || item.type === 'image') ? 'transparent' : (item.color ? getCardColorCSS(item.color, resolvedTheme === 'dark' ? 0.25 : 0.4) : 'var(--card)'),
borderColor: isSelected ? 'rgba(255, 255, 255, 0.8)' : (item.color ? getCardAccentColor(item.color, resolvedTheme === 'dark' ? 0.5 : 0.3) : 'transparent'),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Selected card border is hardcoded white — invisible in light mode.

Line 594: isSelected ? 'rgba(255, 255, 255, 0.8)' will produce a white border on a light background, making the selection indicator invisible. This should use a theme-aware value.

🐛 Proposed fix
-              borderColor: isSelected ? 'rgba(255, 255, 255, 0.8)' : (item.color ? getCardAccentColor(item.color, resolvedTheme === 'dark' ? 0.5 : 0.3) : 'transparent'),
+              borderColor: isSelected ? (resolvedTheme === 'dark' ? 'rgba(255, 255, 255, 0.8)' : 'rgba(0, 0, 0, 0.6)') : (item.color ? getCardAccentColor(item.color, resolvedTheme === 'dark' ? 0.5 : 0.3) : 'transparent'),
📝 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.

Suggested change
backgroundColor: (item.type === 'youtube' || item.type === 'image') ? 'transparent' : (item.color ? getCardColorCSS(item.color, resolvedTheme === 'dark' ? 0.25 : 0.4) : 'var(--card)'),
borderColor: isSelected ? 'rgba(255, 255, 255, 0.8)' : (item.color ? getCardAccentColor(item.color, resolvedTheme === 'dark' ? 0.5 : 0.3) : 'transparent'),
backgroundColor: (item.type === 'youtube' || item.type === 'image') ? 'transparent' : (item.color ? getCardColorCSS(item.color, resolvedTheme === 'dark' ? 0.25 : 0.4) : 'var(--card)'),
borderColor: isSelected ? (resolvedTheme === 'dark' ? 'rgba(255, 255, 255, 0.8)' : 'rgba(0, 0, 0, 0.6)') : (item.color ? getCardAccentColor(item.color, resolvedTheme === 'dark' ? 0.5 : 0.3) : 'transparent'),
🤖 Prompt for AI Agents
In `@src/components/workspace-canvas/WorkspaceCard.tsx` around lines 593 - 594,
The selected-card border uses a hardcoded white color in WorkspaceCard
(borderColor ternary when isSelected) which is invisible in light mode; change
the isSelected branch to use a theme-aware value instead — e.g., call
getCardAccentColor(item.color, resolvedTheme === 'dark' ? 0.5 : 0.3) (or fall
back to a CSS variable like --card-accent) so selection respects resolvedTheme
and item.color rather than always using 'rgba(255, 255, 255, 0.8)'.

Comment on lines 800 to 803
{(item.type === 'note' || item.type === 'pdf' || item.type === 'quiz') && !shouldShowPreview && (
<span className="text-[10px] uppercase tracking-wider text-white/40 mt-auto">
<span className={cn(item.type === 'note' ? 'Note' : item.type === 'pdf' ? 'PDF' : 'Quiz', "text-[10px] uppercase tracking-wider mt-auto", resolvedTheme === 'dark' ? "text-muted-foreground/60" : "text-muted-foreground/40")}>
{item.type === 'note' ? 'Note' : item.type === 'pdf' ? 'PDF' : 'Quiz'}
</span>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Display text passed as CSS class name in cn().

The first argument to cn() evaluates to 'Note', 'PDF', or 'Quiz' — these are content strings, not CSS class names. They'll be harmlessly applied as meaningless class names, but it's clearly unintentional.

🐛 Proposed fix
-                <span className={cn(item.type === 'note' ? 'Note' : item.type === 'pdf' ? 'PDF' : 'Quiz', "text-[10px] uppercase tracking-wider mt-auto", resolvedTheme === 'dark' ? "text-muted-foreground/60" : "text-muted-foreground/40")}>
+                <span className={cn("text-[10px] uppercase tracking-wider mt-auto", resolvedTheme === 'dark' ? "text-muted-foreground/60" : "text-muted-foreground/40")}>
📝 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.

Suggested change
{(item.type === 'note' || item.type === 'pdf' || item.type === 'quiz') && !shouldShowPreview && (
<span className="text-[10px] uppercase tracking-wider text-white/40 mt-auto">
<span className={cn(item.type === 'note' ? 'Note' : item.type === 'pdf' ? 'PDF' : 'Quiz', "text-[10px] uppercase tracking-wider mt-auto", resolvedTheme === 'dark' ? "text-muted-foreground/60" : "text-muted-foreground/40")}>
{item.type === 'note' ? 'Note' : item.type === 'pdf' ? 'PDF' : 'Quiz'}
</span>
{(item.type === 'note' || item.type === 'pdf' || item.type === 'quiz') && !shouldShowPreview && (
<span className={cn("text-[10px] uppercase tracking-wider mt-auto", resolvedTheme === 'dark' ? "text-muted-foreground/60" : "text-muted-foreground/40")}>
{item.type === 'note' ? 'Note' : item.type === 'pdf' ? 'PDF' : 'Quiz'}
</span>
🤖 Prompt for AI Agents
In `@src/components/workspace-canvas/WorkspaceCard.tsx` around lines 800 - 803,
The code is passing display text ('Note'/'PDF'/'Quiz') as the first argument to
the cn() call in WorkspaceCard (using item.type, cn, shouldShowPreview,
resolvedTheme); change that first argument to a real CSS class string (or a
mapping from item.type to class names) and keep the visible label text separate.
Concretely, replace the ternary used as a class name with a proper class (e.g.
item.type === 'note' ? 'note-badge' : item.type === 'pdf' ? 'pdf-badge' :
'quiz-badge') and leave the span children as the human-readable text (Item
label), so the className only contains CSS class identifiers and the inner text
remains 'Note'/'PDF'/'Quiz'.

Comment on lines 96 to 108
<div className="absolute bottom-2 right-2 flex items-center gap-1 px-2 py-1 rounded-md bg-black/80 backdrop-blur-sm">
{isPlaylist ? (
<>
<List className="h-3 w-3 text-white" />
<span className="text-xs font-medium text-white">Playlist</span>
<List className="h-3 w-3 text-foreground dark:text-white" />
<span className="text-xs font-medium text-foreground dark:text-white">Playlist</span>
</>
) : (
<>
<Video className="h-3 w-3 text-white" />
<span className="text-xs font-medium text-white">Video</span>
<Video className="h-3 w-3 text-foreground dark:text-white" />
<span className="text-xs font-medium text-foreground dark:text-white">Video</span>
</>
)}
</div>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

text-foreground on bg-black/80 will be unreadable in light mode.

The badge container has a bg-black/80 background. In light mode, text-foreground resolves to a dark color (e.g., near-black), making the text invisible. These should remain text-white (or use dark:text-white text-white) since the background is always dark regardless of theme.

Proposed fix
-                    <List className="h-3 w-3 text-foreground dark:text-white" />
-                    <span className="text-xs font-medium text-foreground dark:text-white">Playlist</span>
+                    <List className="h-3 w-3 text-white" />
+                    <span className="text-xs font-medium text-white">Playlist</span>
-                    <Video className="h-3 w-3 text-foreground dark:text-white" />
-                    <span className="text-xs font-medium text-foreground dark:text-white">Video</span>
+                    <Video className="h-3 w-3 text-white" />
+                    <span className="text-xs font-medium text-white">Video</span>
📝 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.

Suggested change
<div className="absolute bottom-2 right-2 flex items-center gap-1 px-2 py-1 rounded-md bg-black/80 backdrop-blur-sm">
{isPlaylist ? (
<>
<List className="h-3 w-3 text-white" />
<span className="text-xs font-medium text-white">Playlist</span>
<List className="h-3 w-3 text-foreground dark:text-white" />
<span className="text-xs font-medium text-foreground dark:text-white">Playlist</span>
</>
) : (
<>
<Video className="h-3 w-3 text-white" />
<span className="text-xs font-medium text-white">Video</span>
<Video className="h-3 w-3 text-foreground dark:text-white" />
<span className="text-xs font-medium text-foreground dark:text-white">Video</span>
</>
)}
</div>
<div className="absolute bottom-2 right-2 flex items-center gap-1 px-2 py-1 rounded-md bg-black/80 backdrop-blur-sm">
{isPlaylist ? (
<>
<List className="h-3 w-3 text-white" />
<span className="text-xs font-medium text-white">Playlist</span>
</>
) : (
<>
<Video className="h-3 w-3 text-white" />
<span className="text-xs font-medium text-white">Video</span>
</>
)}
</div>
🤖 Prompt for AI Agents
In `@src/components/workspace-canvas/YouTubeCardContent.tsx` around lines 96 -
108, The badge's icon and label use text-foreground which becomes dark in light
mode and is unreadable on the always-dark bg (bg-black/80); update the className
for the List and Video icons and the two span labels inside the conditional in
YouTubeCardContent.tsx so they use a persistent white color (e.g., replace
"text-foreground dark:text-white" with "text-white" or "text-white
dark:text-white") so the badge remains readable regardless of theme.

Comment on lines 110 to 113
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-14 h-14 rounded-full bg-red-600 hover:bg-red-500 flex items-center justify-center shadow-lg transition-all group-hover:scale-110">
<Play className="h-7 w-7 text-white fill-white ml-1" />
<Play className="h-7 w-7 text-foreground fill-foreground ml-1 dark:text-white dark:fill-white" />
</div>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Play button icon has the same contrast problem.

The play icon sits on a bg-red-600 circle. text-foreground / fill-foreground will be dark in light mode, reducing contrast. This should stay text-white fill-white since the red background is theme-independent.

Proposed fix
-                  <Play className="h-7 w-7 text-foreground fill-foreground ml-1 dark:text-white dark:fill-white" />
+                  <Play className="h-7 w-7 text-white fill-white ml-1" />

Same applies to the fallback play button on line 120.

📝 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.

Suggested change
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-14 h-14 rounded-full bg-red-600 hover:bg-red-500 flex items-center justify-center shadow-lg transition-all group-hover:scale-110">
<Play className="h-7 w-7 text-white fill-white ml-1" />
<Play className="h-7 w-7 text-foreground fill-foreground ml-1 dark:text-white dark:fill-white" />
</div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-14 h-14 rounded-full bg-red-600 hover:bg-red-500 flex items-center justify-center shadow-lg transition-all group-hover:scale-110">
<Play className="h-7 w-7 text-white fill-white ml-1" />
</div>
🤖 Prompt for AI Agents
In `@src/components/workspace-canvas/YouTubeCardContent.tsx` around lines 110 -
113, The Play icon's color classes are using theme-dependent utilities
(text-foreground / fill-foreground) which reduce contrast against the fixed
bg-red-600; update the Play element inside YouTubeCardContent (the Play
component instance in the red circular button and the fallback play button) to
use explicit white colors (text-white and fill-white) and remove the dark:...
variants so the icon remains white regardless of theme, preserving sufficient
contrast against the red background.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 37 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/components/workspace-canvas/ItemPanelContent.tsx">

<violation number="1" location="src/components/workspace-canvas/ItemPanelContent.tsx:127">
P2: The inline `backgroundColor` overrides the `.dark .note-panel-background` CSS rule, so dark mode will never use `--note-bg-dark`. Remove the inline background color and let the class handle light/dark switching.</violation>
</file>

<file name="src/components/landing/FloatingWorkspaceCards.tsx">

<violation number="1" location="src/components/landing/FloatingWorkspaceCards.tsx:124">
P2: Rendering a class based on resolvedTheme during the initial render can cause a hydration mismatch (resolvedTheme is undefined on the server and resolves on the client). Consider avoiding theme-dependent rendering before mount (e.g., use Tailwind's `dark:` variant or guard with a mounted check).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

['--note-bg-dark' as string]: item.color
? getCardColorCSS(item.color, 0.1) // Darker opacity for dark mode
: "rgba(0, 0, 0, 0.1)",
backgroundColor: 'var(--note-bg-light)',
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 7, 2026

Choose a reason for hiding this comment

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

P2: The inline backgroundColor overrides the .dark .note-panel-background CSS rule, so dark mode will never use --note-bg-dark. Remove the inline background color and let the class handle light/dark switching.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/workspace-canvas/ItemPanelContent.tsx, line 127:

<comment>The inline `backgroundColor` overrides the `.dark .note-panel-background` CSS rule, so dark mode will never use `--note-bg-dark`. Remove the inline background color and let the class handle light/dark switching.</comment>

<file context>
@@ -116,9 +116,15 @@ export function ItemPanelContent({
+                ['--note-bg-dark' as string]: item.color
+                    ? getCardColorCSS(item.color, 0.1) // Darker opacity for dark mode
+                    : "rgba(0, 0, 0, 0.1)",
+                backgroundColor: 'var(--note-bg-light)',
                 backdropFilter: "blur(24px)",
                 WebkitBackdropFilter: "blur(24px)",
</file context>
Fix with Cubic

className={cn(
"absolute inset-0 w-[120%] -ml-[10%] -mt-[5%] columns-2 md:columns-3 lg:columns-6 gap-4 md:gap-6 lg:gap-8 opacity-30 transition-transform duration-800 ease-out pointer-events-none",
"absolute inset-0 w-[120%] -ml-[10%] -mt-[5%] columns-2 md:columns-3 lg:columns-6 gap-4 md:gap-6 lg:gap-8 transition-transform duration-800 ease-out pointer-events-none",
resolvedTheme === 'dark' ? "opacity-30" : "opacity-50",
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 7, 2026

Choose a reason for hiding this comment

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

P2: Rendering a class based on resolvedTheme during the initial render can cause a hydration mismatch (resolvedTheme is undefined on the server and resolves on the client). Consider avoiding theme-dependent rendering before mount (e.g., use Tailwind's dark: variant or guard with a mounted check).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/landing/FloatingWorkspaceCards.tsx, line 124:

<comment>Rendering a class based on resolvedTheme during the initial render can cause a hydration mismatch (resolvedTheme is undefined on the server and resolves on the client). Consider avoiding theme-dependent rendering before mount (e.g., use Tailwind's `dark:` variant or guard with a mounted check).</comment>

<file context>
@@ -118,7 +120,8 @@ export function FloatingWorkspaceCards({
                 className={cn(
-                    "absolute inset-0 w-[120%] -ml-[10%] -mt-[5%] columns-2 md:columns-3 lg:columns-6 gap-4 md:gap-6 lg:gap-8 opacity-30 transition-transform duration-800 ease-out pointer-events-none",
+                    "absolute inset-0 w-[120%] -ml-[10%] -mt-[5%] columns-2 md:columns-3 lg:columns-6 gap-4 md:gap-6 lg:gap-8 transition-transform duration-800 ease-out pointer-events-none",
+                    resolvedTheme === 'dark' ? "opacity-30" : "opacity-50",
                     className
                 )}
</file context>
Fix with Cubic

Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed 77569c1 in 20 seconds. Click for details.
  • Reviewed 70 lines of code in 2 files
  • Skipped 0 files when reviewing.
  • Skipped posting 0 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.

Workflow ID: wflow_SfKwbmv2vJfRyv4b

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@urjitc
Copy link
Member Author

urjitc commented Feb 7, 2026

@greptile did any existing styling change for dark mode users

@urjitc urjitc merged commit bcb57f2 into main Feb 7, 2026
3 of 5 checks passed
@urjitc urjitc deleted the feat-light-mode branch February 7, 2026 19:23
@github-project-automation github-project-automation bot moved this from Backlog to Done in Dev Board Feb 7, 2026
Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed 9a32afb in 20 seconds. Click for details.
  • Reviewed 175 lines of code in 1 files
  • Skipped 0 files when reviewing.
  • Skipped posting 0 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.

Workflow ID: wflow_JDBsTFj4NP5mNNtj

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 7, 2026

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/components/workspace-canvas/QuizContent.tsx">

<violation number="1" location="src/components/workspace-canvas/QuizContent.tsx:426">
P2: "dark:dark:" is an invalid Tailwind variant and will be ignored, so the intended dark-mode styling won’t apply. Use a single dark: prefix.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

{/* Center: Progress bar */}
<div className="flex-1 mx-4">
<div className="w-full h-1.5 bg-white/10 rounded-full overflow-hidden">
<div className="w-full h-1.5 bg-gray-200/50 rounded-full overflow-hidden dark:bg-foreground/10 dark:dark:bg-white/10">
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 7, 2026

Choose a reason for hiding this comment

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

P2: "dark:dark:" is an invalid Tailwind variant and will be ignored, so the intended dark-mode styling won’t apply. Use a single dark: prefix.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/workspace-canvas/QuizContent.tsx, line 426:

<comment>"dark:dark:" is an invalid Tailwind variant and will be ignored, so the intended dark-mode styling won’t apply. Use a single dark: prefix.</comment>

<file context>
@@ -423,9 +423,9 @@ export function QuizContent({ item, onUpdateData, isScrollLocked = false }: Quiz
                     {/* Center: Progress bar */}
                     <div className="flex-1 mx-4">
-                        <div className="w-full h-1.5 bg-foreground/10 rounded-full overflow-hidden dark:bg-white/10">
+                        <div className="w-full h-1.5 bg-gray-200/50 rounded-full overflow-hidden dark:bg-foreground/10 dark:dark:bg-white/10">
                             <div
-                                className="h-full bg-foreground transition-all duration-300 dark:bg-white"
</file context>
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant