-
-
Notifications
You must be signed in to change notification settings - Fork 45
Themes #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. WalkthroughAdds a multi-theme system with radius control, a new Themes playground and CopyTheme dialog, extensive global CSS theme palettes, UI styling updates (rounded/token alignment), preview blocks, navigation/layout tweaks, and a marketing CTA update linking to /themes. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Browser
participant ThemesPage as "Themes Page"
participant UI as "Preview UI"
participant CopyTheme as "CopyTheme Dialog"
participant ThemeCfg as "themeConfig()"
User->>Browser: Navigate to /themes
Browser->>ThemesPage: Load page
ThemesPage->>UI: Render preview grid with class "theme-{name}" (+".with-radius" if set)
User->>UI: Select Theme / Variant
UI->>ThemesPage: onValueChange(theme, variant)
ThemesPage->>UI: Update root classNames -> re-render previews
User->>CopyTheme: Click "Copy Theme"
CopyTheme->>ThemeCfg: Request CSS for selected theme (hasRadius?)
ThemeCfg-->>CopyTheme: Return CSS string
CopyTheme->>Browser: Show dialog with copyable code
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (2)
✨ 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. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
app/(marketing)/page.tsx (1)
209-213: Fix typo in alt text: “shotting” → “shooting”User-facing copy. Correct the alt text in both images.
- alt="shotting stars" + alt="shooting stars"Also applies to: 224-229
components/retroui/Dialog.tsx (3)
13-25: Overlay “none” variant doesn’t cover the viewport.
inset-0 z-50are only on the default variant; “none” yields a zero-size overlay that won’t capture interactions.-const overlayVariants = cva( - ` fixed bg-black/80 font-head +const overlayVariants = cva( + ` fixed inset-0 z-50 bg-black/80 font-head data-[state=open]:fade-in-0 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 `, { variants: { variant: { - default: "inset-0 z-50 bg-black/85", + default: "bg-black/85", none: "fixed bg-transparent", }, },
104-107: Dialog lacks an accessible title; replace empty Title with a real Title.You render an empty
<Dialog.Title />and the header is a plain div, so screen readers get no name.- <VisuallyHidden> - <ReactDialog.Title /> - </VisuallyHidden> + {/* Title is provided by DialogHeader below via ReactDialog.Title */}-const DialogHeaderDefaultLayout = ({ children }: { children: ReactNode }) => { +const DialogHeaderDefaultLayout = ({ children }: { children: ReactNode }) => { return ( <> {children} - <DialogTrigger title="Close pop-up" className="cursor-pointer" asChild> - <X /> - </DialogTrigger> + <ReactDialog.Close asChild> + <button aria-label="Close dialog" className="cursor-pointer p-2"> + <X /> + </button> + </ReactDialog.Close> </> ); };- return ( - <div - className={cn(dialogHeaderVariants({ position, variant }), className)} - {...props} - > + return ( + <ReactDialog.Title + className={cn(dialogHeaderVariants({ position, variant }), className)} + {...props} + > {asChild ? ( children ) : ( <DialogHeaderDefaultLayout>{children}</DialogHeaderDefaultLayout> )} - </div> + </ReactDialog.Title> );Also applies to: 167-183, 202-221
186-194: Use Close instead of Trigger for the header X.
Triggertoggles;Closealways closes and is the intended control inside content.
🧹 Nitpick comments (38)
components/retroui/charts/LineChart.tsx (1)
54-54: Avoid line/dot tooltip clipping by restoring headroom (dynamic top margin)
top: 0risks clipping stroke/active dots and tooltips. Compute a small top margin fromstrokeWidth/dotSize.Apply this diff:
- <RechartsLineChart data={data} margin={{ top: 0, right: 30, left: 0, bottom: 0 }}> + <RechartsLineChart data={data} margin={{ top: topMargin, right: 30, left: 0, bottom: 0 }}>Add inside the component before the return:
const topMargin = Math.max(strokeWidth + (dotSize ?? 0), 8);Quick check: verify no clipping at small container heights (e.g., h-40) and with larger
dotSize(6–8).components/retroui/Alert.tsx (2)
7-7: Use the shared radius token utility instead of fixedroundedKeeps radius consistent with the new theme “Variant” toggle.
-const alertVariants = cva("relative w-full rounded border-2 p-4", { +const alertVariants = cva("relative w-full with-radius border-2 p-4", {
14-18: Align status colors with theme tokensHard-coded Tailwind palette locks colors and fights theming. Consider mapping
statusto tokenized classes (e.g.,bg-[var(--success)],text-[var(--success-foreground)], etc.) or theme-aware variants.Would you like a follow-up patch that introduces token-backed status variants?
components/retroui/Accordion.tsx (1)
18-18: Gate radius with theme utility (not fixedrounded)Keeps Accordion aligned with the theme’s radius toggle.
- "border-2 bg-background rounded text-foreground shadow-md hover:shadow-sm data-[state=open]:shadow-sm transition-all overflow-hidden", + "border-2 bg-background with-radius text-foreground shadow-md hover:shadow-sm data-[state=open]:shadow-sm transition-all overflow-hidden",config/navigation.ts (1)
11-11: Top-nav addition looks goodThe item matches INavItem and the route is consistent with the new page. Optionally add a temporary "New" tag to highlight it.
- { title: "Themes", href: "/themes" }, + { title: "Themes", href: "/themes", tag: "New" },components/retroui/Textarea.tsx (2)
5-5: Remove unused/invalidtypeprop
textareadoesn’t accept atypeattribute. The prop is unused and can confuse consumers.-export function Textarea({ - type = "text", +export function Textarea({ placeholder = "Enter text...", className = "", ...props }) {
15-16: Use design token for border radius (optional)
Consider replacing the fixedroundedclass with your radius token utility—e.g.rounded-[var(--radius)]—or add the existingwith-radiusclass to stay aligned with your theming.components/retroui/Badge.tsx (1)
11-12: Strayoutline-2onsurfacevariant
outline-2has no effect withoutoutlinestyle. Remove it (or addoutline), to avoid dead CSS.- surface: "outline-2 bg-primary text-primary-foreground", + surface: "bg-primary text-primary-foreground",app/(marketing)/page.tsx (1)
206-206: Avoid hard-coding stars; fetch dynamically (optional)The “700+” count will drift. Consider fetching
stargazers_countfrom GitHub with revalidation.- 700+ + {stars.toLocaleString()}+Add (outside this hunk) and call it in the page:
async function getRepoStats() { const res = await fetch( "https://api.github.com/repos/Logging-Stuff/RetroUI", { next: { revalidate: 3600 } } // 1h ); const json = await res.json(); return { stars: json?.stargazers_count ?? 0 }; } // usage const { stars } = await getRepoStats();preview/blocks/sign-in.tsx (7)
24-26: Add names and autocomplete for better a11y and UX.Helps autofill and screen readers; harmless in a preview.
- <Input id="email" type="email" placeholder="you@example.com" /> + <Input id={emailId} name="email" autoComplete="email" type="email" placeholder="you@example.com" required /> ... - <Input - id="password" + <Input + id={passwordId} + name="password" + autoComplete="current-password" type={showPassword ? "text" : "password"} placeholder="••••••••" + required />Also applies to: 39-42
11-11: Avoid ID collisions across multiple previews with useId.Hard-coded ids can duplicate when multiple blocks render.
import { useState } from "react"; +import { useId } from "react"; ... - const [showPassword, setShowPassword] = useState(false); + const [showPassword, setShowPassword] = useState(false); + const emailId = useId(); + const passwordId = useId(); + const rememberId = useId(); ... - <Label htmlFor="email">Email</Label> - <Input id="email" type="email" placeholder="you@example.com" /> + <Label htmlFor={emailId}>Email</Label> + <Input id={emailId} type="email" placeholder="you@example.com" /> ... - <Label htmlFor="password">Password</Label> + <Label htmlFor={passwordId}>Password</Label> ... - <Checkbox id="remember" /> + <Checkbox id={rememberId} /> - <label - htmlFor="remember" + <label + htmlFor={rememberId}Also applies to: 24-26, 39-42, 60-67
43-56: Improve password toggle accessibility.Expose pressed state and control relationship.
- <button + <button type="button" className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent" - onClick={() => setShowPassword(!showPassword)} + aria-pressed={showPassword} + aria-controls={passwordId} + onClick={() => setShowPassword(!showPassword)} >
73-80: Use theme tokens instead of hard-coded grays/white.Ensures proper theming in dark/alt themes.
- <div className="absolute inset-0 flex items-center"> - <span className="w-full border-t border-gray-300" /> + <div className="absolute inset-0 flex items-center"> + <span className="w-full border-t border-border" /> </div> <div className="relative flex justify-center text-xs uppercase"> - <span className="bg-white px-2 text-gray-500"> + <span className="bg-card px-2 text-muted-foreground"> Or continue with </span> </div>
87-89: Brand capitalization: GitHub.- Github + GitHub
94-96: Icon mismatch for Google.Mail icon suggests email sign-in; consider a Google “G” icon (e.g., react-icons/fc) or omit the icon.
30-36: Prefer Next.js Link for internal routes.Prevents full page reloads; safe even in previews.
- <a - href="/forgot-password" + <Link + href="/forgot-password" className="text-sm font-medium underline underline-offset-4" > Forgot password? - </a> + </Link> ... - <a - href="/sign-up" + <Link + href="/sign-up" className="font-bold underline underline-offset-4" > Sign up - </a> + </Link>Note: add
import Link from "next/link";.Also applies to: 101-106
config/theme.ts (2)
13-15: Unify display names (name field) casing.Mix of "default"/"purple" vs "Red"/"Green". Pick one convention (Title Case recommended).
- name: "default", + name: "Default", ... - name: "purple", + name: "Purple", ... - name: "Lime", + name: "Lime", ... - name: "Red", + name: "Red", ... - name: "Lavender", + name: "Lavender", ... - name: "Orange", + name: "Orange", ... - name: "Green", + name: "Green",Also applies to: 56-58, 99-101, 146-148, 197-199, 249-251, 300-302
353-353: Avoid recomputing config for keys.Minor: cache
const CONFIG = themeConfig({ hasRadius: false })and export bothCONFIGandObject.keys(CONFIG). Cleaner and avoids duplicate construction.app/global.css (4)
371-375: Improve Firefox scrollbar theming (track should not be foreground).Using the foreground as the track color can look inverted and noisy in both themes. Prefer a transparent or background track.
-html { - scrollbar-width: auto; - scrollbar-color: var(--muted) var(--foreground); -} +html { + scrollbar-width: auto; + /* thumb, track */ + scrollbar-color: var(--muted) transparent; +}
66-89: Advertise color scheme to the browser for native UI (forms/scrollbars).Setting
color-schemeimproves native controls rendering in light/dark.:root { + color-scheme: light; --radius: 0; /* ... */ } .dark { + color-scheme: dark; --background: #1a1a1a; /* ... */ }Also applies to: 40-64
480-494: Add hover style for WebKit scrollbars to match Firefox behavior.Currently only Firefox thumb color changes on hover.
.sidebar-scroll::-webkit-scrollbar-thumb { background: transparent; border-radius: 2px; transition: background 0.3s ease; } + +.sidebar-scroll:hover::-webkit-scrollbar-thumb { + background: var(--color-primary); +}
139-180: Unify token surface across themes.Several themes define extra tokens (
--input,--ring,--popover*) while others don’t. If components read these tokens, missing definitions will fall back unexpectedly. Consider defining a consistent set across all themes (or provide safe fallbacks in component styles).Also applies to: 182-226, 228-272, 274-318, 320-364
components/TopNav.tsx (3)
39-39: Use themed border color and drop redundantright-0on a sticky element.
right-0has no effect onposition: sticky. Also specifyborder-borderto use the tokenized border color.- <nav className="sticky z-10 top-0 right-0 w-full border-b-2 bg-background"> + <nav className="sticky top-0 z-10 w-full border-b-2 border-border bg-background">
13-23: Avoid duplicating theme toggle logic (layout already sets dark class).You set dark mode in RootLayout before paint and again here after mount. This is harmless but redundant and can cause brief icon flicker. Consider reading the initial state from
document.documentElement.classList.contains('dark')and skipping the first-effect logic, or remove one of the two.Also applies to: 110-113
40-55: Consider theming the promo bar.Hardcoded
bg-black text-whiteignores selected themes. If intentional, ignore; otherwise swap to tokens (bg-secondary text-secondary-foregroundor similar).app/layout.tsx (1)
48-66: Respect OS preference when no saved theme exists.If
localStorage.themeis unset, you can default toprefers-color-schemeto reduce surprises for first-time visitors.- const theme = localStorage.getItem('theme'); - if (theme === 'dark') { + const theme = localStorage.getItem('theme'); + const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + if (theme === 'dark' || (!theme && prefersDark)) { document.documentElement.classList.add('dark'); } else { document.documentElement.classList.remove('dark'); }preview/blocks/course-card.tsx (2)
31-44: Use themed border color instead of hardcoded black tint.
border-black/10will be too light/dark depending on theme. Use the border token.- <div className="flex text-sm justify-between gap-4 py-4 border-y-2 border-black/10 mb-4"> + <div className="flex text-sm justify-between gap-4 py-4 border-y border-border mb-4">
46-48: Non-submit intent: settype="button"on button.Prevents accidental form submission if this block is ever placed inside a form.
- <Button className="w-full font-bold font-sans justify-center"> + <Button type="button" className="w-full font-bold font-sans justify-center">components/CopyTheme.tsx (3)
13-13: Harden dynamic classes and avoid stray spaces.Use a classnames helper and ensure
themeis a string name (not a numeric enum) to avoidtheme-0classes.-import { CodeBlock } from "./CodeBlock"; +import { CodeBlock } from "./CodeBlock"; +import { cn } from "@/lib/utils"; @@ - <Dialog.Content className={`theme-${theme} ${hasRadius ? "with-radius" : ""}`}> + <Dialog.Content className={cn(`theme-${String(theme)}`, hasRadius && "with-radius")}>
11-12: Non-submit intent: settype="button"on trigger button.- <Button>Copy Theme</Button> + <Button type="button">Copy Theme</Button>
20-22: Large code blocks: considerwhite-space: preandfont-mono.For long CSS strings, ensure code wraps correctly and uses monospaced font. Your CodeBlock probably handles this, but if not, add classes here.
preview/blocks/team-members.tsx (4)
4-4: Remove unused import.
Clockisn’t used.-import { Clock, User } from "lucide-react"; +import { User } from "lucide-react";
10-11: Align borders with theme tokens.Use token-aware border utilities for consistent theming.
- className="border-2 p-4 flex items-center justify-between gap-3 bg-card rounded" + className="border border-muted p-4 flex items-center justify-between gap-3 bg-card rounded"Also applies to: 32-33, 53-54
15-17: Use descriptive alt text (accessibility).Alt should reflect the person’s name.
- alt="Person A" + alt="Alex Johnson" ... - alt="Person B" + alt="John Doe" ... - alt="Person C" + alt="Jane Smith"Also applies to: 36-38, 57-59
9-75: De-duplicate the three cards.Consider mapping over a small data array to reduce repetition and ease future edits.
Example:
const members = [ { name: "Alex Johnson", email: "alex@example.com", avatar: "…person_a.png", badge: { label: "Admin", variant: "solid" as const } }, { name: "John Doe", email: "john@example.com", avatar: "…person_b.png", badge: { label: "Member", variant: "solid" as const } }, { name: "Jane Smith", email: "jane@example.com", avatar: "…person_c.png", badge: { label: "Pending", variant: "surface" as const } }, ];Then render with
members.map(...).app/(marketing)/themes/page.tsx (3)
17-46: Avoid name shadowing and future drift: rename option arrays.Prevents confusion with exported
themesfrom config and avoids shadowing state vars inside maps.-const themes: {value: Theme, label: string}[] = [ +const themeOptions: {value: Theme, label: string}[] = [ @@ -const variants = [ +const variantOptions = [ @@ - const [theme, setTheme] = useState<Theme>(themes[0].value); - const [variant, setVariant] = useState(variants[0].value); + const [theme, setTheme] = useState<Theme>(themeOptions[0].value); + const [variant, setVariant] = useState(variantOptions[0].value); @@ - themes.map((theme) => ( - <Select.Item key={theme.value} value={theme.value}> - {theme.label} + themeOptions.map((opt) => ( + <Select.Item key={opt.value} value={opt.value}> + {opt.label} </Select.Item> )) @@ - variants.map((variant) => ( - <Select.Item key={variant.value} value={variant.value}> - {variant.label} + variantOptions.map((opt) => ( + <Select.Item key={opt.value} value={opt.value}> + {opt.label} </Select.Item> ))Also applies to: 60-61, 75-80, 48-57, 61-61, 93-97
70-73: Add accessible names to Select triggers.Improves a11y for screen readers.
- <Select.Trigger> + <Select.Trigger aria-label="Theme"> @@ - <Select.Trigger> + <Select.Trigger aria-label="Variant">Also applies to: 87-90
107-107: Make the grid responsive.Prevents cramped layout on small screens.
- <div className="grid grid-cols-3 w-full gap-6"> + <div className="grid grid-cols-1 md:grid-cols-3 w-full gap-6">
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (25)
app/(marketing)/page.tsx(2 hunks)app/(marketing)/themes/page.tsx(1 hunks)app/global.css(8 hunks)app/layout.tsx(1 hunks)components/CopyTheme.tsx(1 hunks)components/TopNav.tsx(1 hunks)components/retroui/Accordion.tsx(1 hunks)components/retroui/Alert.tsx(1 hunks)components/retroui/Badge.tsx(1 hunks)components/retroui/Button.tsx(1 hunks)components/retroui/Card.tsx(1 hunks)components/retroui/Checkbox.tsx(1 hunks)components/retroui/Command.tsx(4 hunks)components/retroui/Dialog.tsx(3 hunks)components/retroui/Input.tsx(1 hunks)components/retroui/Select.tsx(1 hunks)components/retroui/Textarea.tsx(1 hunks)components/retroui/charts/LineChart.tsx(1 hunks)config/navigation.ts(1 hunks)config/theme.ts(1 hunks)content/docs/install/index.mdx(1 hunks)preview/blocks/course-card.tsx(1 hunks)preview/blocks/sign-in.tsx(1 hunks)preview/blocks/team-members.tsx(1 hunks)preview/components/command-style-default.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (19)
components/retroui/Button.tsx (3)
preview/components/dialog-style-width-variant.tsx (1)
singleVariant(45-54)preview/components/button-style-outline.tsx (1)
ButtonStyleOutline(3-5)preview/components/tooltip-style-primary.tsx (1)
TooltipStylePrimary(5-16)
content/docs/install/index.mdx (1)
app/(docs)/docs/layout.tsx (1)
ComponentLayout(8-18)
components/retroui/Input.tsx (1)
preview/components/input-style-error.tsx (1)
InputStyleError(4-18)
components/retroui/charts/LineChart.tsx (2)
components/retroui/charts/AreaChart.tsx (1)
value(32-151)components/retroui/charts/BarChart.tsx (1)
value(32-157)
components/retroui/Accordion.tsx (1)
preview/components/accordion-style-default.tsx (1)
AccordionStyleDefault(5-28)
preview/blocks/course-card.tsx (3)
components/retroui/Badge.tsx (1)
Badge(29-44)components/retroui/Text.tsx (1)
Text(30-37)components/retroui/Button.tsx (1)
Button(39-62)
components/retroui/Alert.tsx (4)
preview/components/alert-style-solid.tsx (1)
AlertStyleDefault(3-12)preview/components/alert-style-default.tsx (1)
AlertStyleDefault(3-12)preview/components/alert-style-all-status.tsx (1)
AlertAllStatus(4-25)preview/components/alert-style-with-icon.tsx (1)
AlertStyleDefaultIcon(4-16)
app/layout.tsx (3)
components/TopNav.tsx (1)
TopNav(10-118)app/(marketing)/blogs/layout.tsx (1)
BlogsLayout(7-18)app/(docs)/docs/layout.tsx (1)
ComponentLayout(8-18)
preview/blocks/team-members.tsx (3)
components/retroui/Text.tsx (1)
Text(30-37)components/retroui/Badge.tsx (1)
Badge(29-44)preview/components/badge-style-variants.tsx (1)
BadgeStyleVariants(3-12)
components/CopyTheme.tsx (4)
components/retroui/Button.tsx (1)
Button(39-62)components/retroui/Text.tsx (1)
Text(30-37)components/CodeBlock.tsx (1)
CodeBlock(16-55)config/theme.ts (1)
themeConfig(12-351)
config/navigation.ts (1)
types/index.d.ts (3)
INavigationConfig(12-15)INavItem(1-5)INavItemWithChildren(7-10)
components/retroui/Badge.tsx (4)
preview/components/badge-style-variants.tsx (1)
BadgeStyleVariants(3-12)preview/components/badge-style-sizes.tsx (1)
BadgeStyleVariants(3-11)preview/components/badge-style-rounded.tsx (1)
BadgeStyleRounded(3-14)preview/components/badge-style-default.tsx (1)
BadgeStyleDefault(3-5)
components/retroui/Card.tsx (3)
preview/components/card-style-default.tsx (1)
BasicCard(3-15)components/retroui/BasicCard.tsx (1)
BasicCard(3-12)preview/components/card-style-commerce.tsx (1)
CommerceCard(4-23)
app/(marketing)/themes/page.tsx (3)
config/theme.ts (1)
themes(353-353)components/retroui/Text.tsx (1)
Text(30-37)components/CopyTheme.tsx (1)
CopyTheme(7-27)
components/retroui/Textarea.tsx (1)
preview/components/textarea-style-default.tsx (1)
TextareaStyleDefault(3-11)
components/retroui/Command.tsx (1)
preview/components/command-style-dialog.tsx (1)
Component(15-78)
preview/blocks/sign-in.tsx (5)
components/retroui/Text.tsx (1)
Text(30-37)components/retroui/Label.tsx (1)
Label(11-16)components/retroui/Input.tsx (1)
Input(7-25)components/retroui/Checkbox.tsx (1)
Checkbox(30-50)components/retroui/Button.tsx (1)
Button(39-62)
app/(marketing)/page.tsx (1)
components/retroui/Badge.tsx (1)
Badge(29-44)
components/retroui/Dialog.tsx (2)
preview/components/dialog-style-width-variant.tsx (1)
DialogStyleWidthVariant(17-61)preview/components/dialog-style-default.tsx (1)
DialogStyleDefault(5-29)
🪛 ESLint
app/(marketing)/themes/page.tsx
[error] 65-65: ' can be escaped with ', ‘, ', ’.
(react/no-unescaped-entities)
preview/blocks/sign-in.tsx
[error] 100-100: ' can be escaped with ', ‘, ', ’.
(react/no-unescaped-entities)
🔇 Additional comments (12)
components/retroui/Select.tsx (1)
18-18: Usewith-radiusutility, consolidate border classes, fix width, add accessible focus ring
- Replace
roundedwith existingwith-radiusutility (defined in global.css).- Remove
border-input; retain onlyborder-border.- Swap non-default
min-w-40formin-w-[10rem](or extendminWidthin tailwind.config).- Introduce an accessible focus state: use
focus-visible:ring-2 ring-ring ring-offset-2instead of hiding all outlines.Apply this diff:
- "flex h-10 rounded min-w-40 items-center shadow-md justify-between border-2 border-input border-border bg-transparent px-4 py-2 placeholder:text-muted-foreground outline-none focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50", + "flex h-10 with-radius min-w-[10rem] items-center shadow-md justify-between border-2 border-border bg-transparent px-4 py-2 placeholder:text-muted-foreground outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",Confirm your tailwind.config includes the
with-radiusutility and anyminWidthextensions.components/retroui/Checkbox.tsx (1)
6-6: Confirm Tailwind DEFAULT borderRadius mapping (optional): I couldn’t locate atailwind.config.*file to verify ifDEFAULTborder radius is tied tovar(--radius). Please confirm your Tailwind config—and if it isn’t mapped, consider changing the variant fromroundedtorounded-[var(--radius)]to bind rounding to your theme’s radius token.preview/components/command-style-default.tsx (1)
17-17: LGTM: rely on component default radius.Removing redundant
rounded-lglets the baseCommandradius control visuals uniformly across themes.content/docs/install/index.mdx (1)
12-12: LGTM: grid columns now match item count.
lg:grid-cols-2correctly places the two cards in one row on large screens.components/retroui/Button.tsx (1)
7-13: Rounded + token-based foreground: good alignment with themingThe base
roundedandtext-primary-foregroundfor the default variant align with the new theme tokens.Please confirm there’s a visible keyboard focus style across themes since
outline-hiddensuppresses the default outline (e.g., viafocus-visible:ring-*or equivalent).components/retroui/Badge.tsx (1)
5-9: Theme-token adoption looks goodBase
roundedandbg-muted text-muted-foregroundfor the default variant match the palette shift.app/(marketing)/page.tsx (1)
57-63: Nice CTAEnabling the charts CTA via Badge + Link is clean and unobtrusive.
config/theme.ts (1)
124-144: Verify Lime dark palette (white background in .dark)..white backgrounds in dark mode may be intentional, but it breaks expectations and contrast in many components. Confirm design intent.
components/retroui/Dialog.tsx (1)
52-59: Content animations simplified — LGTM.The new layout is simpler and still provides adequate motion without the slide-in quirks.
app/layout.tsx (1)
75-76: Verify top spacing after removing the old wrapper.The previous wrapper added bottom padding; with a sticky (not fixed) nav this is likely fine, but please sanity-check pages with hero sections to ensure no overlap/regression.
app/(marketing)/themes/page.tsx (2)
104-105: LGTM: CopyTheme wiring.Prop derivation of
hasRadiusfrom variant is correct.
59-61: No changes required:Themeis a string enum
Themeis defined as a string enum ("default","purple","lime"), so bothsliceoperations and theSelectcallback receiving string values are safe.
| const [variant, setVariant] = useState(variants[0].value); | ||
|
|
||
| return ( | ||
| <section className={`theme-${theme} ${variant} container max-w-8xl mx-auto py-12`}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
❓ Verification inconclusive
Verify Tailwind support for max-w-8xl.
Ensure 8xl is defined; default Tailwind stops at 7xl.
🏁 Script executed:
#!/bin/bash
# Check Tailwind config for custom '8xl' size
rg -nP "maxWidth|extend\s*:\s*{[^}]*maxWidth" -C3 -- tailwind.config.* tailwind.config.*.* 2>/dev/null
rg -nP "8xl" -- tailwind.config.* tailwind.config.*.* 2>/dev/nullLength of output: 154
Add custom 8xl max-width or switch to 7xl. Default Tailwind defines max-w only up to 7xl; no 8xl entry was found in theme.extend.maxWidth. Either add an 8xl value in your tailwind.config.(js|ts) or update this class to max-w-7xl.
🤖 Prompt for AI Agents
In app/(marketing)/themes/page.tsx around line 64, the class uses a non-existent
Tailwind utility "max-w-8xl"; either change the class to use the existing
"max-w-7xl" or add a custom "8xl" entry in your Tailwind config. If you choose
to add the custom size, open tailwind.config.js/ts and under
theme.extend.maxWidth define an "8xl" key with your desired width value, then
rebuild; otherwise update the JSX class from max-w-8xl to max-w-7xl.
| .theme-purple { | ||
| --background: #f5f5f5; | ||
| --foreground: #1a1a1a; | ||
| --card: #FFFFFF; | ||
| --card-foreground: #f5f5f5; | ||
| --primary: #5F4FE6; | ||
| --primary-hover: #4938C2; | ||
| --primary-foreground: #fff; | ||
| --secondary: #3a3a3a; | ||
| --secondary-foreground: #f5f5f5; | ||
| --muted: #CFCCEA; | ||
| --muted-foreground: #5B5686; | ||
| --accent: #FED13B; | ||
| --accent-foreground: #000000; | ||
| --destructive: #EF4444; | ||
| --destructive-foreground: #fff; | ||
| --border: #3a3a3a; | ||
| --background-image: url("/images/bg_void_3.svg"); /* Optional: A different image for dark mode */ | ||
| --background-image: url("/images/bg_void_3.svg"); | ||
| --chart-1: #ffdb33; | ||
| --chart-2: #000; | ||
| --chart-3: #aeaeae; | ||
| --chart-4: #fae583; | ||
| --chart-5: #e63946; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix low-contrast text: card foreground is white on white in theme-purple (accessibility bug).
In light mode, --card: #FFFFFF and --card-foreground: #f5f5f5 produce near-invisible text. Switch the foreground to a dark tone for adequate contrast.
Apply:
- --card-foreground: #f5f5f5;
+ --card-foreground: #1a1a1a;📝 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.
| .theme-purple { | |
| --background: #f5f5f5; | |
| --foreground: #1a1a1a; | |
| --card: #FFFFFF; | |
| --card-foreground: #f5f5f5; | |
| --primary: #5F4FE6; | |
| --primary-hover: #4938C2; | |
| --primary-foreground: #fff; | |
| --secondary: #3a3a3a; | |
| --secondary-foreground: #f5f5f5; | |
| --muted: #CFCCEA; | |
| --muted-foreground: #5B5686; | |
| --accent: #FED13B; | |
| --accent-foreground: #000000; | |
| --destructive: #EF4444; | |
| --destructive-foreground: #fff; | |
| --border: #3a3a3a; | |
| --background-image: url("/images/bg_void_3.svg"); /* Optional: A different image for dark mode */ | |
| --background-image: url("/images/bg_void_3.svg"); | |
| --chart-1: #ffdb33; | |
| --chart-2: #000; | |
| --chart-3: #aeaeae; | |
| --chart-4: #fae583; | |
| --chart-5: #e63946; | |
| } | |
| .theme-purple { | |
| --background: #f5f5f5; | |
| --foreground: #1a1a1a; | |
| --card: #FFFFFF; | |
| --card-foreground: #1a1a1a; | |
| --primary: #5F4FE6; | |
| --primary-hover: #4938C2; | |
| --primary-foreground: #fff; | |
| --secondary: #3a3a3a; | |
| --secondary-foreground: #f5f5f5; | |
| --muted: #CFCCEA; | |
| --muted-foreground: #5B5686; | |
| --accent: #FED13B; | |
| --accent-foreground: #000000; | |
| --destructive: #EF4444; | |
| --destructive-foreground: #fff; | |
| --border: #3a3a3a; | |
| --background-image: url("/images/bg_void_3.svg"); | |
| --chart-1: #ffdb33; | |
| --chart-2: #000; | |
| --chart-3: #aeaeae; | |
| --chart-4: #fae583; | |
| --chart-5: #e63946; | |
| } |
🤖 Prompt for AI Agents
In app/global.css around lines 95 to 118, the theme-purple definition sets
--card: #FFFFFF and --card-foreground: #f5f5f5 which results in very
low-contrast (white-on-white) text; change --card-foreground to a dark color
(for example reuse --foreground: #1a1a1a or a similar dark hex) so card text
meets contrast requirements and remains consistent with the theme’s foreground
variable.
| <div | ||
| className={cn( | ||
| "inline-block border-2 shadow-md transition-all hover:shadow-xs bg-card", | ||
| "inline-block border-2 rounded shadow-md transition-all hover:shadow-nonw bg-card", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo breaks hover shadow.
hover:shadow-nonw isn’t a Tailwind class; likely meant hover:shadow-none.
Fix:
- "inline-block border-2 rounded shadow-md transition-all hover:shadow-nonw bg-card",
+ "inline-block border-2 rounded shadow-md transition-all hover:shadow-none bg-card",📝 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.
| "inline-block border-2 rounded shadow-md transition-all hover:shadow-nonw bg-card", | |
| "inline-block border-2 rounded shadow-md transition-all hover:shadow-none bg-card", |
🤖 Prompt for AI Agents
In components/retroui/Card.tsx around line 13, the class string contains a typo
"hover:shadow-nonw" which is not a valid Tailwind class and prevents the
intended hover shadow behavior; replace it with the correct class
"hover:shadow-none" (or another valid Tailwind shadow utility as intended) so
the hover state works properly.
| className={`px-4 py-2 w-full rounded border-2 shadow-md transition focus:outline-hidden focus:shadow-xs ${ | ||
| props["aria-invalid"] | ||
| ? "border-red-500 text-red-500 shadow-xs shadow-red-600" | ||
| ? "border-destructive text-destructive shadow-xs shadow-destructive" | ||
| : "" | ||
| } ${className}`} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Radius via token; verify custom utilities; simplify error visuals
- Swap
roundedfor your radius utility. focus:outline-hiddenandshadow-destructivelook custom; verify they’re defined.- Error state mixes
shadow-md(base) andshadow-xs; pick one and prefer a focus ring for accessibility.
- className={`px-4 py-2 w-full rounded border-2 shadow-md transition focus:outline-hidden focus:shadow-xs ${
- props["aria-invalid"]
- ? "border-destructive text-destructive shadow-xs shadow-destructive"
- : ""
- } ${className}`}
+ className={`px-4 py-2 w-full with-radius border-2 shadow-md transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${
+ props["aria-invalid"]
+ ? "border-destructive text-destructive"
+ : ""
+ } ${className}`}Verification script:
🏁 Script executed:
#!/bin/bash
# Confirm presence of custom utilities and avoid accidental no-ops
rg -n 'outline-hidden|shadow-destructive|with-radius' -S -g '!node_modules/**'
fd -HI 'global.css' -x rg -n 'with-radius|outline-hidden|shadow-destructive' {}Length of output: 23954
🏁 Script executed:
#!/bin/bash
echo "== app/global.css lines 80-120 =="
sed -n '80,120p' app/global.css || echo "app/global.css not found"
echo "== tailwind config files =="
fd -t f -HI 'tailwind.config.*'
for cfg in $(fd -t f -HI 'tailwind.config.*'); do
echo "== $cfg =="
sed -n '1,200p' "$cfg"
echo "..."
doneLength of output: 1146
Swap to design tokens and remove undefined utilities
- Replace
roundedwithwith-radius(components/retroui/Input.tsx:17) and verify your global CSS or Tailwind config actually uses--radiusforborder-radius. - Remove
focus:outline-hiddenandshadow-destructive(not defined in global.css) and use core Tailwind classes (focus:outline-none+focus-visible:ring-2 focus-visible:ring-offset-2) for accessible focus. - Don’t mix
shadow-mdandshadow-xs; pick one or lean on a focus ring for error states.
🤖 Prompt for AI Agents
components/retroui/Input.tsx lines 17-21: replace non-token/undefined utilities
by swapping `rounded` for the design-token class `with-radius` (ensure your
global CSS/Tailwind uses --radius for border-radius), remove
`focus:outline-hidden` and `shadow-destructive` and instead use core Tailwind
focus utilities like `focus:outline-none` plus `focus-visible:ring-2
focus-visible:ring-offset-2` for accessible focus, stop mixing `shadow-md` and
`shadow-xs` (choose one consistent shadow or remove shadows and rely on focus
ring for error state), and update the conditional error classes to apply a
visible ring/border color (e.g., error border or ring) rather than undefined
shadow utilities.
| [Theme.Default]: { | ||
| name: "default", | ||
| color: | ||
| `:root { | ||
| --radius: ${hasRadius ? "0.5rem" : "0"}; | ||
| --background: #fff; | ||
| --foreground: #000; | ||
| --card: #fff; | ||
| --card-foreground: #000; | ||
| --primary: #ffdb33; | ||
| --primary-hover: #ffcc00; | ||
| --primary-foreground: #000; | ||
| --secondary: #000; | ||
| --secondary-foreground: #fff; | ||
| --muted: #cccccc; | ||
| --muted-foreground: #5a5a5a; | ||
| --accent: #fae583; | ||
| --accent-foreground: #000; | ||
| --destructive: #e63946; | ||
| --destructive-foreground: #fff; | ||
| --border: #000; | ||
| } | ||
| .dark { | ||
| --background: #1a1a1a; | ||
| --foreground: #f5f5f5; | ||
| --card: #242424; | ||
| --card-foreground: #f5f5f5; | ||
| --primary: #ffdb33; | ||
| --primary-hover: #ffcc00; | ||
| --primary-foreground: #000; | ||
| --secondary: #3a3a3a; | ||
| --secondary-foreground: #f5f5f5; | ||
| --muted: #3f3f46; | ||
| --muted-foreground: #a0a0a0; | ||
| --accent: #fae583; | ||
| --accent-foreground: #000; | ||
| --destructive: #e63946; | ||
| --destructive-foreground: #fff; | ||
| --border: #5c5c5c; | ||
| } | ||
| `, | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Normalize token coverage across themes.
Default lacks tokens present elsewhere (e.g., --input, --ring, --popover); Purple also omits some. Missing vars can cause inconsistent UI when components rely on them. Recommend ensuring each theme defines a complete, identical token set.
I can generate a consolidated token schema and a validation script to assert presence across all themes if you want.
Also applies to: 56-98, 99-145, 146-196, 197-248, 249-299, 300-351
🏁 Script executed:
#!/bin/bash
python3 - << 'EOF'
import re,sys
try:
data = open('config/theme.ts', 'r').read()
except FileNotFoundError:
sys.stderr.write('ERROR: config/theme.ts not found\n')
sys.exit(1)
# Extract each theme's CSS variable tokens
themes = re.findall(r'\[Theme\.(\w+)\]:\s*{[^}]*color:\s*`([^`]*)`', data, re.DOTALL)
if not themes:
sys.stderr.write('ERROR: No theme definitions found\n')
sys.exit(1)
for name, color in themes:
vars = re.findall(r'--([\w-]+):', color)
print(f"{name}: {sorted(set(vars))}")
EOFLength of output: 2129
Normalize tokens across all themes
Default and Purple are missing CSS vars (--input, --ring, --popover, --popover-foreground) present in other themes; ensure every theme defines the complete token set and add a validation check (script or unit test) to enforce consistency.
🤖 Prompt for AI Agents
In config/theme.ts around lines 13 to 55, the Default theme is missing several
CSS token variables (--input, --ring, --popover, --popover-foreground) that
other themes include; update the Default (and Purple if applicable) theme CSS
string to define those missing vars for both light and .dark blocks to match the
full token set used across themes, and then add a validation step (either a
small script or unit test) that loads all theme objects, extracts the CSS
variable names from each theme string, compares them to a canonical set (e.g.,
from the most complete theme), and fails the build/test if any theme is missing
or has extra tokens so future changes are enforced.
| Don't have an account?{" "} | ||
| <a | ||
| href="/sign-up" | ||
| className="font-bold underline underline-offset-4" | ||
| > | ||
| Sign up | ||
| </a> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix ESLint: escape the apostrophe in “Don't”.
react/no-unescaped-entities flags this and can fail CI.
- Don't have an account?{" "}
+ Don't have an account?{" "}📝 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.
| Don't have an account?{" "} | |
| <a | |
| href="/sign-up" | |
| className="font-bold underline underline-offset-4" | |
| > | |
| Sign up | |
| </a> | |
| </div> | |
| Don't have an account?{" "} | |
| <a | |
| href="/sign-up" | |
| className="font-bold underline underline-offset-4" | |
| > | |
| Sign up | |
| </a> | |
| </div> |
🧰 Tools
🪛 ESLint
[error] 100-100: ' can be escaped with ', ‘, ', ’.
(react/no-unescaped-entities)
🤖 Prompt for AI Agents
In preview/blocks/sign-in.tsx around lines 100 to 107, the string "Don't have an
account?" triggers react/no-unescaped-entities; replace the apostrophe with an
escaped entity (e.g., Don't) or otherwise escape the apostrophe in the JSX
so the text becomes Don't have an account? to satisfy ESLint and avoid CI
failures.
Summary by CodeRabbit
New Features
Style
Documentation
Chores