DEV Community

loading...
Cover image for Why You Should Try Theme UI

Why You Should Try Theme UI

luisrondow profile image Luís Eduardo ・3 min read

Today, we have a bunch of technologies to create interactive UI in React. But, the library that I work recently open my eyes to a easy way of UI engineering and Design management, Theme UI.

In my case, I mostly used it with Next.js and Typescript, but it's possible to use with CRA and Gatsby.

Theme UI provide us components, theming and the sx prop.

Theming

Theme UI works with ThemeProvider. It's possible to use a different provider anywhere we want, but in the mostly case there's a global ThemeProvider that wrap the entire App.

The ThemeProvider has a required prop, theme. That prop expect an object that you can include custom colors, typography, layout values and custom style variants, (e.g button.primary, input.error).

// example theme.ts
export default {
  fonts: {
    body: "system-ui, sans-serif",
    heading: '"Avenir Next", sans-serif',
    monospace: "Menlo, monospace",
  },
  colors: {
    text: "#000",
    background: "#fff",
    primary: "#33e",
  },
  styles: {
    colors: {
      background: "#fff",
      primary: "#e1c539",
      black: "#262626",
      lightGray: "#e8e8e8",
      mediumGray: "#c2c4c4",
      darkGray: "#50515F",
    },
    button: {
      primary: {
        backgroundColor: "primary",
      },
      secondary: {
        backgroundColor: "mediumGray",
      },
    },
  },
};

Enter fullscreen mode Exit fullscreen mode

A great way to architect that is encapsulate the ThemeProvider in a provider file.

import React from "react";
import { ThemeProvider } from "theme-ui";
import theme from "./theme";

type ProviderProps = {
  children?: React.ReactNode;
};

const Provider = ({ children }: ProviderProps) => {
  return (
    <ThemeProvider theme={theme}>
      {children}
    </ThemeProvider>
  );
}

export default Provider;
Enter fullscreen mode Exit fullscreen mode

Now, just wrap your app in that provider. In Next.js, it's necessary to wrap the Component prop of _app file.

import React from 'react';
import ThemeProvider from '../styles/provider';
import { AppPropsType } from 'next/dist/next-server/lib/utils';

const App = ({ Component, pageProps }: AppPropsType) => {
  return (
    <ThemeProvider>
      <Component {...pageProps} />
    </ThemeProvider>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

For complete how-to-implement the theme object, take a look at Theming Doc.

Components

Theme UI provide a lot of built-in UI components. That is, an abstraction of components necessary for the construction of a UI. Forgot about HTML tags, the Theme UI purpose is make you think that your page is a canvas.

import React from "react";
import { Box, Flex, Text, Button } from "theme-ui";

const Example = () => {
  return (
    <Box p={4} bg="highlight">
      <Flex
        sx={{
          alignItems: "center",
        }}
      >
        <Text as="h2">Components</Text>
        <Button ml="auto">Beep</Button>
      </Flex>
    </Box>
  );
};

Enter fullscreen mode Exit fullscreen mode

The components accepts layout props (e.g padding or p, margin or m, color, etc) and the default props: as (used for set the underlying HTML tag), variant (set the a predefined style) and sx.

The sx Prop

That's the Golden eggs chicken. The sx Prop let you style inline any JSX element you want.

Unlike React default inline style prop, with the sx it is possible to use values from your theme, reponsive values (width: ['100%', '50%', '25%']), media queries and CSS pseudo-class (e.g :hover, :active).

import React from "react";
import { Box, Text } from "theme-ui";

const Example = () => {
  return (
    <Box
      sx={{
        padding: 3,
        bg: "primary",
        "&:hover": {
          bg: "highlight",
          cursor: "pointer",
        },
      }}
    >
      <Text
        as="h1"
        sx={{
          color: "black",
        }}
      >
        Hello
      </Text>
    </Box>
  );
};
Enter fullscreen mode Exit fullscreen mode

Dark mode

Dark mode it's the most expected feature from every UI that is used, in Theme UI was not different.

To apply the dark mode it is quite simple and fast, just add Dark Mode colors on mode objects, in the style:

// style.ts
{
  colors: {
    text: '#000',
    background: '#fff',
    primary: '#07c',
    modes: {
      dark: {
        text: '#fff',
        background: '#000',
        primary: '#0cf',
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

...and set with Color Modes hook provided.

import React from "react";
import { Box, Text, Button, useColorMode } from "theme-ui";

export default (props) => {
  const [colorMode, setColorMode] = useColorMode();

  return (
    <Box as="header">
      <Button
        onClick={() => {
          setColorMode(colorMode === "default" ? "dark" : "default");
        }}
      >
        Dark Mode {colorMode === "default" ? "Dark" : "Light"}
      </Button>
    </Box>
  );
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

Theme UI is a great alternative for your CSS-in-JS. I like it because I can do everything I want without work with any native CSS or other UI lib that is too much opinative.

With the sx prop, it's no more necessary to use Styled Components and that makes the code cleaner and more understandable. Also, the components purpose makes build the UI quick and easy.

Because all that, I really think you should try Theme UI.

Thanks for reading!

cya :)

References

Theme UI Documentation

Discussion (3)

pic
Editor guide
Collapse
omawhite profile image
Omar White

All the times I’ve used theme-UI I’ve really liked it. It’s a solid library and feels easy to make something look different, not the case using something like material UI in my experience. I really wish there was a react native implementation of theme-ui that would be sweet.

Collapse
bycedric profile image
Cedric van Putten

It's sort of there, I tried github.com/nandorojo/dripsy. It's a bit rough in some spots, but works on all React Native projects 😬

Collapse
omawhite profile image
Omar White

It looks pretty solid, what were the rough spots you ran into? Anything that makes it not worth trying out?