En ocasiones, al desarrollar aplicaciones web es necesario realizar un diseño que acepte el cambio de Tema para adaptarlo a las necesidades del cliente de forma dinámica y personalizando su experiencia. Si estamos usando React.JS junto con JSS para aplicar los estilos, podemos hacerlo de forma manual añadiendo el código necesario o, usar la característica de Theming de JSS.
¿Qué es JSS?
JSS es una herramienta que permite combinar JavaScript con CSS para poder escribir los estilos de forma declarativa, reutilizable, flexible, etc. Además es independiente del framework que usemos, aunque, en nuestro caso, lo aplicaremos a React.
Gracias a esta herramienta podremos usar estilos dinámicos, dependientes del valor de determinadas variables de nuestro código JavaScript y, este fundamento es la base sobre la que podremos aplicar temas de una forma sencilla.
ThemeProvider
Vamos a entrar un poco en faena y vamos a empezar por ThemeProvider. Ya que estamos haciendo uso de React, es fácil pensar que una solución adecuada es usar un componente, y como elementos hijos, aquellos que queramos que varíen su comportamiento dependiendo del tema seleccionado.
ThemeProvider es el componente que envolverá aquellos a los que deseamos cambiar su comportamiento según el tema seleccionado, de forma que se propagará por todos ellos para poder ser utilizado según convenga.
{ <ThemeProvider theme={currentTheme}> Children... </ThemeProvider>
El tema lo definiremos como un objeto que contiene los estilos que vamos a utilizar, organizado o agrupado a nuestra conveniencia.
{ const lightTheme = { value: "light", colors: { background: "#ffffff", foreground: "#0c0c0c", border: 'cyan' } } const darkTheme = { value: "dark", colors: { background: "#000000", foreground: "#c0c0c0", border: 'cyan' } }
Obtener el tema usado
Para poder obtener el tema desde un componente, tenemos dos opciones diferentes «withTheme» y «useTheme».
Es importante tener en cuenta que los componente sólo podrán acceder al tema si alguno de los componentes de orden superior es un ThemeProvider
withTheme
El uso de withTheme se realiza dentro del propio componente a la hora de exportarlo y esto hará que el tema se añada como propiedad necesaria al invocarlo desde un componente superior.
Nuestro componente:
{ const ThemedBlockStyles = createUseStyles((theme: any) => ({ themedBlock: { padding: "20px", margin: "20px", textAlign: "center", border: '1px solid ' + theme.colors.border, borderRadius: '20px', background: theme.colors.background, color: theme.colors.foreground } })); const ThemedBlockWithTheme = (props: any) => { const styles = ThemedBlockStyles(props.theme); return ( <> <div className={styles.themedBlock}> Hello themed world!! </div> </> ) }; export default withTheme(ThemedBlockWithTheme);
El componente de orden superior
1
|
<ThemedBlockWithTheme theme={currentTheme} /> |
Como veis, al invocar el componente, es necesario pasarle como parámetro/atributo el tema y, posteriormente, en el propio componente, se podrá hacer uso de ese tema desde las propiedades mediante «props.theme» aprovechando para pasárselo a los estilos creados con JSS.
useTheme
useTheme tiene un uso un poco más «clásico» y permite, desde cualquier componente, acceder al tema sin necesidad de que se le tenga que pasar como atributo a la hora de invocarlo.
{ const ThemedBlockStyles = createUseStyles((theme: any) => ({ themedBlock: { padding: "20px", margin: "20px", textAlign: "center", border: '1px solid ' + theme.colors.border, borderRadius: '20px', background: theme.colors.background, color: theme.colors.foreground } })); const ThemedBlockUseTheme = (props: any) => { const theme = useTheme(); const styles = ThemedBlockStyles({...props, theme}); return ( <> <div className={styles.themedBlock}> Hello themed world!! </div> </> ) }; export default ThemedBlockUseTheme;
Y el componente de orden superior ya no tendría que pasarle como atributo el tema a la hora de instanciarlo.
1
|
<ThemedBlockUseTheme /> |
Ejemplo
Si queréis ver el ejemplo, podéis ver el código en mi repo de GitHub y usarlo a vuestro gusto.
Mejoras del ejemplo
Tema del sistema
Para mejorar el ejemplo, podéis mirar cómo obtener el tema del sistema usando window.matchMedia(«(prefers-color-scheme: dark)»); y así mostrar vuestros componentes con el tema adecuado a la selección del usuario en su sistema. Podéis indagar en este artículo.
Extender la interfaz DefaultTheme para definir el tema y tener Intellisense
Conclusiones
Como podéis comprobar, en el código de ejemplo he indicado «any» como tipo del parámetro theme dentro de los estilos JSS, de forma que pueda recibir cualquier objeto con la agrupación deseada.
Lo malo de esto es que no podemos garantizar que lo que escribamos exista, así que, una mejor forma, es extender la interfaz DefaultTheme agregando las propiedades que queramos tener dentro de nuestro tema y así no sólo disponer de intellisense sino que podemos garantizar que las propiedades que vayamos a intentar usar existan en nuestro tema. Enjoy coding!