theme-switcher-nextjs
Version:
A beautifully designed theme switcher component for Next.js apps based on the Vercel's Geist design system.
1 lines • 11.8 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/components/theme-switcher.tsx","../src/icons/index.tsx","../src/lib/utils.ts"],"sourcesContent":["export { ThemeSwitcher } from './components/theme-switcher'\nexport type { ThemeSwitcherProps } from './components/theme-switcher'\nexport { cn } from './lib/utils'","import React, { useEffect, useState } from 'react'\nimport { useTheme } from 'next-themes'\nimport { GearSixIcon, MoonIcon, SunIcon } from '../icons'\nimport { cn } from '../lib/utils'\n\ntype Theme = 'light' | 'dark' | 'system'\ntype Size = 'sm' | 'md' | 'lg'\n\ninterface ThemeSwitcherProps extends React.HTMLAttributes<HTMLDivElement> {\n defaultTheme?: Theme\n themes?: Theme[]\n size?: Size\n includeSystem?: boolean\n}\n\nconst ThemeSwitcher: React.FC<ThemeSwitcherProps> = ({\n className,\n defaultTheme = 'system',\n themes = ['light', 'dark', 'system'],\n size = 'sm',\n includeSystem = true,\n ...props\n}) => {\n const { theme, setTheme } = useTheme()\n const [mounted, setMounted] = useState<boolean>(false)\n\n useEffect(() => {\n setMounted(true)\n }, [])\n\n // filter out system theme if includeSystem is false\n const filteredThemes = includeSystem\n ? themes\n : themes.filter(t => t !== 'system')\n\n // adjust defaultTheme if system is not included\n useEffect(() => {\n if (!includeSystem && theme === 'system') {\n setTheme('light')\n }\n }, [includeSystem, theme, setTheme])\n\n if (!mounted) {\n return null\n }\n\n const getIcon = (t: Theme): React.ReactNode => {\n const iconSize = size === 'sm' ? 16 : size === 'md' ? 24 : 32\n switch (t) {\n case 'light':\n return <SunIcon size={iconSize} className=\"rounded-full\" />\n case 'dark':\n return <MoonIcon size={iconSize} className=\"rounded-full\" />\n case 'system':\n return <GearSixIcon size={iconSize} className=\"rounded-full\" />\n }\n }\n\n return (\n <div\n className={cn(\n 'flex gap-1 rounded-full h-fit w-fit border border-zinc-200 bg-zinc-100 dark:bg-[#1F1F22] dark:border-zinc-600',\n className\n )}\n {...props}\n >\n {filteredThemes.map(t => (\n <button\n key={t}\n type=\"button\"\n onClick={() => setTheme(t)}\n className={cn(\n `${size === 'sm' ? 'h-6 w-6' : size === 'md' ? 'h-10 w-10' : 'h-12 w-12'}`,\n 'inline-flex items-center justify-center whitespace-nowrap text-sm font-medium relative rounded-full bg-zinc-100 dark:bg-[#1F1F22]',\n theme === t\n ? `bg-white hover:bg-white text-zinc-700 dark:text-zinc-50 dark:bg-neutral-900 dark:hover:bg-neutral-900 dark:hover:text-zinc-50 shadow-[0_0_0_1px_rgba(228,228,231,1)] dark:shadow-[0_0_0_1px_rgba(82,82,91,1)]`\n : 'text-zinc-400 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-50'\n )}\n >\n <div\n className=\"transition-transform duration-300 ease-in-out\"\n style={{\n transform: theme === t ? 'scale(1.15)' : 'scale(1)'\n }}\n >\n {getIcon(t)}\n </div>\n <span className=\"sr-only\">\n {t.charAt(0).toUpperCase() + t.slice(1)} theme\n </span>\n </button>\n ))}\n </div>\n )\n}\n\nexport { ThemeSwitcher, type ThemeSwitcherProps }\n","import React from 'react'\n\ninterface IconProps {\n size?: number\n className?: string\n}\n\nexport const SunIcon: React.FC<IconProps> = ({ size = 16, className }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n className={className}\n >\n <rect width=\"256\" height=\"256\" fill=\"none\" />\n <line x1=\"128\" y1=\"40\" x2=\"128\" y2=\"16\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\"\n strokeLinejoin=\"round\" strokeWidth=\"16\" />\n <circle cx=\"128\" cy=\"128\" r=\"56\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\"\n strokeWidth=\"16\" />\n <line x1=\"64\" y1=\"64\" x2=\"48\" y2=\"48\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\"\n strokeLinejoin=\"round\" strokeWidth=\"16\" />\n <line x1=\"64\" y1=\"192\" x2=\"48\" y2=\"208\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\"\n strokeLinejoin=\"round\" strokeWidth=\"16\" />\n <line x1=\"192\" y1=\"64\" x2=\"208\" y2=\"48\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\"\n strokeLinejoin=\"round\" strokeWidth=\"16\" />\n <line x1=\"192\" y1=\"192\" x2=\"208\" y2=\"208\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\"\n strokeLinejoin=\"round\" strokeWidth=\"16\" />\n <line x1=\"40\" y1=\"128\" x2=\"16\" y2=\"128\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\"\n strokeLinejoin=\"round\" strokeWidth=\"16\" />\n <line x1=\"128\" y1=\"216\" x2=\"128\" y2=\"240\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\"\n strokeLinejoin=\"round\" strokeWidth=\"16\" />\n <line x1=\"216\" y1=\"128\" x2=\"240\" y2=\"128\" fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\"\n strokeLinejoin=\"round\" strokeWidth=\"16\" />\n </svg>\n)\n\nexport const MoonIcon: React.FC<IconProps> = ({ size = 16, className }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n className={className}\n >\n <rect width=\"256\" height=\"256\" fill=\"none\" />\n <path d=\"M108.11,28.11A96.09,96.09,0,0,0,227.89,147.89,96,96,0,1,1,108.11,28.11Z\" fill=\"none\" stroke=\"currentColor\"\n strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"16\" />\n </svg>\n)\n\nexport const GearSixIcon: React.FC<IconProps> = ({ size = 16, className }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n className={className}\n >\n <path\n d=\"M128,80a48,48,0,1,0,48,48A48.05,48.05,0,0,0,128,80Zm0,80a32,32,0,1,1,32-32A32,32,0,0,1,128,160Zm109.94-52.79a8,8,0,0,0-3.89-5.4l-29.83-17-.12-33.62a8,8,0,0,0-2.83-6.08,111.91,111.91,0,0,0-36.72-20.67,8,8,0,0,0-6.46.59L128,41.85,97.88,25a8,8,0,0,0-6.47-.6A112.1,112.1,0,0,0,54.73,45.15a8,8,0,0,0-2.83,6.07l-.15,33.65-29.83,17a8,8,0,0,0-3.89,5.4,106.47,106.47,0,0,0,0,41.56,8,8,0,0,0,3.89,5.4l29.83,17,.12,33.62a8,8,0,0,0,2.83,6.08,111.91,111.91,0,0,0,36.72,20.67,8,8,0,0,0,6.46-.59L128,214.15,158.12,231a7.91,7.91,0,0,0,3.9,1,8.09,8.09,0,0,0,2.57-.42,112.1,112.1,0,0,0,36.68-20.73,8,8,0,0,0,2.83-6.07l.15-33.65,29.83-17a8,8,0,0,0,3.89-5.4A106.47,106.47,0,0,0,237.94,107.21Zm-15,34.91-28.57,16.25a8,8,0,0,0-3,3c-.58,1-1.19,2.06-1.81,3.06a7.94,7.94,0,0,0-1.22,4.21l-.15,32.25a95.89,95.89,0,0,1-25.37,14.3L134,199.13a8,8,0,0,0-3.91-1h-.19c-1.21,0-2.43,0-3.64,0a8.08,8.08,0,0,0-4.1,1l-28.84,16.1A96,96,0,0,1,67.88,201l-.11-32.2a8,8,0,0,0-1.22-4.22c-.62-1-1.23-2-1.8-3.06a8.09,8.09,0,0,0-3-3.06l-28.6-16.29a90.49,90.49,0,0,1,0-28.26L61.67,97.63a8,8,0,0,0,3-3c.58-1,1.19-2.06,1.81-3.06a7.94,7.94,0,0,0,1.22-4.21l.15-32.25a95.89,95.89,0,0,1,25.37-14.3L122,56.87a8,8,0,0,0,4.1,1c1.21,0,2.43,0,3.64,0a8.08,8.08,0,0,0,4.1-1l28.84-16.1A96,96,0,0,1,188.12,55l.11,32.2a8,8,0,0,0,1.22,4.22c.62,1,1.23,2,1.8,3.06a8.09,8.09,0,0,0,3,3.06l28.6,16.29A90.49,90.49,0,0,1,222.9,142.12Z\" />\n </svg>\n)\n","import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs))\n}"],"mappings":"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","names":["import_react","React","React"]}