Skip to content
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

docs(themes): adding theme generator #4626

Open
wants to merge 16 commits into
base: canary
Choose a base branch
from
Open

Conversation

macci001
Copy link
Contributor

@macci001 macci001 commented Jan 23, 2025

Closes #3066

📝 Description

PR adds the theme generator
base pr: #3707 (credits: @xylish7)

⛳️ Current behavior (updates)

🚀 New behavior

💣 Is this a breaking change (Yes/No):

📝 Additional Information

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a comprehensive theme builder and configuration system.
    • Introduced new showcase components for various UI elements (Avatar, Button, Checkbox, Chip, etc.).
    • Created a flexible theming interface with support for light and dark modes.
    • Added new icons including Crop, Filters, MirrorLeft, PaletteRound, RadialBlur, Scaling, and TextSquare.
    • Implemented a new ColorPicker component for color selection.
    • Introduced a ConfigSection component for organizing configuration settings.
    • Added a new ThemesPage component for theme management.
  • Components

    • New theme configuration page with customizable settings.
    • Enhanced navigation with a new "Themes" link.
    • Implemented responsive design for theme showcase components.
    • Added new components for managing layout colors, border widths, and font selections.
    • Introduced new Actions, BaseColors, ContentColors, DefaultColors, DisableOpacity, Fonts, Radiuses, Scaling, and Swatch components for theme customization.
  • Styling

    • Added new utility classes for scrollbar hiding.
    • Expanded color and styling options for UI components.
  • Dependencies

    • Added react-colorful and values.js for advanced color manipulation.

Copy link

changeset-bot bot commented Jan 23, 2025

⚠️ No Changeset found

Latest commit: 4e4087c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

vercel bot commented Jan 23, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
heroui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 30, 2025 9:13am
heroui-sb ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 30, 2025 9:13am

Copy link

linear bot commented Jan 23, 2025

Copy link
Contributor

coderabbitai bot commented Jan 23, 2025

Walkthrough

A comprehensive theme builder and configuration system has been introduced to the documentation application. This new feature allows users to interactively customize UI themes, including color schemes, font settings, border radii, and other design parameters. The implementation spans multiple components and utilities that work together to provide a dynamic theming experience across light and dark modes.

Changes

File/Directory Change Summary
apps/docs/components/themes/ Added comprehensive theming infrastructure with configuration, templates, utilities, and showcase components
apps/docs/components/icons/ Introduced multiple new SVG icon components for theme-related interactions
apps/docs/navbar.tsx Added a new "Themes" navigation item
apps/docs/package.json Added dependencies react-colorful and values.js for color manipulation
apps/docs/tailwind.config.js Added .scrollbar-hide utility class

Assessment against linked issues

Objective Addressed Explanation
Theme generator feature Fully implemented a comprehensive theme builder with interactive configuration
Showcase of theme customization Included multiple showcase components demonstrating theme variations
Flexible theme configuration Supported multiple theme templates and granular customization options
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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: 40

🧹 Nitpick comments (73)
apps/docs/components/themes/css-vars.ts (3)

7-13: Consider caching DOM lookups for performance.
Repeated calls to document.getElementById() in each function can cause performance overhead. Caching these elements once or passing them as parameters can make the code more efficient and easier to maintain.


47-51: Confirm units for font sizes.
Using rem units for font sizes is standard, but verify if the rest of the codebase consistently applies rem. Mixed usage of px or em elsewhere might cause inconsistent sizing.


118-157: Minimize repetitive set function calls.
The setAllCssVars() function invokes a series of nearly identical setter functions. Consider grouping color or layout properties in loops, or using a more dynamic approach to reduce verbosity.

apps/docs/components/themes/templates/heroui.ts (1)

4-8: Consider clarity around config naming.
Exporting the theme config under the name heroui can be confusing if the broader codebase also references the brand or library as "HeroUI." Clarifying the naming or adding documentation might help avoid confusion.

apps/docs/app/themes/page.tsx (1)

3-9: Separate layout concerns from content.
Embedding utility classes directly within the component’s div is convenient, but it makes layout changes harder to maintain. Consider abstracting layout styles into a dedicated container component or a CSS/SCSS module.

apps/docs/components/icons/index.ts (1)

16-19: Consider organizing exports alphabetically.

While the new exports are valid, organizing them alphabetically would improve maintainability and make it easier to locate specific icons.

 export * from "./bug";
+export * from "./filters";
 export * from "./gaming-console";
 export * from "./linear";
 export * from "./magic";
 export * from "./mirror-left";
 export * from "./moon";
 export * from "./moon-filled";
 export * from "./palette";
+export * from "./palette-round";
+export * from "./scaling";
 export * from "./social";
 export * from "./star";
 export * from "./sun";
 export * from "./two-tone";
-export * from "./mirror-left";
-export * from "./palette-round";
-export * from "./filters";
-export * from "./scaling";
apps/docs/hooks/use-previous.ts (1)

3-8: Enhance JSDoc documentation.

The current documentation could be more descriptive and include an example.

 /**
- * Holds the previous value of the provided [value] parameter
+ * A React hook that stores and returns the previous value of a given state.
  *
- * @param value
- * @returns
+ * @param value - The current value to track
+ * @returns The previous value (undefined on first render)
+ * 
+ * @example
+ * ```tsx
+ * const [count, setCount] = useState(0);
+ * const previousCount = usePrevious(count);
+ * // If count is 5, previousCount will be 4
+ * ```
  */
apps/docs/components/themes/templates/index.ts (1)

7-11: Consider adding runtime type validation.

While TypeScript provides compile-time type safety, consider adding runtime validation for the template values to ensure they match the expected schema.

import {z} from 'zod';

const templateSchema = z.object({
  label: z.string(),
  name: z.string(),
  value: z.object({
    // Add specific theme configuration validation
  })
});

export const templates = [
  {label: "HeroUI", name: "heroui", value: heroui},
  {label: "Coffee", name: "coffee", value: coffee},
  {label: "Emerald", name: "emerald", value: emerald},
].map(template => templateSchema.parse(template));
apps/docs/components/themes/utils/shared.ts (1)

21-30: Simplify border width mapping using an object literal.

Consider using an object map for better maintainability and easier extension of border types.

+const BORDER_WIDTH_MAP = {
+  thin: 1,
+  medium: 2,
+  default: 4,
+} as const;
+
-export function getBorderWidth(data: Border) {
-  if (data === "thin") {
-    return 1;
-  }
-  if (data === "medium") {
-    return 2;
-  }
-
-  return 4;
+export function getBorderWidth(data: Border): number {
+  return BORDER_WIDTH_MAP[data] ?? BORDER_WIDTH_MAP.default;
}
apps/docs/components/themes/components/configuration/fonts.tsx (2)

13-16: Consider using an enum or constant for font options.

The font options ("inter", "roboto", "outfit", "lora") are hardcoded. Consider defining them as a typed constant or enum to improve maintainability and type safety.

const FONT_OPTIONS = {
  INTER: "inter",
  ROBOTO: "roboto",
  OUTFIT: "outfit",
  LORA: "lora"
} as const;

type FontOption = typeof FONT_OPTIONS[keyof typeof FONT_OPTIONS];

13-16: Clarify the purpose of varying border radius classes.

The rounded-tl-* classes follow an increasing pattern but the purpose isn't immediately clear. Consider adding a comment explaining the visual effect or extract to a constant with a descriptive name.

apps/docs/components/themes/components/showcase/index.tsx (2)

16-16: Consider making the min-width responsive.

The hardcoded min-w-6xl might cause horizontal scrolling on smaller screens. Consider using responsive classes or CSS variables for better adaptability.


18-27: Consider implementing lazy loading for showcase components.

With multiple UI components being rendered at once, consider using React.lazy() and Suspense for better initial load performance.

const Avatar = React.lazy(() => import("./avatar"));
const BreadCrumbs = React.lazy(() => import("./breadcrumbs"));
// ... other imports

return (
  <Suspense fallback={<div>Loading...</div>}>
    {/* components */}
  </Suspense>
);
apps/docs/components/themes/components/configuration/editable-button.tsx (1)

25-25: Use CSS variables for colors instead of hardcoded values.

The hardcoded color values (#0077ff1A, #92c5ff00) should use theme variables for consistency.

-"border-primary-500 bg-gradient-to-b from-[#0077ff1A] to-[#92c5ff00]"
+"border-primary-500 bg-gradient-to-b from-primary-100/10 to-transparent"
apps/docs/components/themes/components/config-section.tsx (1)

16-16: Use theme variables instead of hardcoded colors.

Replace hardcoded colors with theme variables for better dark mode support and consistency.

-"text-[#71717A] dark:text-[#A1A1AA]"
+"text-default-400 dark:text-default-500"
apps/docs/components/themes/components/configuration/default-colors.tsx (1)

15-32: Add loading state handling

The component should handle loading states when theme configuration is being fetched or updated.

Consider adding loading state handling:

 export function DefaultColors({config, theme}: DefaultColorsProp) {
-  const {setDefaultColor} = useThemeBuilder();
+  const {setDefaultColor, isLoading} = useThemeBuilder();

   return (
     <ConfigSection
       icon={<PaletteRound className="h-5 w-5" />}
       id={defaultColorsId}
       title="Default Colors"
+      disabled={isLoading}
     >
       <ColorPicker
         hexColor={config[theme].defaultColor.default}
         type="default"
         onChange={(hexColor) => setCssColor("default", hexColor, theme)}
         onClose={(hexColor) => setDefaultColor({default: hexColor}, theme, false)}
+        disabled={isLoading}
       />
     </ConfigSection>
   );
 }
apps/docs/components/themes/components/showcase-component.tsx (1)

26-37: Optimize performance with useMemo and add error boundary

The component should memoize the style object and handle font loading failures gracefully.

+import {useMemo} from 'react';
+import {ErrorBoundary} from 'react-error-boundary';

 export function ShowcaseComponent({children, id, name}: ShowcaseComponentProps) {
   const {font} = useThemeBuilder();
-  const style = getFontStyle(font);
+  const style = useMemo(() => getFontStyle(font), [font]);

   return (
+    <ErrorBoundary fallback={<div>Failed to load font. Using system font.</div>}>
       <div className="bg-background text-foreground py-6 p-4 rounded-lg" id={id} style={style}>
         <span className="text-xl font-medium text-default-800">{name}</span>
         <Divider className="mt-4 mb-6" />
         <div className="flex flex-wrap gap-6 mt-8">{children}</div>
       </div>
+    </ErrorBoundary>
   );
 }
apps/docs/components/theme-panel.tsx (2)

27-36: Extract theme toggle to a separate component

The theme toggle logic should be encapsulated in a dedicated component for better maintainability.

+const ThemeToggle = () => {
+  const {theme, setTheme} = useTheme();
+  return (
+    <Button
+      isIconOnly
+      variant="flat"
+      aria-label={`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`}
+      onPress={() => setTheme(theme === "dark" ? "light" : "dark")}
+    >
+      {theme === "dark" ? <SunIcon /> : <MoonIcon />}
+    </Button>
+  );
+};

-        <Button
-          isIconOnly
-          variant="flat"
-          onPress={() => {
-            theme === "dark" ? setTheme("light") : setTheme("dark");
-          }}
-        >
-          {theme == "dark" ? <SunIcon /> : <MoonIcon />}
-        </Button>
+        <ThemeToggle />

20-41: Add loading state feedback

Users should receive visual feedback while the theme is being loaded or changed.

   return (
     <div className="border p-4 col-span-1 h-[70vh] overflow-scroll">
       <h2 className="font-bold text-xl">Theme</h2>
       <div className="flex gap-x-2 my-2">
+        {!isMounted ? (
+          <div className="animate-pulse h-9 w-20 bg-gray-200 rounded" />
+        ) : (
           <Button isIconOnly variant="flat">
             <CopyIcon />
           </Button>
+        )}
         <ThemeToggle />
       </div>
       <div className="my-2">
         <h4 className="text-md">Color</h4>
       </div>
     </div>
   );
apps/docs/components/themes/components/configuration/radiuses.tsx (1)

13-42: Improve radius preview visualization.

Currently, the preview only shows the top-left radius (rounded-tl-*), which might not give users a complete understanding of how the radius will look when applied. Consider showing the radius on all corners.

 <EditableButton
-  className="rounded-tl-none"
+  className="rounded-none"
   setValue={setRadiusValue}
   title="none"
   value={radiusValue}
 />

Also, consider extracting the repeated button configurations to reduce duplication:

const radiusOptions = [
  { title: "none", className: "rounded-none" },
  { title: "sm", className: "rounded-sm" },
  { title: "md", className: "rounded-md" },
  { title: "lg", className: "rounded-lg" },
  { title: "full", className: "rounded-full" },
];
apps/docs/components/themes/components/configuration/font-sizes.tsx (1)

35-38: Optimize onChange handler to prevent unnecessary re-renders.

The current implementation creates a new function on every render. Consider memoizing the handler or moving it outside the component.

+const handleFontSizeChange = (type: keyof ConfigLayout["fontSize"]) => 
+  (value: string) => {
+    setFontSize({[type]: value});
+    setCssFontSize(type, value);
+  };

 <NumberInput
   label={label}
   value={value}
-  onChange={(value) => {
-    setFontSize({[type]: value});
-    setCssFontSize(type, value);
-  }}
+  onChange={handleFontSizeChange(type)}
 />
apps/docs/components/themes/templates/coffee.ts (2)

6-56: Document color usage and relationships.

Add JSDoc comments to explain the purpose and relationships between different color variables, making it easier for other developers to understand and maintain the theme.

+/**
+ * Coffee theme configuration
+ * 
+ * Color Relationships:
+ * - Primary: Used for main actions and highlights (#db924b)
+ * - Secondary: Used for supporting elements (#5a8486)
+ * - Content scales: Background surfaces from lightest to darkest
+ */
 export const coffee: Config = {

21-21: Standardize color value sources.

The theme inconsistently mixes imported colors (from @heroui/theme) with hardcoded values. Consider using the imported colors consistently where applicable.

-      overlay: colors.black,
+      overlay: colors.black[500],

Also applies to: 46-46

apps/docs/components/themes/components/configuration/scaling.tsx (1)

8-10: Add validation for scaling values.

Consider adding validation to ensure scaling values remain within acceptable range.

 export function Scaling() {
   const {scaling, setScaling} = useThemeBuilder();
+
+  const handleScaling = (value: number) => {
+    if (value >= 90 && value <= 110) {
+      setScaling(value);
+    }
+  };
apps/docs/components/themes/components/select-template.tsx (1)

37-45: Improve type safety in template mapping.

The value prop uses array index which could lead to issues if the templates array order changes.

-          value={index}
+          value={template.name}
apps/docs/components/themes/components/configuration/line-heights.tsx (2)

16-23: Reduce code duplication in onChange handlers

The onChange handlers for all four NumberInput components contain identical logic structure. Consider extracting this into a reusable function to improve maintainability.

 export function LineHeights({config}: LineHeightsProps) {
   const {setLineHeight} = useThemeBuilder();
+
+  const handleLineHeightChange = (size: 'tiny' | 'small' | 'medium' | 'large', value: number) => {
+    setLineHeight({[size]: value});
+    setCssLineHeight(size, value);
+  };
+
   return (
     <ConfigSection title="Line height (rem)">
       <NumberInput
         label="Tiny"
         value={config.layout.lineHeight.tiny}
-        onChange={(value) => {
-          setLineHeight({tiny: value});
-          setCssLineHeight("tiny", value);
-        }}
+        onChange={(value) => handleLineHeightChange('tiny', value)}
       />
       // Apply similar changes to other NumberInput components

Also applies to: 24-31, 32-39, 40-47


16-47: Add input validation and accessibility improvements

Consider the following enhancements:

  1. Add min/max constraints for line height values
  2. Include aria-label attributes for better screen reader support
  3. Add step size for number inputs
       <NumberInput
         label="Tiny"
         value={config.layout.lineHeight.tiny}
+        min={0.5}
+        max={3}
+        step={0.1}
+        aria-label="Set tiny line height"
         onChange={(value) => handleLineHeightChange('tiny', value)}
       />
apps/docs/components/icons/filters.tsx (2)

3-11: Improve icon accessibility and reusability

The icon component could benefit from the following improvements:

  1. Add aria-label for accessibility
  2. Make fill color configurable through props
-export const Filters = ({size = 24, width, height, ...props}: IconSvgProps) => (
+interface FiltersProps extends IconSvgProps {
+  fill?: string;
+}
+
+export const Filters = ({
+  size = 24,
+  width,
+  height,
+  fill = "#A1A1AA",
+  ...props
+}: FiltersProps) => (
   <svg
     focusable="false"
+    aria-label="Filters"
+    role="img"
     height={size || height}
     viewBox="0 0 20 20"
     width={size || width}
     xmlns="http://www.w3.org/2000/svg"
     {...props}
   >

12-17: Make fill color dynamic

Update the path to use the configurable fill color.

     <path
       clipRule="evenodd"
       d="M10.0003 2.29166C7.58408 2.29166 5.62533 4.25041..."
-      fill="#A1A1AA"
+      fill={fill}
       fillRule="evenodd"
     />
apps/docs/components/themes/components/configuration/disable-opacity.tsx (2)

24-51: Reduce duplication and improve type safety

The component has several areas for improvement:

  1. Repeated setValue handlers
  2. Hardcoded opacity values
  3. Missing type safety
+const OPACITY_VALUES = ['0.2', '0.4', '0.6', '0.8'] as const;
+type OpacityValue = typeof OPACITY_VALUES[number];
+
 export function DisableOpacity({config}: DisableOpacityProps) {
   const {setOtherParams} = useThemeBuilder();

-  const handleChange = (key: keyof Config["layout"]["otherParams"], value: string) => {
+  const handleChange = (key: keyof Config["layout"]["otherParams"], value: OpacityValue) => {
     setOtherParams({[key]: value});
     setOtherCssParams(key, value);
   };

   return (
     <ConfigSection icon={<RadialBlur className="h-5 w-5" />} title="Disable Opacity">
-      <ValueButton
-        currentValue={config.layout.otherParams.disabledOpacity}
-        setValue={(value) => {
-          handleChange("disabledOpacity", value);
-        }}
-        value={"0.2"}
-      />
-      // ... repeat for other values
+      {OPACITY_VALUES.map((value) => (
+        <ValueButton
+          key={value}
+          currentValue={config.layout.otherParams.disabledOpacity}
+          setValue={(val) => handleChange("disabledOpacity", val as OpacityValue)}
+          value={value}
+        />
+      ))}
     </ConfigSection>
   );
 }

23-23: Add tooltip to explain opacity impact

Consider adding a tooltip to explain how the disabled opacity affects components.

-    <ConfigSection icon={<RadialBlur className="h-5 w-5" />} title="Disable Opacity">
+    <ConfigSection 
+      icon={<RadialBlur className="h-5 w-5" />} 
+      title="Disable Opacity"
+      tooltip="Controls the opacity of disabled components. Higher values make disabled elements more visible."
+    >
apps/docs/components/icons/palette-round.tsx (2)

3-11: Apply same improvements as Filters icon

For consistency with the Filters icon, apply the same improvements:

  1. Add accessibility attributes
  2. Make fill color configurable
-export const PaletteRound = ({size = 24, width, height, ...props}: IconSvgProps) => (
+interface PaletteRoundProps extends IconSvgProps {
+  fill?: string;
+}
+
+export const PaletteRound = ({
+  size = 24,
+  width,
+  height,
+  fill = "#A1A1AA",
+  ...props
+}: PaletteRoundProps) => (
   <svg
     focusable="false"
+    aria-label="Color palette"
+    role="img"
     height={size || height}
     viewBox="0 0 20 20"
     width={size || width}
     xmlns="http://www.w3.org/2000/svg"
     {...props}
   >

12-17: Make fill color dynamic

Update the path to use the configurable fill color.

     <path
       clipRule="evenodd"
       d="M1.04199 4.99999C2.8142 1.04166..."
-      fill="#A1A1AA"
+      fill={fill}
       fillRule="evenodd"
     />
apps/docs/components/themes/components/copy-button.tsx (1)

43-57: Enhance accessibility for dropdown menu items

Add aria-label to dropdown items to improve screen reader experience.

       <DropdownMenu aria-label="Copy configuration">
         <DropdownItem
           key="light"
+          aria-label="Copy light theme configuration"
           startContent={<Icon className="text-lg" icon={SunIcon} />}
           onPress={() => handleCopy("light")}
         >
           Light config
         </DropdownItem>
         <DropdownItem
           key="dark"
+          aria-label="Copy dark theme configuration"
           startContent={<Icon className="text-lg" icon={MoonIcon} />}
           onPress={() => handleCopy("dark")}
         >
           Dark config
         </DropdownItem>
apps/docs/components/themes/components/configuration/content-colors.tsx (1)

25-48: Reduce code duplication in ColorPicker implementations

The ColorPicker components share similar patterns and could be refactored to reduce duplication.

+  const contentColors = [
+    { type: "content1", index: 1 },
+    { type: "content2", index: 2 },
+    { type: "content3", index: 3 },
+    { type: "content4", index: 4 },
+  ];
+
   return (
     <ConfigSection
       icon={<PaletteRound className="w-5 h-5" />}
       id={baseColorsId}
       title="Content colors"
       toolTip="content1, content2, content3, content4 colors"
     >
-      <ColorPicker
-        hexColor={config[theme].contentColor.content1}
-        type="content1"
-        onChange={(hexColor) => setCssContentColor(1, hexColor)}
-        onClose={(hexColor) => setConentColor({content1: hexColor}, theme)}
-      />
-      // ... repeated ColorPicker components
+      {contentColors.map(({ type, index }) => (
+        <ColorPicker
+          key={type}
+          hexColor={config[theme].contentColor[type]}
+          type={type}
+          onChange={(hexColor) => setCssContentColor(index, hexColor)}
+          onClose={(hexColor) => setContentColor({[type]: hexColor}, theme)}
+        />
+      ))}
     </ConfigSection>
   );
apps/docs/components/themes/components/showcase/code.tsx (2)

38-59: Refactor scaling switch statement to use a lookup object

The switch statement contains magic numbers and repetitive patterns. Consider using a lookup object for better maintainability.

-  switch (scaling) {
-    case 90: {
-      className = "p-2 px-3 text-tiny";
-      break;
-    }
-    // ... other cases
-  }
+  const scalingClasses = {
+    90: "p-2 px-3 text-tiny",
+    95: "p-2 px-4 text-tiny",
+    100: "p-2 px-4 text-medium",
+    105: "p-3 px-5 text-medium",
+    110: "p-2 px-8 text-medium",
+  };
+  className = scalingClasses[scaling] ?? className;

68-79: Optimize component performance with memoization

The component could benefit from memoization to prevent unnecessary re-renders.

-export const Code = () => {
+import {memo, useMemo} from "react";
+
+export const Code = memo(() => {
   const colors: Color[] = ["default", "primary", "secondary", "success", "warning", "danger"];
   const {radiusValue, scaling} = useThemeBuilder();
 
+  const sections = useMemo(() => 
+    colors.map((color, idx) => (
+      <Section key={idx} color={color} radius={radiusValue} scaling={scaling} />
+    )), [colors, radiusValue, scaling]);
+
   return (
     <ShowcaseComponent name="Code">
-      {colors.map((color, idx) => (
-        <Section key={idx} color={color} radius={radiusValue} scaling={scaling} />
-      ))}
+      {sections}
     </ShowcaseComponent>
   );
-};
+});
+
+Code.displayName = "Code";
apps/docs/components/themes/components/configuration/layout-colors.tsx (1)

16-52: Reduce code duplication in ColorPicker implementations

Similar to ContentColors component, the ColorPicker implementations could be refactored to reduce duplication.

+  const layoutColors = [
+    { type: "background", key: "background" },
+    { type: "foreground", key: "foreground" },
+    { type: "focus", key: "focus" },
+    { type: "overlay", key: "overlay" },
+  ];
+
   return (
     <ConfigSection
       icon={<PaletteIcon className="w-5 h-5" />}
       id={otherColorsId}
       title="Layout colors"
       toolTip="background, foreground, focus, overlay colors"
     >
-      <ColorPicker
-        hexColor={config[theme].layoutColor.background}
-        type="background"
-        onChange={(hexColor) => setCssOtherColor("background", hexColor)}
-        onClose={(hexColor) => setLayoutColor({background: hexColor}, theme, syncThemes)}
-      />
-      // ... repeated ColorPicker components
+      {layoutColors.map(({ type, key }) => (
+        <ColorPicker
+          key={type}
+          hexColor={config[theme].layoutColor[key]}
+          type={type}
+          onChange={(hexColor) => setCssOtherColor(key, hexColor)}
+          onClose={(hexColor) => setLayoutColor({[key]: hexColor}, theme, syncThemes)}
+        />
+      ))}
     </ConfigSection>
   );
apps/docs/components/themes/components/configuration/base-colors.tsx (1)

27-32: Consider extracting color configuration to reduce repetition.

The ColorPicker components follow the same pattern with different color types. This could be refactored to reduce code duplication.

-      <ColorPicker
-        hexColor={config[theme].baseColor.primary}
-        type="primary"
-        onChange={(hexColor) => setCssColor("primary", hexColor, theme)}
-        onClose={(hexColor) => setBaseColor({primary: hexColor}, theme, syncThemes)}
-      />
-      <ColorPicker
-        hexColor={config[theme].baseColor.secondary}
-        type="secondary"
-        onChange={(hexColor) => setCssColor("secondary", hexColor, theme)}
-        onClose={(hexColor) => setBaseColor({secondary: hexColor}, theme, syncThemes)}
-      />
-      // ... more ColorPicker components
+      {['primary', 'secondary', 'success', 'warning', 'danger'].map((colorType) => (
+        <ColorPicker
+          key={colorType}
+          hexColor={config[theme].baseColor[colorType]}
+          type={colorType}
+          onChange={(hexColor) => setCssColor(colorType, hexColor, theme)}
+          onClose={(hexColor) => setBaseColor({[colorType]: hexColor}, theme, syncThemes)}
+        />
+      ))}

Also applies to: 33-38, 39-44, 45-50, 51-56

apps/docs/components/themes/utils/config.ts (1)

13-42: Consider memoizing theme color generation.

The color generation could be expensive when called frequently. Consider memoizing the results for performance optimization.

+const memoizedThemeColors = new Map();
+
 function generateThemeColorsConfig(config: Config, theme: ThemeType) {
+  const key = `${theme}-${JSON.stringify(config[theme])}`;
+  if (memoizedThemeColors.has(key)) {
+    return memoizedThemeColors.get(key);
+  }
+
   const colors = {
     default: generateThemeColor(config[theme].defaultColor.default, "default", "light"),
     // ... rest of the colors
   };
+  memoizedThemeColors.set(key, colors);
+  return colors;
 }
apps/docs/components/themes/components/showcase/popover.tsx (1)

44-67: Consider using a lookup object for scaling classes.

The switch statement for scaling classes could be simplified using a lookup object.

-  switch (scaling) {
-    case 90: {
-      className = "px-1 py-2 text-tiny";
-      break;
-    }
-    // ... more cases
-  }
+  const scalingClasses = {
+    90: "px-1 py-2 text-tiny",
+    95: "text-tiny px-2 py-3",
+    100: "text-small px-2 py-3",
+    105: "text-small px-3 py-3",
+    110: "text-medium px-3 py-3",
+  };
+  className = scalingClasses[scaling] ?? "text-small px-1";
apps/docs/components/themes/components/showcase/switch.tsx (1)

76-77: Consider using map for disabled states.

Similar to the Popover component, avoid using cloneElement with static elements and consider using map for the disabled states.

-      {cloneElement(<SectionBase />, {color, classNames, isDisabled: false})}
-      {cloneElement(<SectionBase />, {color, classNames, isDisabled: true})}
+      {[false, true].map((isDisabled) => (
+        <SectionBase
+          key={isDisabled.toString()}
+          color={color}
+          classNames={classNames}
+          isDisabled={isDisabled}
+        />
+      ))}
apps/docs/components/themes/components/showcase/checkbox.tsx (3)

20-20: Replace any type with a more specific type definition.

Using any reduces type safety. Consider defining a proper interface for the classNames object structure.

-  classNames?: any;
+  classNames?: {
+    wrapper: string;
+    icon: string;
+    label: string;
+  };

31-31: Enhance accessibility by using meaningful labels.

Using color values as labels may not be user-friendly or accessible. Consider using descriptive labels that convey the purpose of each checkbox.

-      {color}
+      {`${color.charAt(0).toUpperCase()}${color.slice(1)} Option`}

51-92: Optimize the scaling logic by using a lookup object.

The switch statement with repeated structures can be simplified using a lookup object, making the code more maintainable and reducing duplication.

-  switch (scaling) {
-    case 90: {
-      classNames = {
-        wrapper: "h-5 w-5",
-        icon: "w-4 h-3",
-        label: "text-small",
-      };
-      break;
-    }
-    // ... other cases
-  }
+  const scalingClassNames = {
+    90: {
+      wrapper: "h-5 w-5",
+      icon: "w-4 h-3",
+      label: "text-small",
+    },
+    95: {
+      wrapper: "h-5 w-5",
+      icon: "w-4 h-3",
+      label: "text-medium",
+    },
+    // ... other scales
+  };
+  classNames = scalingClassNames[scaling] ?? classNames;
apps/docs/components/themes/types.ts (2)

2-48: Add JSDoc comments to improve type documentation.

While the types are well-structured, adding JSDoc comments would improve code documentation and IDE support.

+/** Defines the available color shades from 50 to 900 */
 export interface ColorShades {
   // ...
 }

+/** Defines the available color picker types for theme customization */
 export type ColorPickerType =
   | "background"
   // ...

89-117: Add value constraints to configuration types.

Consider adding string literal types or number ranges for configuration values to ensure valid inputs.

 export interface ConfigLayout {
   fontSize: {
-    tiny: string;
+    tiny: `${number}px` | `${number}rem`;
     // ... similar for other size properties
   };
   otherParams: {
-    disabledOpacity: string;
+    disabledOpacity: `${number}%` | number;
     // ... similar for other numeric parameters
   };
 }
apps/docs/components/themes/components/showcase/avatar.tsx (2)

32-32: Consider making the avatar image URL configurable.

Hardcoding the avatar image URL reduces component reusability. Consider making it a prop or using a configuration value.

-      src="https://i.pravatar.cc/150?u=a04258114e29026708c"
+      src={props.imageUrl || "https://i.pravatar.cc/150?u=a04258114e29026708c"}

51-52: Improve border width class name generation.

The current implementation might not handle all edge cases cleanly. Consider using a more robust approach.

-  const borderClassName = border <= 2 ? `ring-${border} ring-offset-2` : `ring ring-offset-2`;
+  const getBorderClass = (width: number) => {
+    if (width <= 0) return '';
+    if (width <= 2) return `ring-${width} ring-offset-2`;
+    return 'ring ring-offset-2';
+  };
+  const borderClassName = getBorderClass(border);
apps/docs/components/icons/scaling.tsx (1)

3-21: Enhance icon accessibility with proper ARIA attributes.

While the component includes basic ARIA attributes, it could benefit from additional accessibility improvements.

 export const Scaling = ({size = 24, width, height, ...props}: IconSvgProps) => (
   <svg
     aria-hidden="true"
+    aria-label="Scaling icon"
     fill="none"
     focusable="false"
+    xmlns="http://www.w3.org/2000/svg"
     height={size || height}
     role="presentation"
     viewBox="0 0 24 24"
     width={size || width}
     {...props}
   >
apps/docs/components/themes/constants.ts (2)

5-11: Enhance regex pattern and add documentation for color weights.

  1. The float number regex pattern could be improved to:
  • Prevent multiple decimal points
  • Handle negative numbers
  • Limit decimal places
  1. Consider adding JSDoc comments to explain the purpose and usage of color weights.
 // Colors
+/** Weight factor used for dark theme color calculations */
 export const defaultDarkColorWeight = 20;
+/** Weight factor used for light theme color calculations */
 export const defaultLightColorWeight = 17.5;
+/** Default color weight for theme calculations */
 export const colorWeight = 17.5;

 // Regex
+/** Validates float numbers with optional decimal places */
-export const floatNumberPattern = /^\d+(\.\d*)?$/;
+export const floatNumberPattern = /^-?\d+(\.\d{0,2})?$/;

26-75: LGTM! Well-structured theme configurations.

The light and dark theme configurations are well-organized and maintain symmetry. The content colors follow a logical progression using the zinc color scale.

Consider extracting the common base colors into a shared constant since they're identical in both themes.

+const baseColors = {
+  primary: colors.blue[500],
+  secondary: colors.purple[500],
+  success: colors.green[500],
+  warning: colors.yellow[500],
+  danger: colors.red[500],
+};

 export const initialLightTheme: ConfigColors = {
   defaultColor: {
     default: "#d4d4d8",
   },
-  baseColor: {
-    primary: colors.blue[500],
-    secondary: colors.purple[500],
-    success: colors.green[500],
-    warning: colors.yellow[500],
-    danger: colors.red[500],
-  },
+  baseColor: baseColors,
   // ... rest of the config
 };
apps/docs/components/themes/components/showcase/breadcrumbs.tsx (1)

42-96: Refactor scaling logic to reduce repetition and improve maintainability.

The switch statement contains magic numbers and repeated patterns for text classes.

+const SCALE_CLASSES = {
+  90: { base: "text-[0.7rem]" },
+  95: { base: "text-tiny" },
+  100: { base: "text-small p-0.5" },
+  105: { base: "text-medium p-1" },
+  110: { base: "text-large p-1.5" },
+} as const;

 const Section = ({/* ... */}) => {
   // ...
-  switch (scaling) {
-    case 90: {
-      classNames = {base: "text-[0.7rem]"};
-      break;
-    }
-    case 95: {
-      classNames = {base: "text-tiny"};
-      break;
-    }
-    // ... more cases
-  }
+  classNames = SCALE_CLASSES[scaling as keyof typeof SCALE_CLASSES] ?? SCALE_CLASSES[100];
apps/docs/components/themes/components/showcase/chip.tsx (2)

13-38: Remove unnecessary key prop and make content configurable.

  1. The key prop on HeroUIChip is unnecessary as it's not in an array context.
  2. The chip content is currently limited to the color name.
 const SectionBase = ({
   color,
   isDisabled,
   radius,
   variant,
   className,
+  children,
 }: {
   color?: Color;
   isDisabled?: boolean;
   radius?: Radius;
   variant?: Variant;
   className?: string;
+  children?: React.ReactNode;
 }) => {
   return (
     <HeroUIChip
-      key={radius}
       className={className}
       color={color}
       isDisabled={isDisabled}
       radius={radius}
       variant={variant}
     >
-      {color}
+      {children ?? color}
     </HeroUIChip>
   );
 };

40-109: Optimize performance and simplify className logic.

Consider these improvements:

  1. Memoize variant elements to prevent unnecessary re-renders
  2. Simplify border class logic using an object map
+import {useMemo} from "react";

+const BORDER_CLASSES = {
+  thin: "border-small",
+  medium: "border-medium",
+  thick: "border-large",
+} as const;

 const Section = ({/* ... */}) => {
-  let borderClass = "border-medium";
-
-  if (borderWidthValue === "thin") {
-    borderClass = "border-small";
-  } else if (borderWidthValue === "thick") {
-    borderClass = "border-large";
-  }
+  const borderClass = BORDER_CLASSES[borderWidthValue];

+  const variantElements = useMemo(
+    () =>
+      variants.map((variant, idx) =>
+        cloneElement(<SectionBase key={idx} />, {
+          color,
+          className: clsx(
+            className,
+            variant === "bordered" || variant === "faded" ? borderClass : "",
+          ),
+          variant,
+          isDisabled: false,
+          radius,
+        }),
+      ),
+    [variants, color, className, borderClass, radius]
+  );

   return (
     <div key={color} className="flex flex-col gap-y-4">
-      {variants.map((variant, idx) =>
-        cloneElement(<SectionBase key={idx} />, {
-          // ...
-        }),
-      )}
+      {variantElements}
       {/* ... */}
     </div>
   );
 };
apps/docs/components/themes/components/showcase/input.tsx (1)

39-102: Refactor scaling logic and simplify className handling.

The component has similar scaling patterns as other showcase components and complex className handling.

+const SCALE_CLASSES = {
+  90: { base: "h-8 w-[300px]", label: "text-tiny" },
+  95: { base: "h-8 w-[320px]", label: "text-tiny" },
+  100: { base: "h-10 w-[340px]", label: "text-small" },
+  105: { base: "h-12 w-[360px]", label: "text-medium" },
+  110: { base: "h-12 w-[380px]", label: "text-medium" },
+} as const;

 const Section = ({/* ... */}) => {
   const variants = ["flat", "bordered", "faded", "underlined"];
-  let classNames = {base: "h-10 w-[340px]", label: "text-small"};
+  const classNames = SCALE_CLASSES[scaling as keyof typeof SCALE_CLASSES] ?? SCALE_CLASSES[100];

   const border = getBorderWidth(borderWidthValue);
-  const borderClassName = `border-${border}`;
+  const getBorderClass = (variant: string, color: Color) =>
+    variant === "bordered" ? `border-${border} border-${color}` : "";

-  switch (scaling) {
-    case 90: {
-      classNames = {base: "h-8 w-[300px]", label: "text-tiny"};
-      break;
-    }
-    // ... remove switch statement
-  }

   return (
     <div key={color} className="flex flex-col gap-y-4">
       {variants.map((variant, idx) =>
         cloneElement(<SectionBase key={idx} />, {
           color,
           variant,
           classNames: {
             ...classNames,
-            inputWrapper: clsx(variant === "bordered" ? `${borderClassName} border-${color}` : ""),
+            inputWrapper: clsx(getBorderClass(variant, color)),
           },
           isDisabled: false,
           radius,
         }),
       )}
       {/* ... */}
     </div>
   );
 };
apps/docs/components/themes/components/showcase/button.tsx (2)

13-38: Consider adding prop validation for better type safety.

While the component works correctly, consider adding runtime validation for the color prop to ensure it matches the allowed values.

+const isValidColor = (color?: Color): boolean => {
+  const validColors = ["default", "primary", "secondary", "success", "warning", "danger"];
+  return color === undefined || validColors.includes(color);
+};

 const SectionBase = ({
   color,
   radius,
   isDisabled,
   variant,
   className,
 }: {
   color?: Color;
   radius?: Radius;
   isDisabled?: boolean;
   variant?: Variant;
   className?: string;
 }) => {
+  if (color && !isValidColor(color)) {
+    console.warn(`Invalid color prop: ${color}`);
+  }
   return (
     <HeroUIButton

110-127: Consider memoizing for better performance.

Since the colors array is static, it can be moved outside the component. Also, consider using useMemo for the mapped sections if they're expensive to render.

+const BUTTON_COLORS: Color[] = ["default", "primary", "secondary", "success", "warning", "danger"];

 export const Button = () => {
-  const colors: Color[] = ["default", "primary", "secondary", "success", "warning", "danger"];
   const {radiusValue, scaling, borderWidthValue} = useThemeBuilder();

+  const sections = useMemo(
+    () =>
+      BUTTON_COLORS.map((color, idx) => (
+        <Section
+          key={idx}
+          borderWidthValue={borderWidthValue}
+          color={color}
+          radius={radiusValue}
+          scaling={scaling}
+        />
+      )),
+    [borderWidthValue, radiusValue, scaling]
+  );

   return (
     <ShowcaseComponent name="Button">
-      {colors.map((color, idx) => (
-        <Section
-          key={idx}
-          borderWidthValue={borderWidthValue}
-          color={color}
-          radius={radiusValue}
-          scaling={scaling}
-        />
-      ))}
+      {sections}
     </ShowcaseComponent>
   );
 };
apps/docs/components/themes/components/showcase/tabs.tsx (2)

1-11: Enhance type safety for scaling values.

Consider defining a union type for the scaling values to prevent invalid numbers.

-import {Border, HeroUIScaling} from "../../types";
+import {Border} from "../../types";
+type HeroUIScaling = 90 | 95 | 100 | 105 | 110;

113-130: Extract shared constants to reduce duplication.

Consider moving the colors array to a shared constants file since it's used in both Button and Tabs components.

+// In shared/constants.ts
+export const COMPONENT_COLORS = ["default", "primary", "secondary", "success", "warning", "danger"] as const;
+export type ComponentColor = typeof COMPONENT_COLORS[number];

-export const TabsComponent = () => {
-  const colors: Color[] = ["default", "primary", "secondary", "success", "warning", "danger"];
+export const TabsComponent = () => {
+  const colors = COMPONENT_COLORS;
apps/docs/components/icons/text-square.tsx (1)

1-23: Make icon colors customizable.

Consider making the fill colors customizable through props instead of hardcoding them.

-export const TextSquare = ({size = 24, width, height, ...props}: IconSvgProps) => (
+export const TextSquare = ({
+  size = 24,
+  width,
+  height,
+  fill = "#A1A1AA",
+  ...props
+}: IconSvgProps & {fill?: string}) => (
   <svg
     focusable="false"
     height={size || height}
     viewBox="0 0 20 20"
     width={size || width}
     xmlns="http://www.w3.org/2000/svg"
     {...props}
   >
     <path
       d="M7.29363 4.20835C6.93542..."
-      fill="#A1A1AA"
+      fill={fill}
     />
     <path
       clipRule="evenodd"
       d="M8.95251 0.0416872C7.02885..."
-      fill="#A1A1AA"
+      fill={fill}
       fillRule="evenodd"
     />
   </svg>
 );
apps/docs/components/icons/mirror-left.tsx (1)

1-25: Standardize icon color handling.

  1. Make colors customizable through props
  2. Use consistent colors across paths
-export const MirrorLeft = ({size = 24, width, height, ...props}: IconSvgProps) => (
+export const MirrorLeft = ({
+  size = 24,
+  width,
+  height,
+  primaryFill = "#71717A",
+  secondaryFill = "#A1A1AA",
+  ...props
+}: IconSvgProps & {primaryFill?: string; secondaryFill?: string}) => (
   <svg
     focusable="false"
     height={size || height}
     viewBox="0 0 20 20"
     width={size || width}
     xmlns="http://www.w3.org/2000/svg"
     {...props}
   >
     <path
       clipRule="evenodd"
       d="M11.1174 2.50001C11.1174..."
-      fill="#71717A"
+      fill={primaryFill}
       fillRule="evenodd"
     />
     <path
       clipRule="evenodd"
       d="M10 1.04167C10.3452..."
-      fill="#A1A1AA"
+      fill={secondaryFill}
       fillRule="evenodd"
     />
   </svg>
 );
apps/docs/components/themes/utils/colors.ts (3)

18-37: Consider simplifying the reduce operation.

The reduce operation could be made more readable using Object.fromEntries and map.

-  let shades = colorValues.slice(0, colorValues.length - 1).reduce((acc, shadeValue, index) => {
-    (acc as any)[index === 0 ? 50 : index * 100] = rgbToHex(shadeValue.rgb);
-
-    return acc;
-  }, {} as ColorShades);
+  const shades = Object.fromEntries(
+    colorValues
+      .slice(0, -1)
+      .map((shadeValue, index) => [index === 0 ? 50 : index * 100, rgbToHex(shadeValue.rgb)])
+  ) as ColorShades;

42-87: Consider using color2k for color conversion.

Since the project already uses color2k (imported for readableColor), consider using it for color conversion to reduce complexity and potential bugs.

-export function hexToHsl(hex: string) {
-  // Convert hex to RGB first
-  const [r, g, b] = hexToRgb(hex);
-
-  // Normalize RGB values
-  const normalizedR = r / 255;
-  const normalizedG = g / 255;
-  const normalizedB = b / 255;
-
-  // Find the maximum and minimum values of R, G, B
-  const max = Math.max(normalizedR, normalizedG, normalizedB);
-  const min = Math.min(normalizedR, normalizedG, normalizedB);
-
-  // Calculate the lightness
-  const lightness = (max + min) / 2;
-
-  // If the maximum and minimum are equal, there is no saturation
-  if (max === min) {
-    return `${0} ${0}% ${lightness * 100}%`;
-  }
-
-  // Calculate the saturation
-  let saturation = 0;
-
-  if (lightness < 0.5) {
-    saturation = (max - min) / (max + min);
-  } else {
-    saturation = (max - min) / (2 - max - min);
-  }
-
-  // Calculate the hue
-  let hue;
-
-  if (max === normalizedR) {
-    hue = (normalizedG - normalizedB) / (max - min);
-  } else if (max === normalizedG) {
-    hue = 2 + (normalizedB - normalizedR) / (max - min);
-  } else {
-    hue = 4 + (normalizedR - normalizedG) / (max - min);
-  }
-
-  hue *= 60;
-  if (hue < 0) hue += 360;
-
-  return `${hue.toFixed(2)} ${(saturation * 100).toFixed(2)}% ${(lightness * 100).toFixed(2)}%`;
-}
+import {toHsl} from "color2k";
+
+export function hexToHsl(hex: string) {
+  const {h, s, l} = toHsl(hex);
+  return `${h} ${s}% ${l}%`;
+}

103-107: Consider using padStart for cleaner code.

The hex padding can be simplified using the padStart method.

-function rgbValueToHex(c: number) {
-  const hex = c.toString(16);
-
-  return hex.length == 1 ? "0" + hex : hex;
-}
+function rgbValueToHex(c: number) {
+  return c.toString(16).padStart(2, "0");
+}
apps/docs/components/themes/components/color-picker.tsx (1)

88-123: Consider using a map for color type to class mapping.

The switch statement could be replaced with a map for better maintainability and performance.

+const colorTypeToClass = new Map([
+  ["primary", "bg-primary text-primary-foreground"],
+  ["secondary", "bg-secondary text-secondary-foreground"],
+  ["success", "bg-success text-success-foreground"],
+  ["warning", "bg-warning text-warning-foreground"],
+  ["danger", "bg-danger text-danger-foreground"],
+  ["background", "bg-background text-foreground"],
+  ["foreground", "bg-foreground text-black"],
+  ["default", "bg-default"],
+  ["content1", "bg-content1 text-content1-foreground"],
+  ["content2", "bg-content2 text-content2-foreground"],
+  ["content3", "bg-content3 text-content3-foreground"],
+  ["content4", "bg-content4 text-content4-foreground"],
+  ["divider", "bg-divider"],
+  ["focus", "bg-focus"],
+  ["overlay", "bg-overlay"],
+]);
+
 function getColor(type: ColorPickerType) {
-  switch (type) {
-    case "primary":
-      return "bg-primary text-primary-foreground";
-    case "secondary":
-      return "bg-secondary text-secondary-foreground";
-    case "success":
-      return "bg-success text-success-foreground";
-    case "warning":
-      return "bg-warning text-warning-foreground";
-    case "danger":
-      return "bg-danger text-danger-foreground";
-    case "background":
-      return "bg-background text-foreground";
-    case "foreground":
-      return "bg-foreground text-black";
-    case "default":
-      return "bg-default";
-    case "content1":
-      return "bg-content1 text-content1-foreground";
-    case "content2":
-      return "bg-content2 text-content2-foreground";
-    case "content3":
-      return "bg-content3 text-content3-foreground";
-    case "content4":
-      return "bg-content4 text-content4-foreground";
-    case "divider":
-      return "bg-divider";
-    case "focus":
-      return "bg-focus";
-    case "overlay":
-      return "bg-overlay";
-    default:
-      return undefined;
-  }
+  return colorTypeToClass.get(type);
 }
apps/docs/components/themes/provider.tsx (1)

83-91: Consider memoizing the reset function.

The resetConfig function is recreated on every render but its logic remains constant.

+import {useCallback} from "react";

-  const resetConfig = (theme: ThemeType, sync: boolean) => {
+  const resetConfig = useCallback((theme: ThemeType, sync: boolean) => {
     let newConfig = initialConfig;
     // ... rest of the function
     return newConfig;
-  };
+  }, []);
apps/docs/components/navbar.tsx (1)

336-346: Consider pluralizing the navigation label.

The navigation item's label "Theme" might be better as "Themes" since it leads to a theme generator with multiple theme options.

-              Theme
+              Themes
apps/docs/components/themes/components/configuration/index.tsx (3)

65-75: Consider using a debounce for local storage updates.

The effect that updates local storage on every config change might cause performance issues with rapid changes.

+import {debounce} from "lodash";

+const debouncedSetLsConfig = debounce((config: Config) => {
+  setLsConfig(config);
+}, 300);

 useEffect(() => {
   if (prevTheme !== theme) {
     setAllCssVars(config, theme);
   }

   if (prevTheme === theme) {
-    setLsConfig(config);
+    debouncedSetLsConfig(config);
   }
 }, [config, theme, prevTheme]);

227-290: Remove unnecessary Fragment.

The Fragment wrapper is redundant as it contains only one child element.

-                    <>
                      <div className="w-full grid grid-cols-4 gap-4 flex-wrap items-center justify-around pt-20">
                        {/* ... content ... */}
                      </div>
-                    </>
🧰 Tools
🪛 Biome (1.9.4)

[error] 227-290: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


275-275: Use appropriate icon for font settings.

The RadialBlur icon is reused for the font settings, which might not be the most appropriate icon for this context.

Consider using a more appropriate icon that represents typography or fonts.

apps/docs/package.json (1)

105-106: Standardize version constraints for new dependencies.

The new dependencies use inconsistent version constraints - react-colorful uses ^ while values.js uses an exact version.

Apply this diff to standardize:

-    "react-colorful": "^5.6.1",
-    "values.js": "^2.1.1"
+    "react-colorful": "5.6.1",
+    "values.js": "2.1.1"
apps/docs/tailwind.config.js (1)

348-355: Consider a more descriptive utility class name.

While .scrollbar-hide works, a more descriptive name like .no-scrollbar or .hide-scrollbar would better align with Tailwind's naming conventions.

Apply this diff to improve clarity:

       utilities: {
-        '.scrollbar-hide': {
+        '.hide-scrollbar': {
           'scrollbar-width': 'none',
           '&::-webkit-scrollbar': {
             display: 'none',
           },
         }
       },
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cddba82 and 368ff19.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (61)
  • apps/docs/app/themes/page.tsx (1 hunks)
  • apps/docs/components/icons/crop.tsx (1 hunks)
  • apps/docs/components/icons/filters.tsx (1 hunks)
  • apps/docs/components/icons/index.ts (1 hunks)
  • apps/docs/components/icons/mirror-left.tsx (1 hunks)
  • apps/docs/components/icons/palette-round.tsx (1 hunks)
  • apps/docs/components/icons/radial-blur.tsx (1 hunks)
  • apps/docs/components/icons/scaling.tsx (1 hunks)
  • apps/docs/components/icons/text-square.tsx (1 hunks)
  • apps/docs/components/navbar.tsx (1 hunks)
  • apps/docs/components/theme-panel.tsx (1 hunks)
  • apps/docs/components/themes/components/color-picker.tsx (1 hunks)
  • apps/docs/components/themes/components/config-section.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/actions.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/base-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/border-widths.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/content-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/default-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/disable-opacity.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/editable-button.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/font-button.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/font-sizes.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/fonts.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/index.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/layout-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/line-heights.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/other.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/radiuses.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/scaling.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/swatch.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/value-button.tsx (1 hunks)
  • apps/docs/components/themes/components/copy-button.tsx (1 hunks)
  • apps/docs/components/themes/components/number-input.tsx (1 hunks)
  • apps/docs/components/themes/components/select-template.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase-component.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/avatar.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/breadcrumbs.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/button.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/checkbox.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/chip.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/code.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/index.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/input.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/popover.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/switch.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/tabs.tsx (1 hunks)
  • apps/docs/components/themes/constants.ts (1 hunks)
  • apps/docs/components/themes/css-vars.ts (1 hunks)
  • apps/docs/components/themes/index.tsx (1 hunks)
  • apps/docs/components/themes/provider.tsx (1 hunks)
  • apps/docs/components/themes/templates/coffee.ts (1 hunks)
  • apps/docs/components/themes/templates/emerald.ts (1 hunks)
  • apps/docs/components/themes/templates/heroui.ts (1 hunks)
  • apps/docs/components/themes/templates/index.ts (1 hunks)
  • apps/docs/components/themes/types.ts (1 hunks)
  • apps/docs/components/themes/utils/colors.ts (1 hunks)
  • apps/docs/components/themes/utils/config.ts (1 hunks)
  • apps/docs/components/themes/utils/shared.ts (1 hunks)
  • apps/docs/hooks/use-previous.ts (1 hunks)
  • apps/docs/package.json (1 hunks)
  • apps/docs/tailwind.config.js (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
apps/docs/components/themes/components/configuration/index.tsx

[error] 227-290: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build
🔇 Additional comments (26)
apps/docs/components/themes/css-vars.ts (2)

21-35: Verify consistency when setting default color variations.
Inside the else block, defaultColorEl is set without appending the -${key} suffix. This might inadvertently override the base property if each color variation gets mapped to the same CSS variable.


71-86: Safeguard edge cases for color readability.
readableColor() might produce undesirable outcomes for unusually bright or dark colors. Consider testing extreme inputs or providing fallbacks for high contrast.

apps/docs/hooks/use-previous.ts (1)

9-17: LGTM! Well-implemented custom hook.

The implementation follows React best practices and uses generics effectively.

apps/docs/components/themes/index.tsx (1)

9-22: LGTM! Well-implemented hydration safety pattern.

The component correctly handles client-side rendering to prevent hydration mismatches.

apps/docs/components/themes/components/configuration/default-colors.tsx (1)

24-29: Add validation and error handling for color values

The ColorPicker implementation should validate hex color values and handle potential errors to prevent invalid color states.

Consider adding color validation and error handling:

 <ColorPicker
   hexColor={config[theme].defaultColor.default}
   type="default"
-  onChange={(hexColor) => setCssColor("default", hexColor, theme)}
+  onChange={(hexColor) => {
+    if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(hexColor)) {
+      setCssColor("default", hexColor, theme);
+    }
+  }}
   onClose={(hexColor) => setDefaultColor({default: hexColor}, theme, false)}
 />
apps/docs/components/themes/components/configuration/other.tsx (1)

21-25: 🛠️ Refactor suggestion

Add input validation for opacity values.

The opacity inputs should be constrained between 0 and 1. Currently, there are no validation checks to prevent invalid values.

 <NumberInput
   label="Disabled opacity (0-1)"
   value={config.layout.otherParams.disabledOpacity}
+  min="0"
+  max="1"
+  step="0.1"
   onChange={(value) => handleChange("disabledOpacity", value)}
 />
✅ Verification successful

Review comment is valid - NumberInput lacks opacity value constraints

The current NumberInput implementation only validates that the input is a valid float number but doesn't constrain the range. The suggested addition of min, max, and step props is necessary because:

  • The component allows any float value, which could lead to invalid opacity values
  • The underlying Input component from @heroui/react supports these props
  • This change would prevent users from entering invalid opacity values outside the 0-1 range
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Find NumberInput component definition
ast-grep --pattern 'const NumberInput = $_'

# Find NumberInput component interface or type
ast-grep --pattern 'interface NumberInputProps {
  $$$
}'

# Find other NumberInput usages with opacity
rg -A 2 'opacity.*NumberInput|NumberInput.*opacity' --type tsx

# Look for existing min/max/step props usage with NumberInput
rg 'NumberInput.*min|NumberInput.*max|NumberInput.*step' --type tsx

Length of output: 688


Script:

#!/bin/bash
# Find other NumberInput usages with opacity
rg -t ts -t tsx -A 2 'opacity.*NumberInput|NumberInput.*opacity'

# Look for existing min/max/step props usage with NumberInput
rg -t ts -t tsx 'NumberInput.*min|NumberInput.*max|NumberInput.*step'

# Find the NumberInput component implementation
cat apps/docs/components/themes/components/number-input.tsx

Length of output: 811

apps/docs/components/themes/components/configuration/font-button.tsx (1)

26-48: Consider adding font loading verification.

The component assumes fonts are available but doesn't verify their loading status.

apps/docs/components/themes/components/configuration/base-colors.tsx (1)

10-15: LGTM! Well-structured props interface.

The props interface clearly defines the required properties for theme configuration and synchronization.

apps/docs/components/themes/utils/config.ts (1)

7-11: Consider adding validation for disabledOpacity.

The layout configuration accepts disabledOpacity without validation. Consider adding bounds checking to ensure valid opacity values.

 function generateLayoutConfig(config: Config): HeroUIPluginConfig["layout"] {
+  const opacity = config.layout.otherParams.disabledOpacity;
+  if (opacity < 0 || opacity > 1) {
+    throw new Error("disabledOpacity must be between 0 and 1");
+  }
   return {
-    disabledOpacity: config.layout.otherParams.disabledOpacity,
+    disabledOpacity: opacity,
   };
 }
✅ Verification successful

Runtime validation for disabledOpacity is necessary

The suggestion to add bounds checking is valid because:

  • The value is used directly in CSS variables without validation
  • Current implementation only validates number format, not range
  • Type system and UI components don't enforce the required 0-1 range
  • Documentation explicitly states the value should be between 0 and 1
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Search for type definitions and interfaces related to Config and HeroUIPluginConfig
ast-grep --pattern 'interface Config {
  $$$
}'

ast-grep --pattern 'interface HeroUIPluginConfig {
  $$$
}'

# Search for any existing validation patterns
rg "disabledOpacity" -A 2 -B 2

# Look for type definitions with number constraints
ast-grep --pattern 'type $_ = number'

Length of output: 9595


Script:

#!/bin/bash
# Check NumberInput component implementation
ast-grep --pattern 'NumberInput'

# Search for any validation in the component
rg "NumberInput" -A 5 -B 5

Length of output: 11969

apps/docs/components/themes/components/showcase/checkbox.tsx (1)

102-113: LGTM! Well-structured component with proper theme integration.

The component effectively utilizes the theme builder hook and properly maps through color options.

apps/docs/components/themes/constants.ts (1)

13-21: LGTM! Consistent ID naming convention.

The element IDs follow a consistent naming pattern with the 'th-' prefix, making them easily identifiable as theme-related elements.

apps/docs/components/themes/components/showcase/breadcrumbs.tsx (1)

98-109: LGTM! Clean implementation of the showcase component.

The component effectively uses the theme builder hook and properly manages the rendering of different breadcrumb variations.

apps/docs/components/themes/components/showcase/chip.tsx (2)

1-11: LGTM! Clear and well-organized type definitions.

The type aliases effectively leverage the HeroUI component props, promoting type safety and maintainability.


111-128: LGTM! Well-structured showcase component.

The component effectively manages theme values and renders chip variations in a clean and maintainable way.

apps/docs/components/themes/components/showcase/input.tsx (2)

1-13: LGTM! Clean imports and type definitions.

The imports are well-organized, and type aliases effectively leverage the HeroUI component props.


104-121: LGTM! Consistent showcase component implementation.

The component follows the same clean pattern as other showcase components, effectively using the theme builder hook.

apps/docs/components/themes/components/showcase/button.tsx (1)

1-11: LGTM! Clean imports and type definitions.

The code follows TypeScript best practices by using type aliases for better code maintainability.

apps/docs/components/themes/utils/colors.ts (3)

11-13: LGTM! Clean and focused implementation.

The function follows the single responsibility principle and effectively converts Values.js color to RGBA string format.


92-98: LGTM! Clean implementation.

The function effectively determines color weight based on color type and theme.


112-114: LGTM! Clean implementation.

The function effectively converts RGB values to hex format.

apps/docs/components/themes/provider.tsx (2)

338-346: LGTM! Well-implemented custom hook.

The custom hook implementation follows React best practices:

  1. Provides proper type safety
  2. Includes appropriate error handling for usage outside provider
  3. Returns the full context object

195-206: 🛠️ Refactor suggestion

Improve error handling for clipboard operations.

The setConentColor function is called without error handling for clipboard operations which could fail in certain browsers or contexts.

-  function handleCopy() {
-    navigator.clipboard.writeText(JSON.stringify(generatePluginConfig(config), null, 2));
+  async function handleCopy() {
+    try {
+      await navigator.clipboard.writeText(JSON.stringify(generatePluginConfig(config), null, 2));
+    } catch (error) {
+      console.error('Failed to copy to clipboard:', error);
+      // Consider showing a user-friendly error message
+    }
+  }

Likely invalid or redundant comment.

apps/docs/components/themes/components/configuration/actions.tsx (2)

33-59: LGTM! Well-structured UI implementation.

The UI implementation is clean and follows good practices:

  1. Proper use of tooltips for button actions
  2. Clear visual feedback for copy action
  3. Consistent icon usage

14-14: 🛠️ Refactor suggestion

Improve type definition for onCopy callback.

The unknown return type for onCopy is too permissive and could lead to type-safety issues.

-  onCopy: () => unknown;
+  onCopy: () => Record<string, unknown>;

Likely invalid or redundant comment.

apps/docs/package.json (1)

105-106: Verify security of new dependencies.

Let's verify there are no known vulnerabilities in the new color-related dependencies.

✅ Verification successful

Dependencies verified as secure and well-maintained

Both react-colorful (5.6.1) and values.js (2.1.1) are legitimate, well-maintained packages with:

  • No known vulnerabilities
  • MIT license compliance
  • Active maintenance
  • Clear documentation and repositories
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for known vulnerabilities in new dependencies
echo "Checking react-colorful..."
curl -s "https://api.snyk.io/v1/test/npm/react-colorful/5.6.1" -H "Authorization: token $SNYK_TOKEN"

echo "Checking values.js..."
curl -s "https://api.snyk.io/v1/test/npm/values.js/2.1.1" -H "Authorization: token $SNYK_TOKEN"

Length of output: 455


Script:

#!/bin/bash
# Create a temporary package.json for npm audit
echo '{
  "dependencies": {
    "react-colorful": "5.6.1",
    "values.js": "2.1.1"
  }
}' > temp_package.json

# Run npm audit
npm audit --json || true

# Check GitHub repos
echo "Checking react-colorful GitHub repo..."
gh repo view ISvlad/react-colorful --json name,description,updatedAt,stargazerCount,isArchived,defaultBranchRef

echo "Checking values.js GitHub repo..."
gh repo view noeldelgado/values.js-legacy --json name,description,updatedAt,stargazerCount,isArchived,defaultBranchRef

Length of output: 1303


Script:

#!/bin/bash
# Get package info from npm registry
echo "Checking react-colorful on npm..."
curl -s https://registry.npmjs.org/react-colorful | jq '{name, description, maintainers, repository, homepage, license, version, downloads: ."dist-tags", time: .time}'

echo "Checking values.js on npm..."
curl -s https://registry.npmjs.org/values.js | jq '{name, description, maintainers, repository, homepage, license, version, downloads: ."dist-tags", time: .time}'

# Create package.json and generate lockfile
echo '{
  "dependencies": {
    "react-colorful": "5.6.1",
    "values.js": "2.1.1"
  }
}' > temp_package.json

# Generate lockfile and run audit
npm install --package-lock-only
npm audit --json

Length of output: 6266

apps/docs/tailwind.config.js (1)

348-355: Verify browser compatibility of scrollbar utilities.

The implementation handles both Firefox and WebKit browsers, which is good. However, let's verify if there are any other browser-specific considerations.

✅ Verification successful

Browser compatibility for scrollbar utilities is properly implemented

The implementation covers all major browser engines (WebKit/Blink, Gecko, Trident/EdgeHTML) with appropriate vendor-specific properties and includes a way to restore default scrollbar behavior when needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing scrollbar-related utilities and browser compatibility patterns
rg -g '*.{js,ts,tsx,css}' -A 2 'scrollbar|::-webkit-scrollbar'

Length of output: 7685

apps/docs/components/themes/index.tsx Show resolved Hide resolved
apps/docs/components/themes/utils/shared.ts Show resolved Hide resolved
apps/docs/components/themes/utils/shared.ts Show resolved Hide resolved
apps/docs/components/themes/components/color-picker.tsx Outdated Show resolved Hide resolved
apps/docs/components/icons/radial-blur.tsx Outdated Show resolved Hide resolved
apps/docs/components/icons/crop.tsx Outdated Show resolved Hide resolved
apps/docs/components/themes/provider.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@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: 8

♻️ Duplicate comments (1)
apps/docs/components/themes/components/configuration/actions.tsx (1)

22-24: ⚠️ Potential issue

Improve clipboard operation handling.

The clipboard operation should be handled asynchronously with proper error handling and user feedback.

-  function handleCopyConfig() {
+  async function handleCopyConfig() {
+    try {
-      navigator.clipboard.writeText(JSON.stringify(onCopy(), null, 2));
+      await navigator.clipboard.writeText(JSON.stringify(onCopy(), null, 2));
+      // Show success feedback to user
+    } catch (error) {
+      console.error('Failed to copy configuration:', error);
+      // Show error feedback to user
+    }
   }
🧹 Nitpick comments (23)
apps/docs/components/themes/components/configuration/radiuses.tsx (2)

12-48: Add radiogroup role for improved accessibility.

The buttons form a logical group of options but this isn't conveyed to screen readers.

 <ConfigSection icon={<CropMinimalistic className="h-5 w-5" />} title="Radius">
+  <div role="radiogroup" aria-label="Border radius options">
     <EditableButton
       aria-label="No border radius"
       className="rounded-tl-none"
       // ... other props
     />
     // ... other buttons
+  </div>
 </ConfigSection>

15-15: Consider enhancing visual representation.

The buttons only demonstrate the top-left radius (rounded-tl-*). Consider showing the radius effect on all corners for better visual feedback.

-className="rounded-tl-none"
+className="rounded-none"

-className="rounded-tl-sm"
+className="rounded-sm"

-className="rounded-tl-md"
+className="rounded-md"

-className="rounded-tl-lg"
+className="rounded-lg"

-className="rounded-tl-full"
+className="rounded-full"

Also applies to: 22-22, 29-29, 36-36, 43-43

apps/docs/components/themes/components/configuration/scaling.tsx (1)

10-10: Consider moving scale values outside the component.

To optimize performance, define scaleValues outside the component to prevent unnecessary array recreation on each render:

+const scaleValues = [90, 95, 100, 105, 110];
+
 export function Scaling() {
   const {scaling, setScaling} = useThemeBuilder();
-  const scaleValues = [90, 95, 100, 105, 110];
apps/docs/components/icons/radial-blur.tsx (1)

13-56: Consider making the fill color themeable.

The hard-coded fill color (#A1A1AA) might limit theme customization. Consider accepting a color prop or using currentColor to support different themes.

-      fill="#A1A1AA"
+      fill={color || "currentColor"}

This change should be applied to all path elements in the SVG.

apps/docs/components/themes/components/configuration/actions.tsx (2)

9-14: Improve type safety for onCopy callback.

The unknown return type for onCopy is too permissive. Since the return value is being JSON stringified, it should be constrained to JSON-serializable types.

 interface ActionsProps {
   theme: ThemeType;
-  onCopy: () => unknown;
+  onCopy: () => Record<string, unknown>;
   onResetTheme: () => void;
   onToggleTheme: () => void;
 }

43-45: Maintain consistency in button styling.

The Copy button breaks the pattern of using icons. Consider using a copy icon to maintain visual consistency with other buttons.

 <Button isIconOnly color="secondary" size="sm" variant="flat" onClick={handleCopyConfig}>
-  Copy
+  <Icon className="text-lg" icon={CopyIcon} />
 </Button>

You'll need to add this import:

import CopyIcon from "@iconify/icons-solar/copy-linear";
apps/docs/components/themes/components/showcase/code.tsx (3)

10-24: Remove unnecessary key prop and optimize performance.

Two suggestions for improvement:

  1. Remove the unnecessary key prop from HeroUICode as it's not being rendered in a list.
  2. Consider memoizing the component to prevent unnecessary re-renders.
-const SectionBase = ({
+const SectionBase = React.memo(({
   color,
   radius,
   className,
 }: {
   color?: Color;
   radius?: Radius;
   className?: string;
 }) => {
   return (
-    <HeroUICode key={radius} className={className} color={color} radius={radius}>
+    <HeroUICode className={className} color={color} radius={radius}>
       npm install @heroui/react
     </HeroUICode>
   );
-};
+});
+
+SectionBase.displayName = "SectionBase";

26-65: Optimize Section component implementation.

Several improvements can be made to enhance maintainability and performance:

  1. Replace the switch statement with a mapping object
  2. Memoize the component
  3. Remove unnecessary div wrapper
+const SCALING_CLASSES: Record<HeroUIScaling, string> = {
+  90: "p-2 px-3 text-tiny",
+  95: "p-2 px-4 text-tiny",
+  100: "p-2 px-4 text-medium",
+  105: "p-3 px-5 text-medium",
+  110: "p-2 px-8 text-medium",
+};
+
-const Section = ({
+const Section = React.memo(({
   color,
   radius,
   scaling,
 }: {
   color: Color;
   radius: Radius;
   scaling: HeroUIScaling;
 }) => {
-  let className = "p-0 px-3 text-tiny";
-
-  switch (scaling) {
-    case 90: {
-      className = "p-2 px-3 text-tiny";
-      break;
-    }
-    case 95: {
-      className = "p-2 px-4 text-tiny";
-      break;
-    }
-    case 100: {
-      className = "p-2 px-4 text-medium";
-      break;
-    }
-    case 105: {
-      className = "p-3 px-5 text-medium";
-      break;
-    }
-    case 110: {
-      className = "p-2 px-8 text-medium";
-      break;
-    }
-  }
+  const className = SCALING_CLASSES[scaling] ?? "p-0 px-3 text-tiny";

   return (
-    <div key={color} className="flex flex-col gap-y-4">
-      <SectionBase className={className} color={color} radius={radius} />
-    </div>
+    <SectionBase className={className} color={color} radius={radius} />
   );
-};
+});
+
+Section.displayName = "Section";

67-78: Optimize Code component and fix key prop.

Several improvements can be made:

  1. Move the colors array outside the component to prevent recreation on each render
  2. Use color value as key instead of array index
  3. Memoize the component
+const COLORS: Color[] = [
+  "default",
+  "primary",
+  "secondary",
+  "success",
+  "warning",
+  "danger",
+];
+
-export const Code = () => {
-  const colors: Color[] = ["default", "primary", "secondary", "success", "warning", "danger"];
+export const Code = React.memo(() => {
   const {radiusValue, scaling} = useThemeBuilder();

   return (
     <ShowcaseComponent name="Code">
-      {colors.map((color, idx) => (
-        <Section key={idx} color={color} radius={radiusValue} scaling={scaling} />
+      {COLORS.map((color) => (
+        <Section key={color} color={color} radius={radiusValue} scaling={scaling} />
       ))}
     </ShowcaseComponent>
   );
-};
+});
+
+Code.displayName = "Code";
apps/docs/components/themes/provider.tsx (1)

17-52: Add JSDoc documentation for the interface and its methods.

The interface lacks documentation explaining the purpose of each method and the significance of the sync parameter.

Add JSDoc comments to improve maintainability:

+/**
+ * Context interface for the theme builder functionality.
+ */
 export interface ThemeBuilderContextProps {
+  /** Current theme configuration */
   config: Config;
+  /** Current radius value */
   radiusValue: Radius;
   // ... add documentation for other properties

+  /**
+   * Updates the layout color configuration
+   * @param newConfig - New layout color configuration
+   * @param theme - Theme type (light/dark)
+   * @param sync - Whether to sync changes across both themes
+   */
   setLayoutColor: (
     newConfig: Partial<ConfigColors["layoutColor"]>,
     theme: ThemeType,
     sync: boolean,
   ) => void;
   // ... add documentation for other methods
 }
apps/docs/components/themes/components/showcase/breadcrumbs.tsx (3)

1-1: Consider alternatives to cloneElement.

Using cloneElement can make the code harder to maintain and debug. Consider refactoring to use component composition or render props pattern instead.

-        cloneElement(<SectionBase key={idx} />, {
+        <SectionBase
+          key={idx}
           color={color}
           variant={variant}
           classNames={{
             ...classNames,
             list: variant === "bordered" ? borderClassName : "",
           }}
           isDisabled={disabled[idx]}
-        }),
+        />

Also applies to: 84-84


58-79: Improve scaling implementation.

The scaling implementation can be improved in several ways:

  1. Magic numbers (90, 95, etc.) should be defined as constants
  2. Text size classes could be derived programmatically
+const SCALE_CONFIG = {
+  90: { size: '[0.7rem]' },
+  95: { size: 'tiny' },
+  100: { size: 'small', padding: '0.5' },
+  105: { size: 'medium', padding: '1' },
+  110: { size: 'large', padding: '1.5' },
+} as const;
+
+type Scale = keyof typeof SCALE_CONFIG;
+
 switch (scaling) {
-    case 90: {
-      classNames = {base: "text-[0.7rem]"};
-      break;
-    }
-    // ... other cases
+    case scaling as Scale: {
+      const config = SCALE_CONFIG[scaling as Scale];
+      classNames = {
+        base: `text-${config.size}${config.padding ? ` p-${config.padding}` : ''}`
+      };
+      break;
+    }
     default: {
+      classNames = {base: "text-small"};
+      break;
     }
   }

98-101: Extract colors array to constants and add error handling.

  1. Move the colors array to a constant for better maintainability
  2. Add error handling for the useThemeBuilder hook
+const BREADCRUMB_COLORS: Color[] = [
+  "foreground",
+  "primary",
+  "secondary",
+  "success",
+  "warning",
+  "danger",
+];

 export const BreadCrumbs = () => {
-  const colors: Color[] = ["foreground", "primary", "secondary", "success", "warning", "danger"];
   const {scaling, borderWidthValue} = useThemeBuilder();
+
+  if (!scaling || !borderWidthValue) {
+    return null; // or fallback UI
+  }
apps/docs/components/themes/components/showcase/switch.tsx (2)

39-75: Simplify scaling logic using an object lookup.

The switch statement could be replaced with a more maintainable object lookup pattern.

Consider this improvement:

-  switch (scaling) {
-    case 90: {
-      classNames = {
-        wrapper: "w-8 h-4",
-        thumb: "w-2 h-2 group-data-[selected=true]:ms-4",
-      };
-      break;
-    }
-    // ... other cases
-  }
+  const scalingClassNames = {
+    90: {
+      wrapper: "w-8 h-4",
+      thumb: "w-2 h-2 group-data-[selected=true]:ms-4",
+    },
+    95: {
+      wrapper: "w-10 h-6",
+      thumb: "w-3 h-3",
+    },
+    // ... other mappings
+  };
+  classNames = scalingClassNames[scaling] || classNames;

85-96: Improve component organization and key usage.

  1. Using array index as key in the map function could cause issues with React's reconciliation.
  2. The colors array could be moved to constants for better maintainability.

Consider these improvements:

+const SWITCH_COLORS: Color[] = [
+  "default",
+  "primary",
+  "secondary",
+  "success",
+  "warning",
+  "danger",
+];
+
 export const SwitchComponent = () => {
-  const colors: Color[] = ["default", "primary", "secondary", "success", "warning", "danger"];
   const {scaling} = useThemeBuilder();
 
   return (
     <ShowcaseComponent name="Switch">
-      {colors.map((color, idx) => (
-        <Section key={idx} color={color} scaling={scaling} />
+      {SWITCH_COLORS.map((color) => (
+        <Section key={color} color={color} scaling={scaling} />
       ))}
     </ShowcaseComponent>
   );
 };
apps/docs/components/themes/components/showcase-component.tsx (1)

26-37: Consider adding aria-label for better accessibility.

The showcase component could benefit from better accessibility support.

-    <div className="bg-background text-foreground py-6 p-4 rounded-lg" id={id} style={style}>
+    <div 
+      className="bg-background text-foreground py-6 p-4 rounded-lg" 
+      id={id} 
+      style={style}
+      aria-label={`${name} showcase section`}
+    >
apps/docs/components/themes/components/configuration/font-button.tsx (1)

17-30: Consider caching font styles for performance.

The getFontStyle function creates new objects on each call. Consider memoizing the results.

+const fontStyleCache: Record<FontName, FontStyle> = {};

 function getFontStyle(fontName: FontName): FontStyle {
+  if (fontStyleCache[fontName]) return fontStyleCache[fontName];
+
   switch (fontName) {
     case "inter":
-      return {fontFamily: "'Inter', sans-serif", letterSpacing: "-0.02em"};
+      return (fontStyleCache[fontName] = {fontFamily: "'Inter', sans-serif", letterSpacing: "-0.02em"});
     // ... rest of the cases
   }
 }
apps/docs/components/themes/components/showcase/popover.tsx (2)

45-66: Replace switch statement with lookup object for better maintainability.

The scaling switch statement can be simplified using a lookup object.

-  switch (scaling) {
-    case 90: {
-      className = "px-1 py-2 text-tiny";
-      break;
-    }
-    // ... other cases
-  }
+  const scalingClasses = {
+    90: "px-1 py-2 text-tiny",
+    95: "text-tiny px-2 py-3",
+    100: "text-small px-2 py-3",
+    105: "text-small px-3 py-3",
+    110: "text-medium px-3 py-3"
+  };
+  className = scalingClasses[scaling] || scalingClasses[100];

75-86: Add memoization for color array.

The colors array is recreated on each render.

+const POPOVER_COLORS: Color[] = ["default", "primary", "secondary", "success", "warning", "danger"];

 export const PopoverComponent = () => {
-  const colors: Color[] = ["default", "primary", "secondary", "success", "warning", "danger"];
   const {radiusValue, scaling} = useThemeBuilder();

   return (
     <ShowcaseComponent name="Popover">
-      {colors.map((color, idx) => (
+      {POPOVER_COLORS.map((color, idx) => (
         <Section key={idx} color={color} radius={radiusValue} scaling={scaling} />
       ))}
     </ShowcaseComponent>
   );
 };
apps/docs/components/themes/components/showcase/button.tsx (1)

97-114: Memoize Section components to prevent unnecessary rerenders.

The Button component could benefit from memoization to optimize performance.

+import {useMemo} from 'react';

 export const Button = () => {
   const colors: Color[] = ["default", "primary", "secondary", "success", "warning", "danger"];
   const {radiusValue, scaling, borderWidthValue} = useThemeBuilder();

+  const sections = useMemo(() => 
+    colors.map((color, idx) => (
+      <Section
+        key={idx}
+        borderWidthValue={borderWidthValue}
+        color={color}
+        radius={radiusValue}
+        scaling={scaling}
+      />
+    )), [colors, borderWidthValue, radiusValue, scaling]);

   return (
     <ShowcaseComponent name="Button">
-      {colors.map((color, idx) => (
-        <Section
-          key={idx}
-          borderWidthValue={borderWidthValue}
-          color={color}
-          radius={radiusValue}
-          scaling={scaling}
-        />
-      ))}
+      {sections}
     </ShowcaseComponent>
   );
 };
apps/docs/components/themes/components/color-picker.tsx (3)

24-25: Optimize performance by memoizing color calculations.

The Values calculations and color weight computations are being performed on every render. Consider memoizing these values using useMemo.

-  const selectedColorWeight = getColorWeight(type, theme);
-  const selectedColorValues = new Values(selectedColor).all(selectedColorWeight);
+  const selectedColorWeight = useMemo(() => getColorWeight(type, theme), [type, theme]);
+  const selectedColorValues = useMemo(
+    () => new Values(selectedColor).all(selectedColorWeight),
+    [selectedColor, selectedColorWeight]
+  );

62-74: Enhance accessibility for color grid.

The color grid items should have proper ARIA labels to improve screen reader support.

   <div
     className="h-6 w-6 rounded"
+    role="button"
+    aria-label={`Color shade ${index === 0 ? 50 : index * 100}`}
     style={{backgroundColor: colorValuesToRgb(colorValue)}}
   />

89-124: Enhance type safety for color mapping.

The getColor function could benefit from exhaustive type checking to ensure all color types are handled.

-function getColor(type: ColorPickerType) {
+function getColor(type: ColorPickerType): string {
   switch (type) {
     // ... existing cases ...
     default:
-      return undefined;
+      const _exhaustiveCheck: never = type;
+      return '';
   }
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 368ff19 and d2c5add.

📒 Files selected for processing (19)
  • apps/docs/components/icons/crop.tsx (1 hunks)
  • apps/docs/components/icons/radial-blur.tsx (1 hunks)
  • apps/docs/components/themes/components/color-picker.tsx (1 hunks)
  • apps/docs/components/themes/components/config-section.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/actions.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/border-widths.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/content-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/font-button.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/radiuses.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/scaling.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/value-button.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase-component.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/breadcrumbs.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/button.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/code.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/popover.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/switch.tsx (1 hunks)
  • apps/docs/components/themes/provider.tsx (1 hunks)
  • apps/docs/components/themes/utils/colors.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/docs/components/themes/components/configuration/border-widths.tsx
  • apps/docs/components/themes/components/config-section.tsx
  • apps/docs/components/themes/components/configuration/value-button.tsx
  • apps/docs/components/icons/crop.tsx
  • apps/docs/components/themes/utils/colors.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build
🔇 Additional comments (20)
apps/docs/components/themes/components/configuration/radiuses.tsx (3)

1-6: LGTM! Well-organized imports.

The imports are logically grouped and all are being used in the component.


8-9: LGTM! Clean component structure.

The component follows React best practices with proper hook usage and descriptive naming.


13-19: LGTM! Proper accessibility implementation.

The buttons include appropriate ARIA labels for screen readers.

Also applies to: 20-26, 27-33, 34-40, 41-47

apps/docs/components/themes/components/configuration/scaling.tsx (3)

1-6: LGTM! Well-organized imports.

The imports are logically grouped and properly spaced for better readability.


8-9: LGTM! Clean component structure.

Good use of named export and hook destructuring pattern.


12-24: LGTM! Clean and efficient JSX implementation.

The component successfully implements the previous review suggestion to map over scale values, resulting in cleaner and more maintainable code.

apps/docs/components/icons/radial-blur.tsx (2)

1-3: LGTM! Well-structured component declaration.

The component follows React best practices with proper typing and default props.


4-12: Accessibility requirements properly implemented.

The SVG element includes proper aria-label and focusable attributes as previously requested.

apps/docs/components/themes/components/configuration/actions.tsx (1)

1-7: LGTM! Well-organized imports with performance consideration.

Good choice using the offline distribution of icons which helps with performance and reliability.

apps/docs/components/themes/components/showcase/code.tsx (1)

1-8: LGTM! Well-structured imports and type definitions.

The imports are well organized and the type definitions are properly derived from CodeProps.

apps/docs/components/themes/provider.tsx (2)

1-16: LGTM! Well-organized imports and type definitions.

The imports are properly structured and all necessary types are imported.


337-346: LGTM! Well-implemented custom hook.

The hook follows React best practices with proper error handling for usage outside the provider context.

apps/docs/components/themes/components/showcase/breadcrumbs.tsx (1)

15-40: Improve type safety and component flexibility.

The previous review suggestions about type safety and component flexibility are still valid.

apps/docs/components/themes/components/showcase/switch.tsx (1)

1-8: LGTM! Well-structured imports and type definitions.

The imports are well-organized and the Color type alias improves code maintainability.

apps/docs/components/themes/components/showcase-component.tsx (3)

5-9: LGTM! Well-defined interface with proper type annotations.

The ShowcaseComponentProps interface is clear and properly typed.


20-24: LGTM! Clean and type-safe implementation.

The getFontStyle function is well-implemented with proper type safety and null checks.


11-18: Consider adding font loading validation.

While the font configurations are well-structured, there's no validation to ensure these fonts are actually loaded and available in the browser.

Run this script to check font loading:

apps/docs/components/themes/components/configuration/content-colors.tsx (1)

15-51: 🛠️ Refactor suggestion

Consider implementing color scheme validation.

The component should validate color combinations for accessibility and contrast ratios.

+import {validateColorContrast} from '@heroui/utils';

 export function ContentColors({config, theme}: BaseColorsProps) {
   const {setContentColor} = useThemeBuilder();
+  
+  const handleColorChange = (index: number, hexColor: string) => {
+    const isValid = validateColorContrast(hexColor, config[theme].backgroundColor);
+    if (!isValid) {
+      console.warn(`Color combination may have insufficient contrast ratio`);
+    }
+    setCssContentColor(index, hexColor);
+  };

   return (
     <ConfigSection>
       <ColorPicker
         hexColor={config[theme].contentColor.content1}
         type="content1"
-        onChange={(hexColor) => setCssContentColor(1, hexColor)}
+        onChange={(hexColor) => handleColorChange(1, hexColor)}
         onClose={(hexColor) => setContentColor({content1: hexColor}, theme)}
       />
       {/* Update other ColorPickers similarly */}
     </ConfigSection>
   );
 }

Likely invalid or redundant comment.

apps/docs/components/themes/components/showcase/button.tsx (1)

86-93: 🛠️ Refactor suggestion

Replace cloneElement with direct component rendering.

Using cloneElement with a static element is an anti-pattern.

-      {cloneElement(<SectionBase />, {
-        color,
-        radius,
-        className,
-        isDisabled: true,
-        variant: "solid",
-      })}
+      <SectionBase
+        color={color}
+        radius={radius}
+        className={className}
+        isDisabled={true}
+        variant="solid"
+      />

Likely invalid or redundant comment.

apps/docs/components/themes/components/color-picker.tsx (1)

1-17: LGTM! Well-structured imports and type definitions.

The imports are properly organized and the TypeScript interface is well-defined with clear prop types.

Copy link
Contributor

@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: 2

♻️ Duplicate comments (2)
apps/docs/components/themes/components/showcase/breadcrumbs.tsx (2)

15-40: 🛠️ Refactor suggestion

Improve type safety and component flexibility further.

While moving the items array out of JSX is good, the component still needs improvements.

Building upon the previous review, apply this diff:

+type BreadcrumbsClassNames = {
+  base?: string;
+  list?: string;
+  item?: string;
+};

 const SectionBase = ({
   color,
   variant,
   isDisabled,
   classNames,
+  items = ["Home", "Music", "Artist", "Album", "Song"],
 }: {
   color?: BreadcrumbsProps["color"];
   variant?: BreadcrumbsProps["variant"];
   isDisabled?: boolean;
-  classNames?: any;
+  classNames?: BreadcrumbsClassNames;
+  items?: string[];
 }) => {
-  const items = ["Home", "Music", "Artist", "Album", "Song"];

51-52: ⚠️ Potential issue

Fix array length mismatch between variants and disabled arrays.

The variants array has 4 elements (with a duplicate 'solid') while disabled array has 4 elements.

Apply this diff:

-  const variants = ["bordered", "light", "solid", "solid"];
-  const disabled = [false, false, false, true];
+  const variants = ["bordered", "light", "solid"];
+  const disabled = [false, false, false];
🧹 Nitpick comments (3)
apps/docs/components/themes/components/showcase/input.tsx (2)

56-77: Consider extracting scaling configuration to a constant.

The switch statement contains magic numbers and repeated dimension patterns.

Consider creating a configuration object:

const SCALING_CONFIG = {
} as const;

// Then use it like:
classNames = SCALING_CONFIG[scaling] ?? SCALING_CONFIG[100];

104-121: Improve maintainability and rendering optimization.

  1. Using array indices as keys may lead to rendering issues
  2. The colors array should be extracted as a constant

Consider these improvements:

+const INPUT_COLORS: Color[] = [
+  "default",
+  "primary",
+  "secondary",
+  "success",
+  "warning",
+  "danger",
+] as const;

 export const InputComponent = () => {
-  const colors: Color[] = ["default", "primary", "secondary", "success", "warning", "danger"];
   const {radiusValue, scaling, borderWidthValue} = useThemeBuilder();

   return (
     <ShowcaseComponent name="Input">
-      {colors.map((color, idx) => (
+      {INPUT_COLORS.map((color) => (
         <Section
-          key={idx}
+          key={color}
           borderWidthValue={borderWidthValue}
           color={color}
           radius={radiusValue}
           scaling={scaling}
         />
       ))}
     </ShowcaseComponent>
   );
 };
apps/docs/components/themes/components/showcase/breadcrumbs.tsx (1)

58-79: Simplify scaling-based className assignment.

The switch statement can be replaced with a cleaner object lookup.

Consider this alternative:

-  switch (scaling) {
-    case 90: {
-      classNames = {base: "text-[0.7rem]"};
-      break;
-    }
-    case 95: {
-      classNames = {base: "text-tiny"};
-      break;
-    }
-    case 100: {
-      classNames = {base: "text-small p-0.5"};
-      break;
-    }
-    case 105: {
-      classNames = {base: "text-medium p-1"};
-      break;
-    }
-    case 110: {
-      classNames = {base: "text-large p-1.5"};
-      break;
-    }
-  }
+  const scalingClassNames = {
+    90: {base: "text-[0.7rem]"},
+    95: {base: "text-tiny"},
+    100: {base: "text-small p-0.5"},
+    105: {base: "text-medium p-1"},
+    110: {base: "text-large p-1.5"},
+  };
+  classNames = scalingClassNames[scaling] ?? classNames;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2c5add and cf2c2ca.

📒 Files selected for processing (2)
  • apps/docs/components/themes/components/showcase/breadcrumbs.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/input.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: TypeScript
  • GitHub Check: Build
🔇 Additional comments (4)
apps/docs/components/themes/components/showcase/input.tsx (2)

1-13: LGTM! Clean imports and type definitions.

The imports are well-organized and the type aliases are properly derived from InputProps.


14-37: Previous review comment about type safety is still applicable.

The suggested improvements for type safety and component flexibility have not been addressed:

  1. The classNames prop is still typed as any
  2. The input label is still hardcoded
apps/docs/components/themes/components/showcase/breadcrumbs.tsx (2)

1-14: LGTM! Clean imports and type definitions.

The imports are well-organized and the Color type alias improves code readability.


98-109: LGTM! Clean implementation of the main component.

The BreadCrumbs component effectively orchestrates the showcase with proper theme integration.

Copy link
Contributor

@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: 2

♻️ Duplicate comments (2)
apps/docs/components/themes/components/showcase/breadcrumbs.tsx (2)

13-38: 🛠️ Refactor suggestion

Improve type safety by properly typing the classNames prop.

The classNames prop is still typed as any, which bypasses TypeScript's type checking benefits.

+type BreadcrumbsClassNames = {
+  base?: string;
+  list?: string;
+  item?: string;
+};

 const SectionBase = ({
   color,
   variant,
   isDisabled,
   classNames,
 }: {
   color?: BreadcrumbsProps["color"];
   variant?: BreadcrumbsProps["variant"];
   isDisabled?: boolean;
-  classNames?: any;
+  classNames?: BreadcrumbsClassNames;

49-50: ⚠️ Potential issue

Fix array length mismatch between variants and disabled arrays.

The variants array has a duplicate "solid" value and disabled array length doesn't match.

-  const variants = ["bordered", "light", "solid", "solid"];
-  const disabled = [false, false, false, true];
+  const variants = ["bordered", "light", "solid"];
+  const disabled = [false, false, false];
🧹 Nitpick comments (8)
apps/docs/components/themes/components/showcase-component.tsx (1)

25-36: Consider enhancing maintainability and accessibility.

While the implementation is solid, consider these improvements:

  1. Move className strings to constants or a CSS module
  2. Make the gap spacing configurable via props
  3. Add aria-label for better accessibility

Here's a suggested implementation:

+const SHOWCASE_STYLES = {
+  container: "bg-background text-foreground py-6 p-4 rounded-lg",
+  title: "text-xl font-medium text-default-800",
+  content: "flex flex-wrap mt-8"
+};

+interface ShowcaseComponentProps {
   children: React.ReactElement | React.ReactElement[];
   id?: string;
   name: string;
+  gap?: number;
 }

 export function ShowcaseComponent({
-  children, id, name
+  children, id, name, gap = 6
 }: ShowcaseComponentProps) {
   const {font} = useThemeBuilder();
   const style = getFontStyle(font);

   return (
     <div 
-      className="bg-background text-foreground py-6 p-4 rounded-lg"
+      className={SHOWCASE_STYLES.container}
       id={id} 
       style={style}
+      aria-label={`${name} showcase section`}
     >
-      <span className="text-xl font-medium text-default-800">{name}</span>
+      <span className={SHOWCASE_STYLES.title}>{name}</span>
       <Divider className="mt-4 mb-6" />
-      <div className="flex flex-wrap gap-6 mt-8">{children}</div>
+      <div className={`${SHOWCASE_STYLES.content} gap-${gap}`}>{children}</div>
     </div>
   );
 }
apps/docs/components/themes/components/showcase/breadcrumbs.tsx (1)

61-82: Simplify the scaling switch statement using an object lookup.

The switch statement for scaling can be simplified to improve maintainability.

-  switch (scaling) {
-    case 90: {
-      classNames = {base: "text-[0.7rem]"};
-      break;
-    }
-    case 95: {
-      classNames = {base: "text-tiny"};
-      break;
-    }
-    case 100: {
-      classNames = {base: "text-small p-0.5"};
-      break;
-    }
-    case 105: {
-      classNames = {base: "text-medium p-1"};
-      break;
-    }
-    case 110: {
-      classNames = {base: "text-large p-1.5"};
-      break;
-    }
-  }
+  const scalingClassNames = {
+    90: {base: "text-[0.7rem]"},
+    95: {base: "text-tiny"},
+    100: {base: "text-small p-0.5"},
+    105: {base: "text-medium p-1"},
+    110: {base: "text-large p-1.5"},
+  };
+  classNames = scalingClassNames[scaling] || classNames;
apps/docs/components/themes/components/configuration/font-button.tsx (2)

18-31: Consider using a constant map for font styles.

The switch statement could be replaced with a more maintainable object map.

Consider this refactor:

+const FONT_STYLES: Record<FontName, FontStyle> = {
+  inter: {fontFamily: "'Inter', sans-serif", letterSpacing: "-0.02em"},
+  roboto: {fontFamily: "'Roboto', sans-serif"},
+  outfit: {fontFamily: "'Outfit', sans-serif", letterSpacing: "0.05em"},
+  lora: {fontFamily: "'Lora', serif"},
+};
+
 function getFontStyle(fontName: FontName): FontStyle {
-  switch (fontName) {
-    case "inter":
-      return {fontFamily: "'Inter', sans-serif", letterSpacing: "-0.02em"};
-    case "roboto":
-      return {fontFamily: "'Roboto', sans-serif"};
-    case "outfit":
-      return {fontFamily: "'Outfit', sans-serif", letterSpacing: "0.05em"};
-    case "lora":
-      return {fontFamily: "'Lora', serif"};
-    default:
-      return {fontFamily: "'Inter', sans-serif", letterSpacing: "-0.02em"};
-  }
+  return FONT_STYLES[fontName] ?? FONT_STYLES.inter;
 }

50-52: Remove empty className attribute.

The empty className attribute on the div is unnecessary.

Apply this diff:

 <div className="relative text-sm text-default-500">
-  <div className="">{title}</div>
+  <div>{title}</div>
 </div>
apps/docs/components/themes/types.ts (3)

2-13: Consider adding hex color validation for color shade values.

The ColorShades interface accepts any string for color values. Consider using a more specific type to ensure valid hex color values.

+type HexColor = `#${string}`;
+
 export interface ColorShades {
-  50: string;
+  50: HexColor;
   // ... apply to all other shades
 }

46-46: Consider using an enum or const object for scaling values.

The magic numbers in HeroUIScaling would be more maintainable as named constants.

-export type HeroUIScaling = 90 | 95 | 100 | 105 | 110;
+export const SCALING = {
+  XXS: 90,
+  XS: 95,
+  BASE: 100,
+  XL: 105,
+  XXL: 110,
+} as const;
+export type HeroUIScaling = typeof SCALING[keyof typeof SCALING];

90-118: Add type safety for numeric configuration values.

Consider using more specific types for numeric values in the configuration to ensure valid inputs.

+type CSSSize = `${number}${'px' | 'rem' | 'em' | '%'}`;
+type Opacity = `${number}%`;

 export interface ConfigLayout {
   fontSize: {
-    tiny: string;
+    tiny: CSSSize;
     // ... apply to all size properties
   };
   otherParams: {
-    disabledOpacity: string;
-    hoverOpacity: string;
+    disabledOpacity: Opacity;
+    hoverOpacity: Opacity;
   };
 }
apps/docs/components/themes/components/showcase/input.tsx (1)

59-80: Replace switch statement with a lookup object for better maintainability.

The switch statement for scaling dimensions could be simplified using a lookup object.

-  switch (scaling) {
-    case 90: {
-      classNames = {base: "h-8 w-[300px]", label: "text-tiny"};
-      break;
-    }
-    case 95: {
-      classNames = {base: "h-8 w-[320px]", label: "text-tiny"};
-      break;
-    }
-    case 100: {
-      classNames = {base: "h-10 w-[340px]", label: "text-small"};
-      break;
-    }
-    case 105: {
-      classNames = {base: "h-12 w-[360px]", label: "text-medium"};
-      break;
-    }
-    case 110: {
-      classNames = {base: "h-12 w-[380px]", label: "text-medium"};
-      break;
-    }
-  }
+  const scalingMap = {
+    90: {base: "h-8 w-[300px]", label: "text-tiny"},
+    95: {base: "h-8 w-[320px]", label: "text-tiny"},
+    100: {base: "h-10 w-[340px]", label: "text-small"},
+    105: {base: "h-12 w-[360px]", label: "text-medium"},
+    110: {base: "h-12 w-[380px]", label: "text-medium"},
+  };
+  classNames = scalingMap[scaling] ?? scalingMap[100];
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf2c2ca and 4e4087c.

📒 Files selected for processing (5)
  • apps/docs/components/themes/components/configuration/font-button.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase-component.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/breadcrumbs.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/input.tsx (1 hunks)
  • apps/docs/components/themes/types.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: TypeScript
  • GitHub Check: Build
🔇 Additional comments (12)
apps/docs/components/themes/components/showcase-component.tsx (2)

1-10: LGTM! Well-structured imports and interface definition.

The imports are minimal and the interface is well-typed with clear prop definitions.


12-23: Excellent implementation of type-safe font configurations!

The code follows best practices with a maintainable FONT_CONFIGS constant and a concise getFontStyle utility function.

apps/docs/components/themes/components/showcase/breadcrumbs.tsx (2)

1-11: LGTM! Clean imports and type definitions.

The imports are well-organized and the type alias improves code readability.


102-113: LGTM! Well-structured showcase component.

The component effectively demonstrates various breadcrumb styles and integrates well with the theme builder.

apps/docs/components/themes/components/configuration/font-button.tsx (1)

57-57: LGTM!

The export statement is correct.

apps/docs/components/themes/types.ts (2)

50-56: LGTM! Well-structured theme type definitions.

The theme types are well-defined and properly extend the color system.


120-128: LGTM! Clear template type definitions.

The template types are well-defined and provide good type safety for the theme templates.

apps/docs/components/themes/components/showcase/input.tsx (5)

1-10: LGTM! Clean imports and type definitions.

The imports are well-organized and the type aliases are properly derived from InputProps.


12-35: Improve type safety and component flexibility.

The previous review comment about type safety remains valid.

  1. The classNames prop is still typed as any
  2. The input label remains hard-coded

Please implement the suggested type improvements from the previous review.


1-125: Well-structured component architecture!

The overall component hierarchy is clean, with good separation of concerns and proper prop drilling. The use of the useThemeBuilder hook for theme customization is appropriate.


114-122: 🛠️ Refactor suggestion

Use color value as key instead of array index.

Using array indices as keys may cause issues with React's reconciliation.

-      {colors.map((color, idx) => (
-        <Section
-          key={idx}
+      {colors.map((color) => (
+        <Section
+          key={color}
           borderWidthValue={borderWidthValue}
           color={color}
           radius={radiusValue}
           scaling={scaling}
         />
       ))}

Likely invalid or redundant comment.


84-96: 🛠️ Refactor suggestion

Use meaningful keys for list items.

Using array indices as keys may lead to rendering issues when items are reordered or removed.

-      {variants.map((variant, idx) => (
-        <SectionBase
-          key={idx}
+      {variants.map((variant) => (
+        <SectionBase
+          key={`${color}-${variant}`}
           classNames={{
             ...classNames,
             inputWrapper: clsx(clsx(variant === "bordered" && borderClass)),
           }}
           color={color}
           isDisabled={false}
           radius={radius}
           variant={variant as Variant}
         />
       ))}

Likely invalid or redundant comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request] Theme generator
2 participants