diff --git a/src/components/color-toggle.jsx b/src/components/color-toggle.jsx new file mode 100644 index 000000000..1f9fe84c2 --- /dev/null +++ b/src/components/color-toggle.jsx @@ -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 ( +
+ + {({ open }) => ( + <> + + + + +
+
+ {colors.map((color) => ( + + ))} +
+
+
+
+ + )} +
+
+ ); +} diff --git a/src/pages/_app.js b/src/pages/_app.js index 2607bc9d3..58e3293b9 100644 --- a/src/pages/_app.js +++ b/src/pages/_app.js @@ -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 ( - fetch(resource, init).then((res) => res.json()), + fetcher: (resource, init) => fetch(resource, init).then((res) => res.json()), }} > diff --git a/src/pages/_document.js b/src/pages/_document.js index d69d90447..2a2626c2d 100644 --- a/src/pages/_document.js +++ b/src/pages/_document.js @@ -5,11 +5,7 @@ export default function Document() { - + 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 ( - - - Welcome - -
-
- {widgets && ( - <> - {widgets.map((widget) => ( - + + + + Welcome + +
+
+ {widgets && ( + <> + {widgets.map((widget) => ( + + ))} + + )} +
+ + {services && ( +
+ {services.map((group) => ( + ))} - +
)} -
- {services && ( -
- {services.map((group) => ( - - ))} -
- )} + {bookmarks && ( +
+ {bookmarks.map((group) => ( + + ))} +
+ )} - {bookmarks && ( -
- {bookmarks.map((group) => ( - - ))} +
+ +
- )} - -
-
-
-
+ +
); } diff --git a/src/styles/theme.css b/src/styles/theme.css new file mode 100644 index 000000000..e33fcf835 --- /dev/null +++ b/src/styles/theme.css @@ -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; +} diff --git a/src/utils/color-context.js b/src/utils/color-context.js new file mode 100644 index 000000000..08063b23a --- /dev/null +++ b/src/utils/color-context.js @@ -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 {children}; +}; diff --git a/src/utils/theme-context.js b/src/utils/theme-context.js index 49761633c..476fcb8df 100644 --- a/src/utils/theme-context.js +++ b/src/utils/theme-context.js @@ -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 ( - - {children} - - ); + return {children}; }; diff --git a/tailwind.config.js b/tailwind.config.js index b81ca6d25..973cfa772 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -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) / )", + ["100"]: "rgb(var(--color-100) / )", + ["200"]: "rgb(var(--color-200) / )", + ["300"]: "rgb(var(--color-300) / )", + ["400"]: "rgb(var(--color-400) / )", + ["500"]: "rgb(var(--color-500) / )", + ["600"]: "rgb(var(--color-600) / )", + ["700"]: "rgb(var(--color-700) / )", + ["800"]: "rgb(var(--color-800) / )", + ["900"]: "rgb(var(--color-900) / )", + }, + }, }, }, plugins: [require("@tailwindcss/forms")],