From fcc17d3dcf8233cc13e7dc44a529098b5a814935 Mon Sep 17 00:00:00 2001 From: Emi Matchu Date: Sun, 31 Mar 2024 01:20:45 -0700 Subject: [PATCH] Whoops, fix some style regressions for the React app! Ah beans, I didn't notice when doing my Turbo fixes in 40804c15435b954e3f320c9e84db8690b1d74dad, that I had accidentally prevented Chakra from applying some of its usual global styles! This caused some minor visual regressions in various parts of the app, e.g., the default border color for the search field in the wardrobe app became way darker. Here, instead of copy-pasting the styles and missing some parts, we scope the global styles a bit more carefully: we first use `extendTheme` with the same code as Impress 2020 to get a good `globalStyles` function that includes Chakra's defaults, *then* delete the key from the theme. Then, in `ScopedCSSReset`, we use code similar to Chakra's own global style application code: call the `globalStyles` function with the current theme and color mode, use Chakra's `css` function to convert values like `green.800` to CSS values, prepend our scoping rule onto the selectors, and drop it into our Emotion CSS. Tbh I was worried because when I first noticed this issue while on my trip, I saw the black item search box border, and was like "ah dang, did I destroy all the color in the app by breaking the part where Chakra defines its CSS color variables??" And no, that's not actually what happened, a lot of the app still had its colors! So this was less pressing than I had thought, but still good to get fixed! --- app/javascript/wardrobe-2020/AppProvider.js | 85 +++++++++++++++------ 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/app/javascript/wardrobe-2020/AppProvider.js b/app/javascript/wardrobe-2020/AppProvider.js index bca86234..23bf08f5 100644 --- a/app/javascript/wardrobe-2020/AppProvider.js +++ b/app/javascript/wardrobe-2020/AppProvider.js @@ -4,12 +4,15 @@ import { Integrations } from "@sentry/tracing"; import { ChakraProvider, Box, - theme as defaultTheme, - useColorModeValue, + css as resolveCSS, + extendTheme, + useColorMode, + useTheme, } from "@chakra-ui/react"; +import { mode } from "@chakra-ui/theme-tools"; import { ApolloProvider } from "@apollo/client"; import { BrowserRouter } from "react-router-dom"; -import { Global } from "@emotion/react"; +import { css, Global } from "@emotion/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import produce from "immer"; @@ -17,13 +20,29 @@ import apolloClient from "./apolloClient"; const reactQueryClient = new QueryClient(); -// Use Chakra's default theme, but with the global styles removed. (We've -// copied them into our `ScopedCSSReset` component, to apply in only the places -// we actually want them!) -const theme = produce(defaultTheme, (theme) => { - theme.styles.global = {}; +let theme = extendTheme({ + styles: { + global: (props) => ({ + body: { + background: mode("gray.50", "gray.800")(props), + color: mode("green.800", "green.50")(props), + transition: "all 0.25s", + }, + }), + }, }); +// Capture the global styles function from our theme, but remove it from the +// theme's `styles.global` key. We don't actually want to apply these styles +// globally, because they conflict with the app's other styles when embedded in +// e.g. the item page! We'll apply them more carefully in `ScopedCSSReset`. +// +// NOTE: We get this from `theme`, instead of just declaring the function +// separately, because `extendTheme` returns a wrapped version of our function +// that also returns some of Chakra's own default global styles. +const globalStyles = theme.styles.global; +theme.styles.global = {}; + export default function AppProvider({ children }) { React.useEffect(() => setupLogging(), []); @@ -94,26 +113,46 @@ function setupLogging() { * the selector `:where(.chakra-css-reset) h1` is lower specificity. */ function ScopedCSSReset({ children }) { - const baseTextColor = useColorModeValue("green.800", "green.50"); + // Get the current theme and color mode. + // + // NOTE: The theme object returned by `useTheme` has some extensions that are + // necessary for the code below, but aren't present in the theme config + // returned by `extendTheme`! That's why we use this here instead of `theme`. + const liveTheme = useTheme(); + const colorMode = useColorMode(); + + // Resolve the theme's global styles into CSS objects for Emotion. + const globalStylesCSS = resolveCSS( + globalStyles({ theme: liveTheme, colorMode }), + )(liveTheme); + + // Prepend our special scope selector to the global styles. + const scopedGlobalStylesCSS = {}; + for (let [selector, rules] of Object.entries(globalStylesCSS)) { + // The `body` selector is where typography etc rules go, but `body` isn't + // actually *inside* our scoped element! Instead, ignore the `body` part, + // and just apply it to the scoping element itself. + if (selector.trim() === "body") { + selector = ""; + } + + const scopedSelector = + ":where(.chakra-css-reset, .chakra-portal) " + selector; + scopedGlobalStylesCSS[scopedSelector] = rules; + } return ( <> - - {children} - + {children}