Everything's a box
In this post i’m going to introduce one of the concepts you can use when developing web pages/apps using Theme UI
If you’re not familiar with Theme UI take a moment to have a read of the docs… and if you’re not familiar with CSS-in-Js perhaps have a read up on that first.
I’m going to talk about one quite complicated but in my opinion unnecessary part of Theme UI and it’s something that took me a while to work out since the docs don’t really mention this in a clear and concise way.
It’s my hope that this post might clarify a couple of things and help lower the barrier to entry when using Theme UI. I won’t go into detail about how the CSS properties link to the their respective scales or keys… that’s a post for another time.
In the docs you’ll see a code snippet like this.
/** @jsx jsx */
import { jsx } from 'theme-ui';
export default (props) => (
<div
sx={{
fontWeight: 'bold',
fontSize: 4,
color: 'primary',
}}
>
Hello
</div>
);
When i first saw this i was like “What the flip is @jsx”, and “where’s the import for React
” … and then after a
while i read on and started to understand what jsx
pragma actually is.
The TLDR version is as follows.
When you include /** @jsx jsx */
in your React component you don’t need to import React. This because the jsx
pragma
kind of includes the functionality to transform JSX for us. The jsx
pragma also allows a new “type” of HTML attribute,
it’s called the sx
prop which can be applied to any normal HTML element.
With the sx
prop you can now style you UI using Theme UI’s super powers.
If you’d like to know about more, have a read of this: What is JSX Pragma
No more naming of CSS classes, or importing global variables 🤢 and in no way will we need to worry about order of specificity 🙌… nice ay!
But…
Importing jsx
and having to explain the jsx
pragma is all a bit unnecessary so i’m proposing we try this another
way.
Instead of doing what the docs say, try this approach instead.
import React from 'react';
import { Box } from 'theme-ui';
export default (props) => (
<Box
sx={{
fontWeight: 'bold',
fontSize: 4,
color: 'primary',
}}
>
Hello
</Box>
);
You can see from the above that we don’t need to import the jsx
pragma and instead we can import React as we normally
would and then use one of the components that ships with Theme UI… The <Box />
The Box is technically just a div but if you inspect a Theme UI element, there’s a few things Theme UI does for us which will save us time later.
JavaScript for Box 👇
<Box>I'm a Box</Box>
JavaScript for div 👇
<div>I'm a div</div>
CSS for Box 👇
display: block;
box-sizing: border-box;
margin: 0;
min-width: 0;
CSS for div 👇
display: block;
You can see from the above that the CSS for the <Box />
includes some resets for us, eg. box-sizing
,margin
and
min-width
and whilst in the past we could have used a global CSS file that handles the resets, we don’t want to use
global CSS because this is where problems arise.
Global styles and the dreaded !important
are escape hatches. These are work arounds we’ve developed over the years to
compensate for some of the shortcomings of native CSS. But in the new world of CSS-in-Js these native shortcomings have
been removed which allows us to spend more time focussing on actually building something.
But wait, there’s more, here’s the <Flex />
component that ships with Theme UI
JavaScript for Flex 👇
<Flex>I'm a Flex</Flex>
CSS for Flex 👇
display: flex;
box-sizing: border-box;
margin: 0;
min-width: 0;
…and you can see from the CSS snippet that the defaults that come with this component save us the time of adding them
ourselves. e.g <Flex />
already has display: flex;
Cool ay! 😎
One last thing. Theme UI also allows the Polymorphic as
prop. Poly meaning many, and morphic meaning forms so
you can do things like this.
JavaScript for aside 👇
<Box as='aside'>I'm an aside</Box>
CSS for aside 👇
display: block;
box-sizing: border-box;
margin: 0;
min-width: 0;
You’ll see if you inspect the “I’m man aside” element that the actual HTML element is in fact an <aside />
and it
still inherits all the resets from <Box />
The as
props works with all manner of HTML elements… even inputs:
<Box as='input' placeholder="I'm an input" />
… but don’t do as above, use the <Input />
component instead because it maps to the correct key in the theme object:
forms.input
<Input placeholder="I'm an input" />
So now you know.
Everything’s a <Box />
… sort of