"use client" import * as React from "react" import template from "lodash/template" import { Check, ClipboardIcon, Copy } from "lucide-react" import { useTheme } from "next-themes" import { cn } from "@/lib/utils" import { useConfig } from "@/hooks/use-config" import { copyToClipboardWithMeta } from "@/components/copy-button" import { ThemeWrapper } from "@/components/theme-wrapper" import { Button } from "@/registry/new-york/ui/button" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/registry/new-york/ui/dialog" import { Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle, DrawerTrigger, } from "@/registry/new-york/ui/drawer" import { Label } from "@/registry/new-york/ui/label" import { Popover, PopoverContent, PopoverTrigger, } from "@/registry/new-york/ui/popover" import { Separator } from "@/registry/new-york/ui/separator" import { Skeleton } from "@/registry/new-york/ui/skeleton" import { BaseColor, baseColors, baseColorsOKLCH, } from "@/registry/registry-base-colors" import "@/styles/mdx.css" import { toast } from "sonner" import { Tabs, TabsContent, TabsList, TabsTrigger, } from "@/registry/new-york/ui/tabs" interface BaseColorOKLCH { light: Record dark: Record } export function ThemeCustomizer() { const [config, setConfig] = useConfig() const { resolvedTheme: mode } = useTheme() const [mounted, setMounted] = React.useState(false) React.useEffect(() => { setMounted(true) }, []) return (
) } export function Customizer() { const [mounted, setMounted] = React.useState(false) const { resolvedTheme: mode } = useTheme() const [config, setConfig] = useConfig() React.useEffect(() => { setMounted(true) }, []) return (
{baseColors .filter( (theme) => !["slate", "stone", "gray", "neutral"].includes(theme.name) ) .map((theme) => { const isActive = config.theme === theme.name return mounted ? ( ) : ( ) })}
{["0", "0.3", "0.5", "0.75", "1.0"].map((value) => { return ( ) })}
) } export function CopyCodeButton({ className, ...props }: React.ComponentProps) { return ( <> Theme Copy and paste the following code into your CSS file. Theme Copy and paste the following code into your CSS file. ) } function CustomizerCode() { const [config] = useConfig() const [hasCopied, setHasCopied] = React.useState(false) const [themeVersion, setThemeVersion] = React.useState("v4") const activeTheme = React.useMemo( () => baseColors.find((theme) => theme.name === config.theme), [config.theme] ) const activeThemeOKLCH = React.useMemo( () => baseColorsOKLCH[config.theme as keyof typeof baseColorsOKLCH], [config.theme] ) React.useEffect(() => { if (hasCopied) { setTimeout(() => { setHasCopied(false) }, 2000) } }, [hasCopied]) return (
Tailwind v4 v3
              
                 :root {
                
                     --radius: {config.radius}rem;
                
                {Object.entries(activeThemeOKLCH?.light).map(([key, value]) => (
                  
                       --{key}: {value};
                  
                ))}
                 }
                 
                 .dark {
                {Object.entries(activeThemeOKLCH?.dark).map(([key, value]) => (
                  
                       --{key}: {value};
                  
                ))}
                 }
              
            
              
                @layer base {
                
                    :root {
                
                
                      --background:{" "}
                  {activeTheme?.cssVars.light["background"]};
                
                
                      --foreground:{" "}
                  {activeTheme?.cssVars.light["foreground"]};
                
                {[
                  "card",
                  "popover",
                  "primary",
                  "secondary",
                  "muted",
                  "accent",
                  "destructive",
                ].map((prefix) => (
                  <>
                    
                          --{prefix}:{" "}
                      {
                        activeTheme?.cssVars.light[
                          prefix as keyof typeof activeTheme.cssVars.light
                        ]
                      }
                      ;
                    
                    
                          --{prefix}-foreground:{" "}
                      {
                        activeTheme?.cssVars.light[
                          `${prefix}-foreground` as keyof typeof activeTheme.cssVars.light
                        ]
                      }
                      ;
                    
                  
                ))}
                
                      --border:{" "}
                  {activeTheme?.cssVars.light["border"]};
                
                
                      --input:{" "}
                  {activeTheme?.cssVars.light["input"]};
                
                
                      --ring:{" "}
                  {activeTheme?.cssVars.light["ring"]};
                
                
                      --radius: {config.radius}rem;
                
                {["chart-1", "chart-2", "chart-3", "chart-4", "chart-5"].map(
                  (prefix) => (
                    <>
                      
                            --{prefix}:{" "}
                        {
                          activeTheme?.cssVars.light[
                            prefix as keyof typeof activeTheme.cssVars.light
                          ]
                        }
                        ;
                      
                    
                  )
                )}
                  }
                 
                
                    .dark {
                
                
                      --background:{" "}
                  {activeTheme?.cssVars.dark["background"]};
                
                
                      --foreground:{" "}
                  {activeTheme?.cssVars.dark["foreground"]};
                
                {[
                  "card",
                  "popover",
                  "primary",
                  "secondary",
                  "muted",
                  "accent",
                  "destructive",
                ].map((prefix) => (
                  <>
                    
                          --{prefix}:{" "}
                      {
                        activeTheme?.cssVars.dark[
                          prefix as keyof typeof activeTheme.cssVars.dark
                        ]
                      }
                      ;
                    
                    
                          --{prefix}-foreground:{" "}
                      {
                        activeTheme?.cssVars.dark[
                          `${prefix}-foreground` as keyof typeof activeTheme.cssVars.dark
                        ]
                      }
                      ;
                    
                  
                ))}
                
                      --border:{" "}
                  {activeTheme?.cssVars.dark["border"]};
                
                
                      --input:{" "}
                  {activeTheme?.cssVars.dark["input"]};
                
                
                      --ring:{" "}
                  {activeTheme?.cssVars.dark["ring"]};
                
                {["chart-1", "chart-2", "chart-3", "chart-4", "chart-5"].map(
                  (prefix) => (
                    <>
                      
                            --{prefix}:{" "}
                        {
                          activeTheme?.cssVars.dark[
                            prefix as keyof typeof activeTheme.cssVars.dark
                          ]
                        }
                        ;
                      
                    
                  )
                )}
                  }
                }
              
            
) } function getThemeCodeOKLCH(theme: BaseColorOKLCH | undefined, radius: number) { if (!theme) { return "" } const rootSection = ":root {\n --radius: " + radius + "rem;\n" + Object.entries(theme.light) .map((entry) => " --" + entry[0] + ": " + entry[1] + ";") .join("\n") + "\n}\n\n.dark {\n" + Object.entries(theme.dark) .map((entry) => " --" + entry[0] + ": " + entry[1] + ";") .join("\n") + "\n}\n" return rootSection } function getThemeCode(theme: BaseColor | undefined, radius: number) { if (!theme) { return "" } return template(BASE_STYLES_WITH_VARIABLES)({ colors: theme.cssVars, radius: radius.toString(), }) } const BASE_STYLES_WITH_VARIABLES = ` @layer base { :root { --background: <%- colors.light["background"] %>; --foreground: <%- colors.light["foreground"] %>; --card: <%- colors.light["card"] %>; --card-foreground: <%- colors.light["card-foreground"] %>; --popover: <%- colors.light["popover"] %>; --popover-foreground: <%- colors.light["popover-foreground"] %>; --primary: <%- colors.light["primary"] %>; --primary-foreground: <%- colors.light["primary-foreground"] %>; --secondary: <%- colors.light["secondary"] %>; --secondary-foreground: <%- colors.light["secondary-foreground"] %>; --muted: <%- colors.light["muted"] %>; --muted-foreground: <%- colors.light["muted-foreground"] %>; --accent: <%- colors.light["accent"] %>; --accent-foreground: <%- colors.light["accent-foreground"] %>; --destructive: <%- colors.light["destructive"] %>; --destructive-foreground: <%- colors.light["destructive-foreground"] %>; --border: <%- colors.light["border"] %>; --input: <%- colors.light["input"] %>; --ring: <%- colors.light["ring"] %>; --radius: <%- radius %>rem; --chart-1: <%- colors.light["chart-1"] %>; --chart-2: <%- colors.light["chart-2"] %>; --chart-3: <%- colors.light["chart-3"] %>; --chart-4: <%- colors.light["chart-4"] %>; --chart-5: <%- colors.light["chart-5"] %>; } .dark { --background: <%- colors.dark["background"] %>; --foreground: <%- colors.dark["foreground"] %>; --card: <%- colors.dark["card"] %>; --card-foreground: <%- colors.dark["card-foreground"] %>; --popover: <%- colors.dark["popover"] %>; --popover-foreground: <%- colors.dark["popover-foreground"] %>; --primary: <%- colors.dark["primary"] %>; --primary-foreground: <%- colors.dark["primary-foreground"] %>; --secondary: <%- colors.dark["secondary"] %>; --secondary-foreground: <%- colors.dark["secondary-foreground"] %>; --muted: <%- colors.dark["muted"] %>; --muted-foreground: <%- colors.dark["muted-foreground"] %>; --accent: <%- colors.dark["accent"] %>; --accent-foreground: <%- colors.dark["accent-foreground"] %>; --destructive: <%- colors.dark["destructive"] %>; --destructive-foreground: <%- colors.dark["destructive-foreground"] %>; --border: <%- colors.dark["border"] %>; --input: <%- colors.dark["input"] %>; --ring: <%- colors.dark["ring"] %>; --chart-1: <%- colors.dark["chart-1"] %>; --chart-2: <%- colors.dark["chart-2"] %>; --chart-3: <%- colors.dark["chart-3"] %>; --chart-4: <%- colors.dark["chart-4"] %>; --chart-5: <%- colors.dark["chart-5"] %>; } } `