diff --git a/client/src/theme.js b/client/src/theme.js index 807bcf7..19e3904 100644 --- a/client/src/theme.js +++ b/client/src/theme.js @@ -1,6 +1,5 @@ import {defineSlotRecipe, createSystem, defaultConfig, defineConfig, defineRecipe} from "@chakra-ui/react"; - // REUSABLE GLASS SURFACE const glassSurface = { bg: {base: "rgba(255, 255, 255, 0.4)", _dark: "rgba(255, 255, 255, 0.04)"}, @@ -69,8 +68,7 @@ const sidebarRecipe = defineSlotRecipe({ root: { // 1. Semi-transparent backgrounds bg: { - // base: "linear-gradient(to bottom right, #99000040 20%, #FF660040 60%, #EEEE0040 100%)", // this is too pale for light mode imo - base: "linear-gradient(to bottom right, #97282880 20%, #EB7F3160 60%, #FCAD3840 100%)", // colors from: https://colorhunt.co/palette/972828e45742eb7f31fcad38 + base: "linear-gradient(to bottom right, #97282880 20%, #EB7F3160 60%, #FCAD3840 100%)", _dark: "linear-gradient(to bottom right, #9900003C 20%, #FF66003C 60%, #FFC0003C 100%)" }, @@ -295,248 +293,43 @@ const homeRecipe = defineSlotRecipe({ }, }); -// 5. Glass Panel Surfaces (Separated: Widget, Dropdown, Popover, Dialog) -const glassPanelRecipe = defineSlotRecipe({ +// 5. Dashboard Widget Card +const widgetRecipe = defineSlotRecipe({ slots: ["root", "header", "body"], - - // 🧊 THE GLASS MATH (Applies to everything) base: { root: { display: "flex", flexDirection: "column", position: "relative", ...glassSurface, + width: "100%", + height: "100%", + borderRadius: "3xl", + overflow: "hidden", }, + header: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + px: 3, + pt: 2, + pb: 0, + "& > p": { + color: "titleColor", + fontWeight: "500", + fontSize: "16px", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "clip", + }, + "& > button": { + borderRadius: "xl", + _hover: {color: "white", bg: "gray.500"}, + }, + }, + body: {flex: "1", overflow: "hidden", position: "relative", px: 1, pb: 1, pt: 0}, }, - - // 📏 THE LAYOUTS (Applies selectively) variants: { - layout: { - // Dashboard Widget Card - widget: { - root: { - width: "100%", - height: "100%", - borderRadius: "3xl", - overflow: "hidden", - }, - header: { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - px: 3, - pt: 2, - pb: 0, - - "& > p": { - color: "titleColor", - fontWeight: "500", - fontSize: "16px", - - // the following three specifications are equivalent to Chakra's 'truncate' prop - whiteSpace: "nowrap", - overflow: "hidden", - textOverflow: "clip", - }, - - "& > button": { - borderRadius: "xl", - - _hover: { - color: "white", - bg: "gray.500", - }, - }, - }, - body: { - flex: "1", - overflow: "hidden", - position: "relative", - px: 1, - pb: 1, - pt: 0, - }, - }, - dropdown: { - root: { - borderRadius: "xl", - p: 1, - }, - }, - - // 🎯 TRUE POPOVER: Compact floating card for Widget Settings (3-dots menu) - popover: { - root: { - borderRadius: "xl", - p: 3, - boxShadow: "lg", - bg: { - base: "rgba(255, 255, 255, 0.85)", - _dark: "rgba(20, 22, 25, 0.85)", - }, - backdropFilter: "blur(15px)", - WebkitBackdropFilter: "blur(15px)", - }, - }, - - // 🎯 MODAL DIALOG: Large view for the Widget Shop - dialog: { - root: { - borderRadius: "2xl", - p: 2, - boxShadow: "xl", - minW: "240px", - maxW: "60%", - minH: "80%", - overflow: "hidden", - // OVERRIDES: Make the background 85-90% opaque instead of 40% - bg: { - base: "rgba(255, 255, 255, 0.75)", - _dark: "rgba(20, 22, 25, 0.8)" - }, - - // OVERRIDES: Increase the blur just to smooth out any stubborn gridlines - backdropFilter: "blur(15px)", - WebkitBackdropFilter: "blur(15px)", - - - // IMPORTANT: everything below is specific to the widget menu found in the settingsButton component - // dialog header - "& > div.chakra-dialog__header": { - bg: "transparent", - color: { - base: "gray.800", - _dark: "white" - }, - py: 4, - borderColor: { - base: "gray.200", - _dark: "whiteAlpha.200" - }, - - // title - "& h2": { - fontSize: "xl", - fontWeight: "bold" - }, - }, - - // Dialog Body (Category Tabs & Masked Navigators) - "& > div.chakra-dialog__body": { - p: 0, - - // box container of the widget category menu - "& div.chakra-tabs__root > div:first-child": { - position: "relative", - display: "flex", - maxW: "100%", - mx: 5, - mt: 4, - mb: 6 - }, - - // the horizontal widget categories menu - "& div.chakra-tabs__list": { - bg: { - base: "rgba(0, 0, 0, 0.04)", - _dark: "rgba(255, 255, 255, 0.05)" - }, - p: 1, - borderRadius: "full", - display: "flex", - flexWrap: "nowrap", - overflowX: "auto", - maxW: "100%", - border: "1px solid", - borderColor: { - base: "blackAlpha.50", - _dark: "whiteAlpha.100" - }, - scrollbarWidth: "none", - "&::-webkit-scrollbar": {display: "none"}, - - // the pill shaped menu options - "& button.chakra-tabs__trigger": { - px: 5, - py: 2, - borderRadius: "full", - whiteSpace: "nowrap", - flexShrink: 0, - color: { - base: "gray.500", - _dark: "gray.400" - }, - fontWeight: "medium", - transition: "background-color 0.2s, color 0.2s, box-shadow 0.2s", - _hover: { - color: { - base: "gray.800", - _dark: "gray.200" - }, - }, - _selected: { - bg: { - base: "white", - _dark: "rgba(255, 255, 255, 0.12)" - }, - color: { - base: "blue.600", - _dark: "blue.200" - }, - boxShadow: "sm", - fontWeight: "bold", - } - } - }, - - // base styling applied to both chevron navigators - "& div.chakra-tabs__root > div:first-child div:has(svg)": { - position: "absolute", - top: "0", - bottom: "0", - w: "50px", - alignItems: "center", - justifyContent: "center", - zIndex: 2, - cursor: "pointer", - bg: { - base: "rgba(255, 255, 255, 0.7)", - _dark: "rgba(30, 30, 30, 0.8)", - }, - color: { base: "gray.600", _dark: "gray.300" }, - backdropFilter: "blur(1px)", - transition: "background-color 0.2s", - - _hover: { - color: { base: "blue.500", _dark: "blue.300" }, - bg: { - base: "rgba(255, 255, 255, 0.9)", - _dark: "rgba(40, 40, 40, 0.95)", - }, - }, - - "&:has(.lucide-chevron-left)": { - left: 0, - borderTopLeftRadius: "full", - borderBottomLeftRadius: "full", - // Bites a transparent circle out of the flat right side! - WebkitMaskImage: "radial-gradient(circle at 100% 50%, transparent 30px, black 19px)", - maskImage: "radial-gradient(circle at 100% 50%, transparent 30px, black 35px)", - }, - - "&:has(.lucide-chevron-right)": { - right: 0, - borderTopRightRadius: "full", - borderBottomRightRadius: "full", - // THE MAGIC: Bites a transparent circle out of the flat left side! - WebkitMaskImage: "radial-gradient(circle at 0% 50%, transparent 30px, black 19px)", - maskImage: "radial-gradient(circle at 0% 50%, transparent 30px, black 35px)", - }, - }, - }, - }, - }, - }, mode: { editing: { root: { @@ -549,9 +342,108 @@ const glassPanelRecipe = defineSlotRecipe({ }, }, }, +}); - defaultVariants: { - layout: "widget", +// 6. TRUE POPOVER: Compact floating card for 3-dots menus +const glassPopoverRecipe = defineSlotRecipe({ + slots: ["content", "arrow", "positioner"], + base: { + content: { + ...glassSurface, + borderRadius: "xl", + p: 3, + boxShadow: "lg", + bg: {base: "rgba(255, 255, 255, 0.85)", _dark: "rgba(20, 22, 25, 0.85)"}, + backdropFilter: "blur(15px)", + WebkitBackdropFilter: "blur(15px)", + }, + }, +}); + +// 7. MODAL DIALOG: Large view for the Widget Shop +const glassDialogRecipe = defineSlotRecipe({ + slots: ["content", "header", "body", "backdrop"], + base: { + backdrop: { + bg: "rgba(0, 0, 0, 0.4)", + backdropFilter: "blur(4px)", + WebkitBackdropFilter: "blur(4px)", + }, + content: { + display: "flex", + flexDirection: "column", + position: "relative", + ...glassSurface, + borderRadius: "2xl", + p: 2, + boxShadow: "xl", + minW: "240px", + maxW: "60%", + minH: "80%", + overflow: "hidden", + bg: {base: "rgba(255, 255, 255, 0.75)", _dark: "rgba(20, 22, 25, 0.8)"}, + backdropFilter: "blur(15px)", + WebkitBackdropFilter: "blur(15px)", + }, + header: { + bg: "transparent", + color: {base: "gray.800", _dark: "white"}, + py: 4, + borderColor: {base: "gray.200", _dark: "whiteAlpha.200"}, + "& h2": {fontSize: "xl", fontWeight: "bold"}, + }, + body: { + p: 0, + "& div.chakra-tabs__root > div:first-child": { + position: "relative", + display: "flex", + maxW: "100%", + mx: 5, + mt: 4, + mb: 6 + }, + "& div.chakra-tabs__list": { + bg: {base: "rgba(0, 0, 0, 0.04)", _dark: "rgba(255, 255, 255, 0.05)"}, + p: 1, borderRadius: "full", display: "flex", flexWrap: "nowrap", overflowX: "auto", maxW: "100%", + border: "1px solid", borderColor: {base: "blackAlpha.50", _dark: "whiteAlpha.100"}, + scrollbarWidth: "none", "&::-webkit-scrollbar": {display: "none"}, + "& button.chakra-tabs__trigger": { + px: 5, py: 2, borderRadius: "full", whiteSpace: "nowrap", flexShrink: 0, + color: {base: "gray.500", _dark: "gray.400"}, + fontWeight: "medium", transition: "background-color 0.2s, color 0.2s, box-shadow 0.2s", + _hover: {color: {base: "gray.800", _dark: "gray.200"}}, + _selected: { + bg: {base: "white", _dark: "rgba(255, 255, 255, 0.12)"}, + color: {base: "blue.600", _dark: "blue.200"}, + boxShadow: "sm", + fontWeight: "bold" + } + } + }, + "& div.chakra-tabs__root > div:first-child div:has(svg)": { + position: "absolute", top: "0", bottom: "0", w: "50px", alignItems: "center", justifyContent: "center", + zIndex: 2, cursor: "pointer", bg: {base: "rgba(255, 255, 255, 0.7)", _dark: "rgba(30, 30, 30, 0.8)"}, + color: {base: "gray.600", _dark: "gray.300"}, backdropFilter: "blur(1px)", transition: "background-color 0.2s", + _hover: { + color: {base: "blue.500", _dark: "blue.300"}, + bg: {base: "rgba(255, 255, 255, 0.9)", _dark: "rgba(40, 40, 40, 0.95)"} + }, + "&:has(.lucide-chevron-left)": { + left: 0, + borderTopLeftRadius: "full", + borderBottomLeftRadius: "full", + WebkitMaskImage: "radial-gradient(circle at 100% 50%, transparent 30px, black 19px)", + maskImage: "radial-gradient(circle at 100% 50%, transparent 30px, black 35px)" + }, + "&:has(.lucide-chevron-right)": { + right: 0, + borderTopRightRadius: "full", + borderBottomRightRadius: "full", + WebkitMaskImage: "radial-gradient(circle at 0% 50%, transparent 30px, black 19px)", + maskImage: "radial-gradient(circle at 0% 50%, transparent 30px, black 35px)" + }, + }, + }, }, }); @@ -694,10 +586,12 @@ const config = defineConfig({ slotRecipes: { header: headerRecipe, sidebar: sidebarRecipe, - glassPanel: glassPanelRecipe, home: homeRecipe, + widget: widgetRecipe, + glassPopover: glassPopoverRecipe, + glassDialog: glassDialogRecipe, }, }, }); -export const system = createSystem(defaultConfig, config); +export const system = createSystem(defaultConfig, config); \ No newline at end of file