I do agree generally, but it's funny that the suggested solution (spacers) are basically back to the old 1px transparent gif trick from the 90s/early 00s.
Nothing wrong with the suggestion, but it's just ironic that we seem to be coming full circle again.
Next we'll probably have posts extolling <Table> components for layouts ;)
> I do agree generally, but it's funny that the suggested solution (spacers) are basically back to the old 1px transparent gif trick from the 90s/early 00s.
They aren't the same thing. Check the link in the article [1]. It is using regular CSS, and some of those components use more modern things like Grids.
They also aren't applied between components like old element-spacers. Component Spacers have much more in common with regular margin/padding than with 90s spacers.
There are no "spacer gifs" anywhere, it's only that you're lifting the margin responsibility to the parent. I may be wrong, but I think the whole stack concept is borrowed from SwiftUI.
That said, I've recently started using actual spacer divs that are more similar to the spacer gifs of yore, you mentioned. So I could do things like:
Having been alive during the whole semantic web era, that feels very wrong to me. But it works like a charm to keep your code consistent with a design system which seems like a more noble endeavor than to write html that makes more sense to machines (which was, if I remember correctly, the objective of the semantic web).
Tendency towards semantic web is unfortunately dead. Now you have simple blogs rendered by shadow DOM components. Even with templates separation of template and code was ideal - now you have 'reusable components' with templates all over the place. Functional programming has its place, but I'm not sure if everywhere. (You can separate code from templates even with FP and React - but often it is not the case).
I don't think you know what Shadow DOM means. Shadow DOM is not just another virtual DOM. It is a very specific thing built into browsers used only for HTML Custom Elements.
It is not very popular. React, Vue, etc., do not use the Custom Element spec.
Concurrent with spacer.gif, there was also a `<spacer type="block">`[1] tag that was in use at the time.
Working at a boutique design shop back then, I remember trying endless combinations of the spacer tag along with spacer gifs to try to ship layouts that were at least semi (and hopefully mostly) consistent cross-browser.
Having lived through those web dev dark ages, I'm pretty baffled by the resurgence of 'markup as styling'.
Serious question: What is inherently wrong with using Tables for organizing simple layouts? Is it frowned upon just because it is a "hacky" way of doing stuff?
That's very reductionist approach. Sure, margin is a sharp tool and you need to use it responsibly.
I avoid putting margins on "first"-level selector.
So instead of
.button {
margin-left: 16px
}
I do
.parent > .button {
margin-left: 16px
}
Difference is that I can reuse '.button' elsewhere without modifications.
Another point to consider is that margin is not the only CSS property that affects layout. Both grid, flexbox and 'position' properties should be used with same care. And approach I highlighted above is usually good enough.
For margin specifically there is also a "owl" selector that makes is a bit easier to manage
The term was coined by Heydon Pickering [0]. He's great, also responsible for the useful site Inclusive Components [1] and one of the authors of Every Layout [2].
Note that the owl selector is pretty slow when it comes to being processed by browsers. If you use it sparingly, it shouldn't be so bad, but leave it all over the place, and you'll see the impact
You have the same understanding as the OP. The difference is that the OP is working on a component-based framework. In such a framework, it makes sense to declare the rule defining child margins *in* the parent component's code, not in the child's code.
This is one reason I'm not really into utility frameworks. They are able to deal with "appearance" part of the CSS just fine, but layout part is too rigid and inflexible.
You can use plain old CSS for layout purposes and mix it with utility classes for appearance.
You can do:
<div class="actions">
<button class="h-10 px-6 font-semibold rounded-md bg-black text-white" type="submit">
Buy now
</button>
<button class="h-10 px-6 font-semibold rounded-md border border-gray-200 text-gray-900" type="button">
Add to bag
</button>
</div>
And then:
.actions {
display: flex;
justify-content: flex-end;
align-items: center;
}
.actions > * + * {
margin-left: 16px; // or better use var from tailwind to set sizes consistently.
@apply: ml-5; // or do this https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply
}
This should combine those techniques, but I haven't used it in practice. And I'm sure in this case you will be labeled as heretic by both people who don't use utility frameworks and those who do ;)
Anyway, considered harmful articles considered harmful.
There is a time and place for margins. Being thoughtful with them is a better approach than "banning margin from all components."
Should I really ban the use of `margin-left: auto` when positioning my flexed elements? I don't think this article is actually advocating for that, but a novice reader may.
Being thoughtful is a better approach than "considered harmful" clickbait in any situation.
Regarding CSS margins, they are part of the box model even if certain stylesheets neglect them, and the only conflicts they can be involved in are easily solved (a rule for "container x" is more specific than a rule for "x" and can override margins, particularly with parent padding, in special cases) and they highlight genuine design defects: trying to use a component both with and without its "natural" margins (without adding appropriate classes or wrapping containers), trying to specify conflicting margins, trying to fit variable-sized content inside fixed-size containers by messing with the content's required margins, and so on.
There might be things missing to achieve whatever result you desire, but CSS does allow a parent element to define how spacing (and any other styling) works for child elements.
Out of curiosity, why do you need to use "margin-left: auto" when working with flexbox items? What is it that you're trying to do, which can't be solved with "justify-content" and "align-items"?
Not OP but I had a scenario where I had a row with a variable number of items, left-aligned, and then the final item had to be right-aligned. "margin-left: auto" on the final item achieves that effect.
Clever, but this smells like a performance anti-pattern. Adopting this means you could, at worst, add 4 spacer divs per component. While most sites may never really feel a sting, for complex apps where reusability is a larger concern you've doubled to quadrupled the size of an already large DOM and you will be hit a death-by-one-thousand-cuts situation.
Now you've got more...
- html over the wire
- html to parse for first render
- DOM nodes to mount/unmount
- memory usage from excess DOM
- costly layouts due to extra nodes
- lighthouse complaints of an excessively large DOM
Otherwise a clean approach. Perhaps it could be solved at compile time or some other jsx -> CSS abstraction to maintain DX.
I saw a blog post a while back from a React developer who did this. They didn't use padding or margins, just `<div>` elements with an exact height and width. I fail to see any benefits of doing it that way...just learn and use CSS.
I'm not sure if that's what this post is suggesting, though.
Why do you need to add spacer divs? As long as the child components accept classes as props then the styling comes from the parent but is still applied on the children.
You can also apply margin via element and nth-child selectors, just as a note... Also - custom/shadow dom elements should allow classes - I think it doesn't work in angular yet - but should arrive in the next version(?). Also you could just use ::before ::after pseudo selectors instead of divs...
You can, however the lack of constraints on the spacing can make it difficult to match strict design systems. The dependence the spacing creates on the parent & child dimensions can lead to undesirable edge cases as well for dynamic/responsive content.
I think "spacer components" is a bit confusing and not really the message someone should take away from this. I think it's better to say something like "Make parent components responsible for managing spacing around their children", not as catchy I admit.
I think the main bugbear of margins that the article doesn't mention is margin collapse: I personally don't find the rules super intuitive and I would rather just be explicit with spacing than have the browser collapse things together!
Agree, collapsing margins are counterintuitive, and only applied to top and bottom margin. It’s really surprising how many people don’t know it’s a thing and fight the box model. If you think you’re constantly fighting layout, try to read the MDN docs [1] with a fresh eye. I was guilty to think I needn’t learn CSS, and reading the MDN really help me.
What's really surprising is how many people do know that margin collapse is a thing, but haven't fully understood all of the implications, so they still get mystified by some unintended consequence.
I don't think that "go read all the MDN docs" is a very useful suggestion. More useful is something like the following, which I don't think you will find anywhere in MDN:
When an element's top or bottom margin collapses with its parent,
this may affect the position of the parent, because the minimum of the child and parent's margin will be used to position the parent. (MDN says the child element's margin will fall "outside the parent", but it is not clear that it will be used during layout to place the parent.)
The bigger problem is that CSS is just so complicated and arbitrary. Consider the rule for collapsing margins with parents, from MDN:
> If there is no border, padding, inline part, block formatting context created, or clearance to separate the margin-top of a block from the margin-top of one or more of its descendant blocks; or no border, padding, inline content, height, or min-height to separate the margin-bottom of a block from the margin-bottom of one or more of its descendant blocks, then those margins collapse.
They list five different factors that prevent collapse, one of which ("block formatting context created") is a link to a page that defines it in terms of 15 different constructs or situations in CSS.
On top of that, it appears that the MDN text is in error, because it lists intervening "height" as something that prevents bottom margin collapse, but not top margin collapse (when in fact it does).
This is according to me the real issue. Besides this unintuitive collapsability margins, there is only one difference between margin and padding: the edges/border of the background color.
It's the unintuitive collapsability that messes up margins.
I almost feel it should be written out of the spec, because a lot of people still don't understand it, designers and developers. And design tools like Sketch or Figma will never implement it in their tools.
Margin is generally more desirable than padding imo. Component/element knows the content it contains and uses margins to require a minimum amount of spacing. It avoids the need to make decisions on the spacing between every combination of components.
Padding only works if you know everywhere your component might be used or don't mind remaking spacing decisions every time you use it. In my experience minimizing code needed for each use of a component leads to more coherently styled interfaces, particularly on larger or faster moving teams.
The author is arguing exactly that: remake spacing decisions every time you use a component, because that’s what many designers (most?) do. This is also true in my experience.
There are several types of designers, and even on the web itself, several different things you might be designing. Designing a text medium (eg. a blog) should have more focus on semantic tagging compared to a SPA where you probably want an entirely different approach (and where talk of "components" makes more sense).
I like TeX's box model over CSS which allows for easier "smart" spacing (look up hglue, vglue, hskip, vskip, and eg. care for orphans when typesetting etc.) and smarter column alignment (trivial alignment on decimal point in tables).
While I'd never call myself a designer, I've done plenty of TeX (print and PDF) publications that people have commended for usability and appearance.
I want CSS to make spacing decisions so that I don't have to, and a system where graphical elements look good by default because they come with appropriate margins and containers adapt to what I want them to contain (e.g. by shrinkwrapping the content or by switching between different numbers of columns or rows) is more useful than a non-system that looks bad unless I keep many tedious ad-hoc specifications consistent.
Moreover,
> Margin breaks component encapsulation. A well-built component should not affect anything outside itself.
Definitely not, margins are an essential part of a component, like a garden around a house, and they make components "usable in any context or layout" (unless the context or layout is stupidly overcomplicated).
The point about not having it in a component is valid. It happens all the time that a margin on a component is different based on context. But there is no need to make it any more complicated than a margin -- just add the margin externally and if your framework can't do that, add a wrapper and put the margin there.
Nothing wrong with the suggestion, but it's just ironic that we seem to be coming full circle again.
Next we'll probably have posts extolling <Table> components for layouts ;)
They aren't the same thing. Check the link in the article [1]. It is using regular CSS, and some of those components use more modern things like Grids.
They also aren't applied between components like old element-spacers. Component Spacers have much more in common with regular margin/padding than with 90s spacers.
[1] https://seek-oss.github.io/braid-design-system/components/St...
Not really. The Stack component he uses as an example is actually just a thin abstraction over some reusable css. In plain html, it could look like
There are no "spacer gifs" anywhere, it's only that you're lifting the margin responsibility to the parent. I may be wrong, but I think the whole stack concept is borrowed from SwiftUI.That said, I've recently started using actual spacer divs that are more similar to the spacer gifs of yore, you mentioned. So I could do things like:
Having been alive during the whole semantic web era, that feels very wrong to me. But it works like a charm to keep your code consistent with a design system which seems like a more noble endeavor than to write html that makes more sense to machines (which was, if I remember correctly, the objective of the semantic web).Not dead, but under severe attack. We can and will fightback
It is not very popular. React, Vue, etc., do not use the Custom Element spec.
Working at a boutique design shop back then, I remember trying endless combinations of the spacer tag along with spacer gifs to try to ship layouts that were at least semi (and hopefully mostly) consistent cross-browser.
Having lived through those web dev dark ages, I'm pretty baffled by the resurgence of 'markup as styling'.
[1] https://www.tutorialspoint.com/html/html_spacer_tag.htm
We'll revisit old ideas from time to time after going the opposite extreme, but never in the same way.
I have to agree with others. It is not the same as 1px spacer hacks at all.
Dead Comment
I avoid putting margins on "first"-level selector.
So instead of
I do Difference is that I can reuse '.button' elsewhere without modifications.Another point to consider is that margin is not the only CSS property that affects layout. Both grid, flexbox and 'position' properties should be used with same care. And approach I highlighted above is usually good enough.
For margin specifically there is also a "owl" selector that makes is a bit easier to manage
[0] https://alistapart.com/article/axiomatic-css-and-lobotomized...
[1] https://inclusive-components.design
[2] https://every-layout.dev
Child directly styles itself with color, font, internal layout, etc
Parent positions child with flex box, grid, margin or absolute positioning. Spacer elements are not needed at all.
You can use plain old CSS for layout purposes and mix it with utility classes for appearance.
You can do:
And then: This should combine those techniques, but I haven't used it in practice. And I'm sure in this case you will be labeled as heretic by both people who don't use utility frameworks and those who do ;)[0]: https://tailwindcss.com/docs/gap
[1]: https://tailwindcss.com/docs/space
Anyway, considered harmful articles considered harmful.
There is a time and place for margins. Being thoughtful with them is a better approach than "banning margin from all components."
Should I really ban the use of `margin-left: auto` when positioning my flexed elements? I don't think this article is actually advocating for that, but a novice reader may.
Regarding CSS margins, they are part of the box model even if certain stylesheets neglect them, and the only conflicts they can be involved in are easily solved (a rule for "container x" is more specific than a rule for "x" and can override margins, particularly with parent padding, in special cases) and they highlight genuine design defects: trying to use a component both with and without its "natural" margins (without adding appropriate classes or wrapping containers), trying to specify conflicting margins, trying to fit variable-sized content inside fixed-size containers by messing with the content's required margins, and so on.
It's a shame that CSS doesn't allow the parent element to define how spacing around the child elements should work.
It really would be a shame if CSS didn’t have child selectors, and if they weren’t more specific than bare selectors.
Now you've got more...
- html over the wire
- html to parse for first render
- DOM nodes to mount/unmount
- memory usage from excess DOM
- costly layouts due to extra nodes
- lighthouse complaints of an excessively large DOM
Otherwise a clean approach. Perhaps it could be solved at compile time or some other jsx -> CSS abstraction to maintain DX.
I'm not sure if that's what this post is suggesting, though.
Dead Comment
[1] https://developer.mozilla.org/en-US/docs/Web/CSS
I don't think that "go read all the MDN docs" is a very useful suggestion. More useful is something like the following, which I don't think you will find anywhere in MDN:
When an element's top or bottom margin collapses with its parent, this may affect the position of the parent, because the minimum of the child and parent's margin will be used to position the parent. (MDN says the child element's margin will fall "outside the parent", but it is not clear that it will be used during layout to place the parent.)
The bigger problem is that CSS is just so complicated and arbitrary. Consider the rule for collapsing margins with parents, from MDN:
> If there is no border, padding, inline part, block formatting context created, or clearance to separate the margin-top of a block from the margin-top of one or more of its descendant blocks; or no border, padding, inline content, height, or min-height to separate the margin-bottom of a block from the margin-bottom of one or more of its descendant blocks, then those margins collapse.
They list five different factors that prevent collapse, one of which ("block formatting context created") is a link to a page that defines it in terms of 15 different constructs or situations in CSS.
On top of that, it appears that the MDN text is in error, because it lists intervening "height" as something that prevents bottom margin collapse, but not top margin collapse (when in fact it does).
It's the unintuitive collapsability that messes up margins.
Padding only works if you know everywhere your component might be used or don't mind remaking spacing decisions every time you use it. In my experience minimizing code needed for each use of a component leads to more coherently styled interfaces, particularly on larger or faster moving teams.
I like TeX's box model over CSS which allows for easier "smart" spacing (look up hglue, vglue, hskip, vskip, and eg. care for orphans when typesetting etc.) and smarter column alignment (trivial alignment on decimal point in tables).
While I'd never call myself a designer, I've done plenty of TeX (print and PDF) publications that people have commended for usability and appearance.
Moreover,
> Margin breaks component encapsulation. A well-built component should not affect anything outside itself.
Definitely not, margins are an essential part of a component, like a garden around a house, and they make components "usable in any context or layout" (unless the context or layout is stupidly overcomplicated).
I’ve worked with several awesome designers and they all appreciated when I told them this is untenable.
He reccomends spacing at the parent
Deleted Comment