rinlab
Version:
A comprehensive React component library for Solana blockchain interfaces with a nostalgic retro-computing aesthetic
188 lines (180 loc) • 4.62 kB
text/typescript
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
export type ThemePalette = {
name: string;
label: string;
colors: {
background: string;
foreground: string;
primary: string;
secondary: string;
accent: string;
border: string;
hover: string;
muted: string;
};
};
export const themePalettes: ThemePalette[] = [
{
name: 'night',
label: 'NIGHT',
colors: {
background: '0 0% 10%',
foreground: '0 0% 90%',
primary: '0 0% 90%',
secondary: '0 0% 70%',
accent: '0 0% 70%',
border: '0 0% 30%',
hover: '0 0% 15%',
muted: '0 0% 70%'
}
},
{
name: 'day',
label: 'DAY',
colors: {
background: '0 0% 98%',
foreground: '0 0% 10%',
primary: '0 0% 10%',
secondary: '0 0% 30%',
accent: '0 0% 30%',
border: '0 0% 80%',
hover: '0 0% 95%',
muted: '0 0% 30%'
}
},
{
name: 'dos',
label: 'DOS',
colors: {
background: '240 100% 27%',
foreground: '0 0% 100%',
primary: '0 0% 100%',
secondary: '240 30% 80%',
accent: '240 30% 80%',
border: '240 50% 60%',
hover: '240 100% 32%',
muted: '240 30% 80%'
}
},
{
name: 'cyberpunk',
label: 'CYBERPUNK',
colors: {
background: '300 100% 5%',
foreground: '315 100% 65%',
primary: '315 100% 65%',
secondary: '285 100% 60%',
accent: '285 100% 60%',
border: '315 100% 25%',
hover: '300 100% 8%',
muted: '285 50% 60%'
}
},
{
name: 'solarized',
label: 'SOLARIZED',
colors: {
background: '44 87% 98%',
foreground: '192 81% 29%',
primary: '192 81% 29%',
secondary: '180 30% 45%',
accent: '180 30% 45%',
border: '44 50% 85%',
hover: '44 87% 95%',
muted: '180 30% 45%'
}
}
];
type ThemeStore = {
currentPalette: ThemePalette;
setTheme: (palette: ThemePalette) => void;
updateColor: (key: keyof ThemePalette['colors'], value: string) => void;
resetTheme: () => void;
exportTheme: () => string;
importTheme: (themeString: string) => boolean;
};
export const useTheme = create<ThemeStore>()(
persist(
(set, get) => ({
currentPalette: themePalettes[2], // DOS theme as default
setTheme: (palette) => {
set({ currentPalette: palette });
updateCssVariables(palette.colors);
},
updateColor: (key, value) => {
set((state) => {
const newPalette = {
...state.currentPalette,
colors: { ...state.currentPalette.colors, [key]: value }
};
updateCssVariables(newPalette.colors);
return { currentPalette: newPalette };
});
},
resetTheme: () => {
const defaultPalette = themePalettes[2];
set({ currentPalette: defaultPalette });
updateCssVariables(defaultPalette.colors);
},
exportTheme: () => {
const state = get();
const themeData = {
name: state.currentPalette.name,
label: state.currentPalette.label,
colors: state.currentPalette.colors
};
return btoa(JSON.stringify(themeData));
},
importTheme: (themeString: string) => {
try {
const themeData = JSON.parse(atob(themeString));
const newPalette: ThemePalette = {
name: themeData.name,
label: themeData.label,
colors: themeData.colors
};
set({ currentPalette: newPalette });
updateCssVariables(newPalette.colors);
return true;
} catch (error) {
console.error('Failed to import theme:', error);
return false;
}
}
}),
{
name: 'theme-storage',
onRehydrateStorage: () => (state) => {
if (state && state.currentPalette) {
updateCssVariables(state.currentPalette.colors);
}
}
}
)
);
function updateCssVariables(colors: ThemePalette['colors']) {
if (typeof window !== 'undefined') {
const root = document.documentElement;
Object.entries(colors).forEach(([key, value]) => {
root.style.setProperty(`--${key}`, value);
});
}
}
export const defaultTheme = themePalettes[2]; // DOS theme as default
export const theme = {
fonts: {
mono: "'IBM Plex Mono', monospace",
},
spacing: {
grid: '8px',
},
transitions: {
default: '0.2s ease',
slow: '0.4s ease',
}
};
export const borderStyles = {
default: '1px solid hsl(var(--border))',
glowing: 'hsl(var(--glow))'
};