feat: implement light and dark theme (#30)
This commit is contained in:
@@ -8,6 +8,7 @@ interface BaseIconButtonProps {
|
||||
className?: string;
|
||||
iconClassName?: string;
|
||||
disabledClassName?: string;
|
||||
title?: string;
|
||||
disabled?: boolean;
|
||||
onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
}
|
||||
@@ -32,18 +33,20 @@ export const IconButton = memo(
|
||||
iconClassName,
|
||||
disabledClassName,
|
||||
disabled = false,
|
||||
title,
|
||||
onClick,
|
||||
children,
|
||||
}: IconButtonProps) => {
|
||||
return (
|
||||
<button
|
||||
className={classNames(
|
||||
'flex items-center text-gray-600 bg-transparent enabled:hover:text-gray-900 rounded-md p-1 enabled:hover:bg-gray-200/80 disabled:cursor-not-allowed',
|
||||
'flex items-center text-bolt-elements-item-contentDefault bg-transparent enabled:hover:text-bolt-elements-item-contentActive rounded-md p-1 enabled:hover:bg-bolt-elements-item-backgroundActive disabled:cursor-not-allowed',
|
||||
{
|
||||
[classNames('opacity-30', disabledClassName)]: disabled,
|
||||
},
|
||||
className,
|
||||
)}
|
||||
title={title}
|
||||
disabled={disabled}
|
||||
onClick={(event) => {
|
||||
if (disabled) {
|
||||
|
||||
@@ -8,7 +8,12 @@ interface PanelHeaderProps {
|
||||
|
||||
export const PanelHeader = memo(({ className, children }: PanelHeaderProps) => {
|
||||
return (
|
||||
<div className={classNames('flex items-center gap-2 bg-gray-50 border-b px-4 py-1 min-h-[34px]', className)}>
|
||||
<div
|
||||
className={classNames(
|
||||
'flex items-center gap-2 bg-bolt-elements-background-depth-2 text-bolt-elements-textSecondary border-b border-bolt-elements-borderColor px-4 py-1 min-h-[34px] text-sm',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ export const PanelHeaderButton = memo(
|
||||
return (
|
||||
<button
|
||||
className={classNames(
|
||||
'flex items-center gap-1.5 px-1.5 rounded-md py-0.5 bg-transparent hover:bg-white disabled:cursor-not-allowed',
|
||||
'flex items-center gap-1.5 px-1.5 rounded-md py-0.5 text-bolt-elements-item-contentDefault bg-transparent enabled:hover:text-bolt-elements-item-contentActive enabled:hover:bg-bolt-elements-item-backgroundActive disabled:cursor-not-allowed',
|
||||
{
|
||||
[classNames('opacity-30', disabledClassName)]: disabled,
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ export const Slider = genericMemo(<T,>({ selected, options, setSelected }: Slide
|
||||
const isLeftSelected = selected === options.left.value;
|
||||
|
||||
return (
|
||||
<div className="flex items-center flex-wrap gap-1 border rounded-lg p-1">
|
||||
<div className="flex items-center flex-wrap gap-1 bg-bolt-elements-background-depth-1 rounded-full p-1">
|
||||
<SliderButton selected={isLeftSelected} setSelected={() => setSelected?.(options.left.value)}>
|
||||
{options.left.text}
|
||||
</SliderButton>
|
||||
@@ -45,8 +45,10 @@ const SliderButton = memo(({ selected, children, setSelected }: SliderButtonProp
|
||||
<button
|
||||
onClick={setSelected}
|
||||
className={classNames(
|
||||
'bg-transparent text-sm transition-colors px-2.5 py-0.5 rounded-md relative',
|
||||
selected ? 'text-white' : 'text-gray-600 hover:text-accent-600 hover:bg-accent-600/10',
|
||||
'bg-transparent text-sm px-2.5 py-0.5 rounded-full relative',
|
||||
selected
|
||||
? 'text-bolt-elements-item-contentAccent'
|
||||
: 'text-bolt-elements-item-contentDefault hover:text-bolt-elements-item-contentActive',
|
||||
)}
|
||||
>
|
||||
<span className="relative z-10">{children}</span>
|
||||
@@ -54,7 +56,7 @@ const SliderButton = memo(({ selected, children, setSelected }: SliderButtonProp
|
||||
<motion.span
|
||||
layoutId="pill-tab"
|
||||
transition={{ type: 'spring', duration: 0.5 }}
|
||||
className="absolute inset-0 z-0 bg-accent-600 rounded-md"
|
||||
className="absolute inset-0 z-0 bg-bolt-elements-item-backgroundAccent rounded-full"
|
||||
></motion.span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
29
packages/bolt/app/components/ui/ThemeSwitch.tsx
Normal file
29
packages/bolt/app/components/ui/ThemeSwitch.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { themeStore, toggleTheme } from '~/lib/stores/theme';
|
||||
import { IconButton } from './IconButton';
|
||||
|
||||
interface ThemeSwitchProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ThemeSwitch = memo(({ className }: ThemeSwitchProps) => {
|
||||
const theme = useStore(themeStore);
|
||||
const [domLoaded, setDomLoaded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setDomLoaded(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
domLoaded && (
|
||||
<IconButton
|
||||
className={className}
|
||||
icon={theme === 'dark' ? 'i-ph-sun-dim-duotone' : 'i-ph-moon-stars-duotone'}
|
||||
size="xl"
|
||||
title="Toggle Theme"
|
||||
onClick={toggleTheme}
|
||||
/>
|
||||
)
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user