In this tutorial you'll learn how to create a custom theme editor and a simple Color Panel Editor like in the image below.
In this tutorial you'll use:
You need HyperTheme Editor installed and working on your Chakra UI Project.
If you still not have installed HyperTheme, follow the installation instructions here.
In the Installation page we have added the HyperThemeEditor
component that gives us a plug&play editor on the page through a Button
.
The source code of HyperThemeEditor
itself it's very simple as you can see here:
import React, { FC } from 'react'import {ThemeEditor,ThemeEditorButton,ThemeEditorButtonProps,ThemeEditorDrawer,} from '@hypertheme-editor/chakra-ui-core'import { ThemeEditorColors } from '@hypertheme-editor/chakra-ui-colors'import { ThemeEditorFontSizes } from '@hypertheme-editor/chakra-ui-font-sizes'import { CgColorPicker } from 'react-icons/cg'import { ImFontSize } from 'react-icons/im'export type DefaultThemeEditorProps = ThemeEditorButtonPropsexport const HyperThemeEditor: FC<DefaultThemeEditorProps> = (props) => {return (<ThemeEditor><ThemeEditorButton {...props} /><ThemeEditorDrawer><ThemeEditorColors icon={CgColorPicker} title="Colors" /><ThemeEditorFontSizes icon={ImFontSize} title="Font Sizes" /></ThemeEditorDrawer></ThemeEditor>)}
Read more about HyperThemeEditor
component here.
Based on the source code of HyperThemeEditor
we can create a custom ThemeEditor.
To do so, create a new MyThemeEditor
component with this content:
@hypertheme-editor/chakra-ui-core
leads to a known issue with recoil
.@hypertheme-editor/chakra-ui
or @hypertheme-editor-pro/chakra-ui
.import React, { FC } from 'react'import {ThemeEditor,ThemeEditorButton,ThemeEditorButtonProps,ThemeEditorColors,ThemeEditorFontSizes,} from '@hypertheme-editor/chakra-ui'import { CgColorPicker } from 'react-icons/cg'import { BiText } from 'react-icons/bi'export const MyThemeEditor = (props) => {return (<ThemeEditor><ThemeEditorButton {...props} /><ThemeEditorDrawer><ThemeEditorColors icon={CgColorPicker} title="Colors" /><ThemeEditorFontSizes icon={BiText} title="Typography" /></ThemeEditorDrawer></ThemeEditor>)}
As you can see, you can change the order of the panels, the icons and the labels.
HyperTheme uses react-icons
for rendering the icons on the editor, here you can search for the icon you need.
Read more about: ThemeEditor
, ThemeEditorDrawer
and ThemeEditorButton
.
Now that we have a custom ThemeEditor component we can start creating our custom editor panel.
Create a new MyCustomPanel
component:
import React from 'react'import { Box } from '@chakra-ui/react'export default function MyCustomPanel(props) {return <Box>Testing Custom Panel</Box>}
Add it as a new Panel to our MyThemeEditor
component:
import React, { FC } from 'react'import {ThemeEditor,ThemeEditorButton,ThemeEditorButtonProps,ThemeEditorColors,ThemeEditorFontSizes,} from '@hypertheme-editor/chakra-ui'import { CgColorPicker } from 'react-icons/cg'import { BiText } from 'react-icons/bi'import MyCustomPanel from './MyCustomPanel'export default function MyThemeEditor(props) {return (<ThemeEditor><ThemeEditorButton {...props} /><ThemeEditorDrawer>{/* Add the MyCustomPanel to our theme editor */}<MyCustomPanel icon={CgColorPicker} title="My Panel" /><ThemeEditorColors icon={CgColorPicker} title="Colors" /><ThemeEditorFontSizes icon={BiText} title="Typography" /></ThemeEditorDrawer></ThemeEditor>)}
Now you have a custom panel inside the ThemeEditorDrawer
component, your theme editor should look like the example below:
Now that we have a theme editor with a custom editor panel, it's time to start retrieving and live edit the theme.
HyperTheme Editor provides the useThemeEditor
hook to:
Check out more on the documentation.
In this section we're creating a ThemeColorBox
that shows a color for the theme.
Create a new ThemeColorBox
component with this content:
import React from 'react'import { Box } from '@chakra-ui/react'import { useThemeEditor } from '@hypertheme-editor/chakra-ui'export default function ThemeColorBox({ token, paletteIndex = 500, ...props }) {const { theme } = useThemeEditor()const color = useMemo(() => {// in Chakra UI, colors could be objects with an index (like: 100, 200, 300, etc) or a single colorif (theme && theme.colors[token]) {// if the color is a string, return itif (typeof theme.colors[token] === 'string') {return theme.colors[token]// if it's an object return the current paletteIndex for that color} else if (theme.colors[token][paletteIndex]) {return theme.colors[token][paletteIndex]}}return 'gray'}, [theme, token, paletteIndex])return <Box w="40px" h="40px" borderRadius="md" bgColor={color} {...props} />}
Let's check our new component with some different theme colors, try to change the colors from the editor to check that is working correctly:
It's time to live edit the current theme.
Let's create a SimpleColorEditor
component that shows the current color and set a new one through an Input
:
import React from 'react'import { useThemeEditor } from '@hypertheme-editor/chakra-ui'import { Input } from '@chakra-ui/react'import { useDebouncyEffect } from 'use-debouncy'import { colord } from 'colord'export default function SimpleColorEditor({ token, paletteIndex, ...props }) {const { theme, setTheme } = useThemeEditor()const [inputValue, setInputValue] = useState<string>(theme.colors[token][paletteIndex] || '')const handleOnChange = useCallback((event) => {setInputValue(event.target.value)}, [])// use a debounced effect so the UI is not blocked// while the value are changed through the InputuseDebouncyEffect(() => {// check that the input color is validif (colord(inputValue).isValid()) {// the color token could be a simple color or a palette object,// so we have to check it.// We also check that the input value differ from// the one from the theme, this is necessary// for undo/redo functionality to work correctlyif (typeof theme.colors[token] === 'string' && theme.colors[token] !== inputValue) {// live edit the current themesetTheme({...theme,colors: {...theme.colors,[token]: inputValue,},})} else if (theme.colors[token][paletteIndex] &&theme.colors[token][paletteIndex] !== inputValue) {// live edit the current themesetTheme({...theme,colors: {...theme.colors,[token]: {...theme.colors.token,[paletteIndex]: inputValue,},},})}}},500,[inputValue])// update internal state if another panel change this valueuseEffect(() => {if (theme.colors[token] &&theme.colors[token][paletteIndex] &&theme.colors[token][paletteIndex] !== inputValue) {setInputValue(theme.colors[token][paletteIndex])}// eslint-disable-next-line react-hooks/exhaustive-deps}, [theme.colors, token, paletteIndex])return (<HStack>{/* add the ThemeColorBox we've created before */}<ThemeColorBox token={token} paletteIndex={paletteIndex} /><Input onChange={handleOnChange} value={inputValue} /></HStack>)}
Let's try our new SimpleColorEditor
component.
If you change a value (using HEX notation) you should see the new color in current theme.
Compare the edited colors with the value provided by HyperThemeEditor
.
Finally, we can mount our SimpleColorEditor
into our custom theme editor.
Edit the MyPanel
component like this:
import React from 'react'import { Box } from '@chakra-ui/react'export default function MyCustomPanel(props) {return (<VStack><SimpleColorEditor token="blue" paletteIndex={500} /><SimpleColorEditor token="red" paletteIndex={500} /><SimpleColorEditor token="green" paletteIndex={500} /><SimpleColorEditor token="orange" paletteIndex={500} /><SimpleColorEditor token="yellow" paletteIndex={500} /><SimpleColorEditor token="purple" paletteIndex={500} /></VStack>)}
The final result should be like this:
Congratulations! You have created your first working custom panel.
Prev
Next