1

I am trying to use Joy UI and its dark mode https://mui.com/joy-ui/customization/dark-mode/ in my cljs project and am stuck with translating the part below to cljs code.

import * as React from 'react';
import { CssVarsProvider, extendTheme } from '@mui/joy/styles';
import Sheet from '@mui/joy/Sheet';
import Chip from '@mui/joy/Chip';
import Typography from '@mui/joy/Typography';

const theme = extendTheme({ cssVarPrefix: 'demo' });

export default function DarkModeByDefault() {
  return (
    <CssVarsProvider
      defaultMode="dark"
      // the props below are specific to this demo,
      // you might not need them in your app.
      //
      theme={theme}
      // the selector to apply CSS theme variables stylesheet.
      colorSchemeSelector="#demo_dark-mode-by-default"
      //
      // the local storage key to use
      modeStorageKey="demo_dark-mode-by-default"
      //
      // set as root provider
      disableNestedContext
    >
      <div id="demo_dark-mode-by-default">
        <Sheet sx={{ px: 3, py: 1.5, borderRadius: 'sm' }}>
          <Typography
            component="div"
            endDecorator={
              <Chip variant="outlined" color="primary" size="sm">
                Default
              </Chip>
            }
            fontSize="lg"
          >
            Dark mode
          </Typography>
        </Sheet>
      </div>
    </CssVarsProvider>
  );
}

I am using shadow-cljs, and I tried to translate it like the following

(ns ....
  (:require
    ...
   ["@mui/joy/Button" :as mui-joy-button]
   ["@mui/joy/Grid" :as mui-joy-grid]
   ["@mui/joy/styles" :as mui-joy-styles]))

(def btn (rc/adapt-react-class (.-default mui-joy-button)))
(def grd (rc/adapt-react-class (.-default mui-joy-grid)))
(def cvp (rc/adapt-react-class mui-joy-styles/CssVarsProvider))
(def ext mui-joy-styles/extendTheme)

(def theme (ext {:cssVarPrefix "demo"}))

(defn main-page []
  [cvp {:defaultMode "dark"
        :theme theme
        :colorSchemeSelector "#demo_dark-mode-by-default"
        :modeStorageKey "demo_dark-mode-by-default"
        :disableNestedContext true}]
  [:div {:id "demo_dark-mode-by-default"}
   [grd {:container true :spacing 2}
    [grd {:xs 8} [:p "asdasdfasdf"]]
    [grd {:xs 4} [btn {:variant :solid} "asdf"]]]])

But the theming and dark mode do not work. I believe my problem is CssVarsProvider part. I am not sure how I can import it.

Can someone please provide a clue?

Darren Kim
  • 13
  • 2

2 Answers2

2

The (def theme ...) is calling the (ext ...) JS function with a CLJS Map. It won't support that, it will likely need to be either (def theme (ext #js {:cssVarPrefix "demo"})) or (def theme (clj->js {:cssVarPrefix "demo"})), so that it is converted to a proper JS Object first. Which one you use depends on if you add nested structures. The #js works only for simple objects, which in this case would fit.

You can also shrink the code a bit by using the :> operator and the require sugar, so that you don't need all the extra defs.

(ns ...
  (:require
    ...
    ["@mui/joy/Button$default" :as btn]
    ["@mui/joy/Grid$default" :as grd]
    ["@mui/joy/styles" :as mui-joy-styles :refer (CssVarsProvider extendTheme)]))

(def theme (extendTheme #js {:cssVarPrefix "demo"}))

(defn main-page []
  [:> CssVarsProvider
   {:defaultMode "dark"
    :theme theme
    :colorSchemeSelector "#demo_dark-mode-by-default"
    :modeStorageKey "demo_dark-mode-by-default"
    :disableNestedContext true}
   [:div {:id "demo_dark-mode-by-default"}
    [:> grd {:container true :spacing 2}
     [:> grd {:xs 8}
      [:p "asdasdfasdf"]]
     [:> grd {:xs 4}
      [:> btn {:variant "solid"} "asdf"]]]]])
Thomas Heller
  • 3,842
  • 1
  • 9
  • 9
-1

In Clojure only the last expression in a function is returned. In main-page you have two components (cvp and :div), but only the last (:div) is returned. You want to nest the cvp in the :div.

(defn main-page []
  [cvp {:defaultMode "dark"
        :theme theme
        :colorSchemeSelector "#demo_dark-mode-by-default"
        :modeStorageKey "demo_dark-mode-by-default"
        :disableNestedContext true}
   [:div#demo_dark-mode-by-default
    [grd {:container true :spacing 2}
     [grd {:xs 8} [:p "asdasdfasdf"]]
     [grd {:xs 4} [btn {:variant :solid} "asdf"]]]]])

Note that in Hiccup classes can be appended to the first keyword in the vector with . and ids with #, so I changed [:div {:id "demo_dark-mode-by-default"} to the equivalent :div#demo_dark-mode-by-default.

user2609980
  • 10,264
  • 15
  • 74
  • 143
  • You are right. I updated my code using :<> but there is the real issue lingering. – Darren Kim Mar 16 '23 at 21:35
  • Console says module$node_modules$react_dom$cjs$react_dom_development.js:163 Uncaught Error: No protocol method IKVReduce.-kv-reduce defined for type object: [object Object] at Object.cljs$core$missing_protocol [as missing_protocol] (cljs.core.js:314:9) at cljs$core$IKVReduce$_kv_reduce$dyn_17106 (cljs.core.js:2646:17) ... – Darren Kim Mar 16 '23 at 21:35
  • This is incorrect. You want the div to be a child of cvp. Not a separate thing. – Thomas Heller Mar 17 '23 at 07:09
  • Oh you are right, apologies, I will fix the answer. – user2609980 Mar 17 '23 at 10:16
  • I see @ThomasHeller's answer now, importing the React class using `:>` is even better, I did not check those parts locally. – user2609980 Mar 17 '23 at 10:19