add theme switcher

pull/3/head
Ben Phelps 2 years ago
parent 5711b22b4e
commit 1b2fa720c6

@ -0,0 +1,81 @@
import { useContext } from "react";
import { IoColorPalette } from "react-icons/io5";
import { Popover, Transition } from "@headlessui/react";
import { Fragment } from "react";
import { ColorContext } from "utils/color-context";
const colors = [
"slate",
"gray",
"zinc",
"neutral",
"stone",
"amber",
"yellow",
"lime",
"green",
"emerald",
"teal",
"cyan",
"sky",
"blue",
"indigo",
"violet",
"purple",
"fuchsia",
"pink",
"rose",
"red",
];
export default function ColorToggle() {
const { color: active, setColor } = useContext(ColorContext);
if (!active) {
return null;
}
return (
<div className="w-full self-center">
<Popover className="relative flex items-center">
{({ open }) => (
<>
<Popover.Button className="outline-none">
<IoColorPalette
className="h-5 w-5 text-theme-800 dark:text-theme-200 transition duration-150 ease-in-out"
aria-hidden="true"
/>
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute -top-[75px] left-0">
<div className="rounded-md shadow-lg ring-1 ring-black ring-opacity-5">
<div className="relative grid gap-2 p-2 grid-cols-11 shadow-theme-900/10 dark:shadow-theme-900 rounded-md shadow-md">
{colors.map((color) => (
<button role="button" onClick={() => setColor(color)} key={color}>
<div
className={
(active == color ? "border-2" : "border-0") +
` rounded-md w-5 h-5 border-black/50 dark:border-white/50 theme-${color} bg-theme-500`
}
/>
</button>
))}
</div>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
</div>
);
}

@ -1,13 +1,13 @@
import { SWRConfig } from "swr";
import "styles/globals.css";
import "styles/weather-icons.css";
import "styles/theme.css";
function MyApp({ Component, pageProps }) {
return (
<SWRConfig
value={{
fetcher: (resource, init) =>
fetch(resource, init).then((res) => res.json()),
fetcher: (resource, init) => fetch(resource, init).then((res) => res.json()),
}}
>
<Component {...pageProps} />

@ -5,11 +5,7 @@ export default function Document() {
<Html>
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin="true"
/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="true" />
<link
href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400;1,500&display=swap"
rel="stylesheet"

@ -7,52 +7,60 @@ import { ThemeProvider } from "utils/theme-context";
import ServicesGroup from "components/services/group";
import BookmarksGroup from "components/bookmarks/group";
import Widget from "components/widget";
import { ColorProvider } from "utils/color-context";
const ThemeToggle = dynamic(() => import("components/theme-toggle"), {
ssr: false,
});
const ColorToggle = dynamic(() => import("components/color-toggle"), {
ssr: false,
});
export default function Home() {
const { data: services, error: servicesError } = useSWR("/api/services");
const { data: bookmarks, error: bookmarksError } = useSWR("/api/bookmarks");
const { data: widgets, error: widgetsError } = useSWR("/api/widgets");
return (
<ThemeProvider>
<Head>
<title>Welcome</title>
</Head>
<div className="w-full container m-auto flex flex-col h-screen justify-between">
<div className="flex flex-wrap m-8 pb-4 mt-10 border-b-2 border-theme-800 dark:border-theme-200">
{widgets && (
<>
{widgets.map((widget) => (
<Widget key={widget.type} widget={widget} />
<ColorProvider>
<ThemeProvider>
<Head>
<title>Welcome</title>
</Head>
<div className="w-full container m-auto flex flex-col h-screen justify-between">
<div className="flex flex-wrap m-8 pb-4 mt-10 border-b-2 border-theme-800 dark:border-theme-200">
{widgets && (
<>
{widgets.map((widget) => (
<Widget key={widget.type} widget={widget} />
))}
</>
)}
</div>
{services && (
<div className="flex flex-wrap p-8 items-start">
{services.map((group) => (
<ServicesGroup key={group.name} services={group} />
))}
</>
</div>
)}
</div>
{services && (
<div className="flex flex-wrap p-8 items-start">
{services.map((group) => (
<ServicesGroup key={group.name} services={group} />
))}
</div>
)}
{bookmarks && (
<div className="grow flex flex-wrap pt-0 p-8">
{bookmarks.map((group) => (
<BookmarksGroup key={group.name} group={group} />
))}
</div>
)}
{bookmarks && (
<div className="grow flex flex-wrap pt-0 p-8">
{bookmarks.map((group) => (
<BookmarksGroup key={group.name} group={group} />
))}
<div className="rounded-full flex p-8 w-full justify-between">
<ColorToggle />
<ThemeToggle />
</div>
)}
<div className="rounded-full flex p-8 w-full justify-end">
<ThemeToggle />
</div>
</div>
</ThemeProvider>
</ThemeProvider>
</ColorProvider>
);
}

@ -0,0 +1,285 @@
.theme-slate {
--color-50: 248 250 252;
--color-100: 241 245 249;
--color-200: 226 232 240;
--color-300: 203 213 225;
--color-400: 148 163 184;
--color-500: 100 116 139;
--color-600: 71 85 105;
--color-700: 51 65 85;
--color-800: 30 41 59;
--color-900: 15 23 42;
}
.theme-gray {
--color-50: 249 250 251;
--color-100: 243 244 246;
--color-200: 229 231 235;
--color-300: 209 213 219;
--color-400: 156 163 175;
--color-500: 107 114 128;
--color-600: 75 85 99;
--color-700: 55 65 81;
--color-800: 31 41 55;
--color-900: 17 24 39;
}
.theme-zinc {
--color-50: 250 250 250;
--color-100: 244 244 245;
--color-200: 228 228 231;
--color-300: 212 212 216;
--color-400: 161 161 170;
--color-500: 113 113 122;
--color-600: 82 82 91;
--color-700: 63 63 70;
--color-800: 39 39 42;
--color-900: 24 24 27;
}
.theme-neutral {
--color-50: 250 250 250;
--color-100: 245 245 245;
--color-200: 229 229 229;
--color-300: 212 212 212;
--color-400: 163 163 163;
--color-500: 115 115 115;
--color-600: 82 82 82;
--color-700: 64 64 64;
--color-800: 38 38 38;
--color-900: 23 23 23;
}
.theme-stone {
--color-50: 250 250 249;
--color-100: 245 245 244;
--color-200: 231 229 228;
--color-300: 214 211 209;
--color-400: 168 162 158;
--color-500: 120 113 108;
--color-600: 87 83 78;
--color-700: 68 64 60;
--color-800: 41 37 36;
--color-900: 28 25 23;
}
.theme-red {
--color-50: 254 242 242;
--color-100: 254 226 226;
--color-200: 254 202 202;
--color-300: 252 165 165;
--color-400: 248 113 113;
--color-500: 239 68 68;
--color-600: 220 38 38;
--color-700: 185 28 28;
--color-800: 153 27 27;
--color-900: 127 29 29;
}
.theme-orange {
--color-50: 255 247 237;
--color-100: 255 237 213;
--color-200: 254 215 170;
--color-300: 253 186 116;
--color-400: 251 146 60;
--color-500: 249 115 22;
--color-600: 234 88 12;
--color-700: 194 65 12;
--color-800: 154 52 18;
--color-900: 124 45 18;
}
.theme-amber {
--color-50: 255 251 235;
--color-100: 254 243 199;
--color-200: 253 230 138;
--color-300: 252 211 77;
--color-400: 251 191 36;
--color-500: 245 158 11;
--color-600: 217 119 6;
--color-700: 180 83 9;
--color-800: 146 64 14;
--color-900: 120 53 15;
}
.theme-yellow {
--color-50: 254 252 232;
--color-100: 254 249 195;
--color-200: 254 240 138;
--color-300: 253 224 71;
--color-400: 250 204 21;
--color-500: 234 179 8;
--color-600: 202 138 4;
--color-700: 161 98 7;
--color-800: 133 77 14;
--color-900: 113 63 18;
}
.theme-lime {
--color-50: 247 254 231;
--color-100: 236 252 203;
--color-200: 217 249 157;
--color-300: 190 242 100;
--color-400: 163 230 53;
--color-500: 132 204 22;
--color-600: 101 163 13;
--color-700: 77 124 15;
--color-800: 63 98 18;
--color-900: 54 83 20;
}
.theme-green {
--color-50: 240 253 244;
--color-100: 220 252 231;
--color-200: 187 247 208;
--color-300: 134 239 172;
--color-400: 74 222 128;
--color-500: 34 197 94;
--color-600: 22 163 74;
--color-700: 21 128 61;
--color-800: 22 101 52;
--color-900: 20 83 45;
}
.theme-emerald {
--color-50: 236 253 245;
--color-100: 209 250 229;
--color-200: 167 243 208;
--color-300: 110 231 183;
--color-400: 52 211 153;
--color-500: 16 185 129;
--color-600: 5 150 105;
--color-700: 4 120 87;
--color-800: 6 95 70;
--color-900: 6 78 59;
}
.theme-teal {
--color-50: 240 253 250;
--color-100: 204 251 241;
--color-200: 153 246 228;
--color-300: 94 234 212;
--color-400: 45 212 191;
--color-500: 20 184 166;
--color-600: 13 148 136;
--color-700: 15 118 110;
--color-800: 17 94 89;
--color-900: 19 78 74;
}
.theme-cyan {
--color-50: 236 254 255;
--color-100: 207 250 254;
--color-200: 165 243 252;
--color-300: 103 232 249;
--color-400: 34 211 238;
--color-500: 6 182 212;
--color-600: 8 145 178;
--color-700: 14 116 144;
--color-800: 21 94 117;
--color-900: 22 78 99;
}
.theme-sky {
--color-50: 240 249 255;
--color-100: 224 242 254;
--color-200: 186 230 253;
--color-300: 125 211 252;
--color-400: 56 189 248;
--color-500: 14 165 233;
--color-600: 2 132 199;
--color-700: 3 105 161;
--color-800: 7 89 133;
--color-900: 12 74 110;
}
.theme-blue {
--color-50: 239 246 255;
--color-100: 219 234 254;
--color-200: 191 219 254;
--color-300: 147 197 253;
--color-400: 96 165 250;
--color-500: 59 130 246;
--color-600: 37 99 235;
--color-700: 29 78 216;
--color-800: 30 64 175;
--color-900: 30 58 138;
}
.theme-indigo {
--color-50: 238 242 255;
--color-100: 224 231 255;
--color-200: 199 210 254;
--color-300: 165 180 252;
--color-400: 129 140 248;
--color-500: 99 102 241;
--color-600: 79 70 229;
--color-700: 67 56 202;
--color-800: 55 48 163;
--color-900: 49 46 129;
}
.theme-violet {
--color-50: 245 243 255;
--color-100: 237 233 254;
--color-200: 221 214 254;
--color-300: 196 181 253;
--color-400: 167 139 250;
--color-500: 139 92 246;
--color-600: 124 58 237;
--color-700: 109 40 217;
--color-800: 91 33 182;
--color-900: 76 29 149;
}
.theme-purple {
--color-50: 250 245 255;
--color-100: 243 232 255;
--color-200: 233 213 255;
--color-300: 216 180 254;
--color-400: 192 132 252;
--color-500: 168 85 247;
--color-600: 147 51 234;
--color-700: 126 34 206;
--color-800: 107 33 168;
--color-900: 88 28 135;
}
.theme-fuchsia {
--color-50: 253 244 255;
--color-100: 250 232 255;
--color-200: 245 208 254;
--color-300: 240 171 252;
--color-400: 232 121 249;
--color-500: 217 70 239;
--color-600: 192 38 211;
--color-700: 162 28 175;
--color-800: 134 25 143;
--color-900: 112 26 117;
}
.theme-pink {
--color-50: 253 242 248;
--color-100: 252 231 243;
--color-200: 251 207 232;
--color-300: 249 168 212;
--color-400: 244 114 182;
--color-500: 236 72 153;
--color-600: 219 39 119;
--color-700: 190 24 93;
--color-800: 157 23 77;
--color-900: 131 24 67;
}
.theme-rose {
--color-50: 255 241 242;
--color-100: 255 228 230;
--color-200: 254 205 211;
--color-300: 253 164 175;
--color-400: 251 113 133;
--color-500: 244 63 94;
--color-600: 225 29 72;
--color-700: 190 18 60;
--color-800: 159 18 57;
--color-900: 136 19 55;
}

@ -0,0 +1,42 @@
import { createContext, useState, useEffect } from "react";
let lastColor = false;
const getInitialColor = () => {
if (typeof window !== "undefined" && window.localStorage) {
const storedPrefs = window.localStorage.getItem("theme-color");
if (typeof storedPrefs === "string") {
lastColor = storedPrefs;
return storedPrefs;
}
}
return "slate"; // slate as the default color;
};
export const ColorContext = createContext();
export const ColorProvider = ({ initialTheme, children }) => {
const [color, setColor] = useState(getInitialColor);
const rawSetColor = (rawColor) => {
const root = window.document.documentElement;
root.classList.remove(`theme-${lastColor}`);
root.classList.add(`theme-${rawColor}`);
localStorage.setItem("theme-color", rawColor);
lastColor = rawColor;
};
if (initialTheme) {
rawSetColor(initialTheme);
}
useEffect(() => {
rawSetColor(color);
}, [color]);
return <ColorContext.Provider value={{ color, setColor }}>{children}</ColorContext.Provider>;
};

@ -2,7 +2,7 @@ import { createContext, useState, useEffect } from "react";
const getInitialTheme = () => {
if (typeof window !== "undefined" && window.localStorage) {
const storedPrefs = window.localStorage.getItem("color-theme");
const storedPrefs = window.localStorage.getItem("theme-mode");
if (typeof storedPrefs === "string") {
return storedPrefs;
}
@ -13,7 +13,7 @@ const getInitialTheme = () => {
}
}
return "light"; // light theme as the default;
return "dark"; // dark as the default mode
};
export const ThemeContext = createContext();
@ -28,7 +28,7 @@ export const ThemeProvider = ({ initialTheme, children }) => {
root.classList.remove(isDark ? "light" : "dark");
root.classList.add(rawTheme);
localStorage.setItem("color-theme", rawTheme);
localStorage.setItem("theme-mode", rawTheme);
};
if (initialTheme) {
@ -39,9 +39,5 @@ export const ThemeProvider = ({ initialTheme, children }) => {
rawSetTheme(theme);
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
};

@ -1,15 +1,23 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: "class",
content: [
"./src/pages/**/*.{js,ts,jsx,tsx}",
"./src/components/**/*.{js,ts,jsx,tsx}",
],
content: ["./src/pages/**/*.{js,ts,jsx,tsx}", "./src/components/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
colors: ({ colors }) => ({
theme: colors.slate,
}),
colors: {
theme: {
["50"]: "rgb(var(--color-50) / <alpha-value>)",
["100"]: "rgb(var(--color-100) / <alpha-value>)",
["200"]: "rgb(var(--color-200) / <alpha-value>)",
["300"]: "rgb(var(--color-300) / <alpha-value>)",
["400"]: "rgb(var(--color-400) / <alpha-value>)",
["500"]: "rgb(var(--color-500) / <alpha-value>)",
["600"]: "rgb(var(--color-600) / <alpha-value>)",
["700"]: "rgb(var(--color-700) / <alpha-value>)",
["800"]: "rgb(var(--color-800) / <alpha-value>)",
["900"]: "rgb(var(--color-900) / <alpha-value>)",
},
},
},
},
plugins: [require("@tailwindcss/forms")],

Loading…
Cancel
Save