, for
example, that top margin will push the entire group down, as if
MainContent had margin.
(Hover or focus this visualization to see what I mean!)
I recently wrote about the Rules of Margin Collapse. If you're
surprised to learn that margins behave in this way, I think you'll
find it super valuable!
There's a growing movement of developers choosing not to use margin
at all. I haven't yet given up the habit entirely, but I think
avoiding "leaky margin" like this is a great compromise, a great
place to start.
How do we do spacing without margin? There are a few options!
* If it's inside a CSS grid, you can use grid-gap to space out each
element
* If it's inside a Flex container, the brand-new gap property works
wonders (though you may wish to hold off until Safari adds
support)
* You can use a Spacer component, a controversial but surprisingly
pleasant option
* You can use a dedicated layout component like Stack, from the
Braid design system.
Ultimately, the goal is to avoid painting ourselves into a corner. I
believe it's fine to be pragmatic and use margin occasionally, so
long as we're intentional about it, and we understand the trade-offs.
Finally, we need to chat about stacking contexts.
Take a critical look at this code:
jsx
See the problem? Similar to before, we've pre-emptively given our
component a z-index. We better hope that 2 is the right layer in all
future usecases!
There's an even-more-pernicious version of this problem as well. Take
a look at this code:
jsx
The top-level styled-component, Wrapper, doesn't set a z-index...
Surely, this must be fine??
I wish it were so. In fact, this situation can lead to a super
confusing issue.
If our Flourish component has a sibling with an in-between z-index,
it'll get "interleaved" between the bit and its background:
jsx
We can solve for this by explicitly creating a stacking context,
using the isolation property:
jsx
This ensures that any sibling elements will either be above or below
this element. As a bonus, the new stacking context doesn't have a
z-index, so we can rely purely on DOM order, or pass a specific value
when we know what it needs to be.
This stuff is complicated, and way outside the scope of this article.
Stacking contexts are covered in depth in my upcoming CSS course, CSS
for JavaScript Developers.
Link to this heading
Misc tips and tricks
Phew! We've covered the high-level "big ideas" I wanted to share, but
before I wrap up, I have a few smaller tidbits I think are
worthwhile. Let's go through them.
Link to this heading
The 'as' prop
React developers have a reputation for being ignorant of semantic
HTML, using as a catch-all.
[]
A fair criticism of styled-components is that it adds a layer of
indirection between the JSX and the HTML tags being produced. We need
to be aware of that fact, so that we can account for it!
Every styled-component you create accepts an as prop which'll change
which HTML element gets used. This can be really handy for headings,
where the exact heading level will depend on the circumstance:
jsx
It can also come in handy for components that can either render as
buttons or links, depending on the circumstance:
jsx
Semantic HTML is very important, and the as prop is a crucial bit of
knowledge for all developers building with styled-components.
Link to this heading
Increasing specificity
In most CSS methodologies, you'll occasionally run into situations
where a declaration you write has no effect because another style is
overwriting it. This is known as a specificity issue, since the
undesirable style is "more specific" and wins.
For the most part, if you follow the techniques laid out in this
article, I promise that you won't have specificity issues, except
possibly when dealing with third-party CSS. This blog has ~1000
styled-components, and I've never had specificity problems.
I am hesitant to share this trick, because it's an escape hatch for a
situation that should really be avoided... But I also want to be
realistic. We all work in codebases that are not always ideal, and it
never hurts to have an extra tool in your toolbelt.
Here it is:
jsx
In this situation, we have three separate color declarations,
targeting the same paragraph.
At the base level, our Paragraph is given red text using the standard
styled-components syntax. Unfortunately, the Wrapper has used a
descendent selector and has overwritten that red text with blue text.
To solve this problem, we can use a double-ampersand to flip it to
green text.
As we saw earlier, the & character is a placeholder for the generated
class name. Putting it twice repeats that class: Instead of
.paragraph, it'll be .paragraph.paragraph.
By "doubling down" on the class, its specificity increases.
.paragraph.paragraph is more specific than .wrapper p.
This trick can be useful for increasing specificity without reaching
for the nuclear option, !important. But there's a bit of a pandora's
box here: once you start going down the specificity-tricks road,
you're on the path towards mutually-assured destruction.
Link to this heading
The babel plugin
In production, styled-components will generate unique hashes for each
styled-component you create, like .hNN0ug or .gAJJhs. These terse
names are beneficial, since they won't take up much space in our
server-rendered HTML, but they're completely opaque to us as
developers.
Thankfully, a babel plugin exists! In development, it uses semantic
class names, to help us trace an element/style back to its source:
[]
If you use create-react-app, you can benefit from this plugin without
needing to eject by changing all your imports:
jsx
A quick find-and-replace in your project will dramatically improve
your developer experience!
For other types of projects, you can follow the official
documentation.
Earlier, we talked about how using descendant selectors make it
harder to trace styles in your app. But if we can use the babel
plugin to trace styles, doesn't that concern disappear?
Sadly, no. Not at all.
Show more
Link to this heading
Mental models
In this article, we've looked at some styled-components-specific
APIs, but really the ideas I hope to convey are bigger than any
specific tool or library.
When we extend the component mindset to our CSS, we gain all sorts of
new superpowers:
* The ability to know, with confidence, whether it's safe to remove
a CSS declaration (no possibility of it affecting some
totally-separate part of the application!).
* A complete lack of specificity issues, no more trying to find
tricks to bump up specificity.
* A neat and trim mental model that fits in your head and helps you
understand exactly what your pages will look like, without
needing to do a bunch of manual testing.
styled-components is relatively unopinionated, and so there are a lot
of different ways to use it... I have to admit, though, it makes me a
bit sad when I see developers treat it as a fancy class-name
generator, or "Sass 2.0". If you lean in to the idea that
styled-components are components, first and foremost, you'll get so
much more out of the tool.
These are, of course, only my opinions, but I was happy to learn that
they're in-line with recommended practices. I sent an early draft of
this post to Max Stoiber, creator of styled-components, and here's
how he responded:
[]
A lot of this stuff has faded into view for me, becoming crisp only
after a few years of experimentation. I hope that this post saves you
some time and energy.
Also! I'm currently working on a course called CSS for JavaScript
Developers. It's a CSS fundamentals course for folks who have learned
enough CSS to get by, but not enough to feel comfortable. It's
specifically tailored for JS developers, devs who use React / Vue /
Angular / etc. I'm really excited about it, because I know it'll be
so helpful for the people I have in mind. If you enjoyed this post, I
think you'll love what I'm working on
A front-end web development newsletter that sparks joy
My goal with this blog is to create helpful content for front-end web
devs, and my newsletter is no different! It includes early previews
to upcoming posts and access to special bonus goodies. No spam,
unsubscribe at any time.
First Name
[ ]
Email
[ ]
Subscribe
Last Updated:
January 25th, 2021
JoshWComeau
Thanks for reading!
(c) 2020-present Joshua Comeau. All Rights Reserved.
Tutorials
ReactAnimationCSSCareerGatsbyNext.jsPerformance
Links
TwitterRSSContact
(c) 2020-present Joshua Comeau. All Rights Reserved.