Rules without margins are going to be a mess. For one you can't negative margin the padding out of the first titles in a section and other useful negative margin pulls.
Without margin collapse, you're going to need to handle first and last paragraph so they don't have half margin from their container.
Margins are respected across the elements hierarchy, which is especially useful if you're reusing components in react like systems; neither nested paddings nor grids aren't suitable do that.
I guess I should make all my programs stop printing anything , and creating any files, because I wouldn't want them effecting anything outside themselves.
The Spacer components idea is fine. Using Grid or flex layout in spacers is very sensible. But you could also use margins.
``` .spacer-stack * + * { margin-top: 1em; } ```
If you don't like thinking about them, sure .. introduce the concept of spacer elements in your framework. But outlawing something because you don't fully understand its purpose isn't the way to go.
Maybe the first line of the article?
The implementation of the CSS spec is closely optimised for performance by the browser. Trying to achieve the same outcome by using other means, might feel like a clever way of conceptualising .. but I'd argue there's absolutely no need.
The article's main point is a definitely worthy, arguable one: a React component perhaps should be entirely encapsulated, not affecting anything outside itself. Worth discussing, anyway.
The solution is sadly typical, though. Just grab another component! No interest in what `<Stack>` is actually doing?
How does it solve the problem? Does it use CSS? A single line in the style sheet: `display: flex` or `padding-bottom: 1rem` or something else like that?
This cultural lack of interest is why we have website source code littered with nonsense like `<div class="h1">` and `<div style="position:absolute;">`
Another issue is that if look through job postings carefully, you will see the pattern there where framework knoweledge is valued above everything else. It is "React Frontend Developer" or "Vue Frontend Developer", not just "Frontend developer who is capable to pickup whatever technology we use".
There are reasons for that, of course, but it is hard not to see that this approach will likely skew hiring into looking for a specific knowledge in candidates. And candidates are going along with a path of the least resistance and learning stuff they need to work with backwards.
Edit: The amount of oscillation this comment has had (between -1 and +1) is amazing, I wish I could see total number of up and downvotes..
I would ordinarily agree with you. After all, there is nothing inherent about React that prevents responsible HTML and CSS. And there's nothing about vanilla HTML and CSS that particularly encourages good practice, either.
Except. In practice. React abstracts so much away, that what looks like excellent, well-constructed code (in React) can easily compile into a mess that no one will see unless they go looking for it. And, in practice, I've worked with otherwise excellent teams who find my focus on semantics and CSS honestly baffling and "old school".
I think the rest of your analysis is spot on, though
.stack > * + * { margin-top: 1rem; }https://androidinsider-ru.cdn.ampproject.org/ii/w820/s/andro... — a green one here? (this is a hot link)
Simply put, it’s not a good article.
What does "heavily biased" mean in this context? It's a straightforward article that puts forward a position, a series of propositions supporting that position, and a potential solution.
It's biased in that the author has an opinion he's trying to convince people of by providing arguments in its favour, but that's what writing tends to do (including your comment).
The "banning" stuff is obvious hyperbole[0]. No reasonable reader could conclude the writer means CSS margins should be banned in law.
Any article stating something should be “banned” can’t really be described as a balanced discussion about the usage of that thing.
I’m all for discussions on why usage of something is bad but in the case of margins there’s clearly use cases that are suited to it, which other commenters have highlighted but which the article entirely glossed over.
So the article is more than just written as a persuasive piece; it misses any balance or contextual clarity and thus ends up dishing out bad advice as a result. Bad advice that is so strongly emphasised that lesser experienced developers would easily be intimidated by it.
Now if people want to discuss the strengths and weaknesses of margins and put forward alternative solutions in the process, that at least informs lesser experienced developers about ways to solve their real world problems rather than berating them for not following some misguided idealised view of perfection.
(Yes I know there was some details about alternative ways to implement a margin, but they were crap and lacked any detail, since the article was essentially a rant piece rather than technical document)
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
<section class="stack stack--space-3">
<article class="item">...</article>
<article class="item">...</article>
<article class="item">...</article>
</section>
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:
<section>
<div class="vertical-spacer-2"/>
<article class="item">...</article>
<div class="vertical-spacer-1"/>
<article class="item">...</article>
<div class="vertical-spacer-1"/>
<article class="item">...</article>
<div class="vertical-spacer-2"/>
</section>
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'.
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.
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
.parent > * + * {
margin-top: 16px; // OR
margin-left: 16px;
}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.
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.
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
But margins can be incredibly helpful when implementing internals of complex components, and when laying out components next to each other that are not equal peers (like labels with form fields).
Negative margins can also be very helpful in some circumstances, like when a border shouldn't contribute to the space taken by a component, or when components should overlap without resorting to absolute or relative positioning.
A spacer component is easy to understand and makes consistent spacing easy. There's nothing dirty or impure about it. You just mark it aria-hidden so that both search engines and screen readers ignore them.
Another tactic is to apply component margin spacing on individual instances of a component, as a utility class. It will work most of the time, but is more fragile.
There is but one issue: the internal padding of a component (which is perfectly normal to have) is visually experienced as a margin when two subsequent components share the same background color. In this scenario you're still theoretically consistent with your margins but visually it is not perceived as such.
You might want a component, that as part of its own public API, abstracts away the usage of the spacing component (author calls this "Stack").
Ex: suppose you have a list of images and you always want them to be 8px apart. So you package up the Stack and an <Image /> component, that takes an images[] array.
This prevents the issue where engineers use the raw Stack+Image "atom" components and define random spacing rules: say the spec is 8, some engineers use 6, others 10, etc-- then you have inconsistent spacing in your app.
In addition, if there's also CSS for `:first-child` that removes the leading margin and `:last-child` that removes the trailing, you can easily wrap them inside another component to remove the margins (or introduce a property to collapse them if you prefer). Of course, this is a bit more advanced and needs to be included in the documentation.
Yes, why not?
While eliminating them is hard, the argument here makes perfect sense. We've all been fighting margins one way or another. One could even argue that negative margins are a symptom of the problem.
When you use negative margins you're pulling the element out of the box and essentially causing overflow. Does this fix the issue sometimes? Sure. Is it the best option? Maybe it should just be the exception.
Nope - not really.
There's always been a huge distaste for the box model and it comes from a place of not understanding it vs. anything else. Margins + features like flexbox are a huge feature of modern web development.
If I were interviewing a developer and they started to go off on "ban margins" it would cause the interview to go short.
---
Since you bring up an edgecase to defend the article: negative margins.
Negative margins are almost considered an edge-case use for my entire career which is as-old as the CSS spec. They would show up in situations where complex layouts were just not achievable (holy grail w/dynamic content width and static sidebars pre-flex/grid). The other place they would show up is simple "off-by-one" issues like placing a child div 1px over it's parent border in dropdowns etc.
I'd say for the last 5-10 years specifically they've not been needed and are an artifact of legacy development before flex showed up. If I came across code using negative margins in a PR I'd ask the submitter to refactor.
Idk - just saying that using an old edgecase as an argument against such a ubiquitous feature of the box model (which is core to FE development) seems off to me.
---
One last thought - margins are still perfectly valid with components. It's just that component developers don't want to think about how their component may lay out with something else on the page. It's microservices fiefdom on the front-end calling to give up core web development features because people don't want to think about components interacting with others.
Here's the example linked by the article: https://seek-oss.github.io/braid-design-system/components/St...
The difference being spacer components (and margins for that matter) don't wrap well, which is important for building fluid layouts that don't depend on hard-coded breakpoints.
I find that parent components specifying a gap between their children also happens to align much better with my mental model for designing layouts, not so much with spacer components or margins.
TL;DR: I haven't found any situation so far where I needed to reach for margins or spacer components ever since flex gap became widely supported. They're both dead to me now. Long live the gap.
False, the Stack/Grid style component has been a maintstay of XAML based layouts for over a decade now, and at least generally speaking two decades old. Also, many web frameworks have had this for a while. Braid was not the first one. It's still a very well made framework though but the technical inaccuracy masks a whole load of history.
When I'm the html/css guy on a team, it makes my work super frustrating to have to deal with devs who preemptively try to break down everything into smaller bits. It gets in the way of iteratively improving my templates and css throughout a project.
The solution is simple.
First, you should use the "bottom margins only" approach wherever possible, cf. CSS best practice "use single directions margins only".
https://csswizardry.com/2012/06/single-direction-margin-decl...
If the Vue/React devs I worked this even understood just this, and put single direction margins in their components, there would be less issues already with collapsed margins and/or unintended vertical spacings.
Even better, just get rid of the spacing margins in the component altogether as the article suggest. But instead of creating more abstraction with "spacer" components, you simply add tailwind-style classes on your components such as list items, in the PARENT component's template.
Then the html/css guy till has full control over the templating, it's fluid and simple to edit, and there is no headaches with abstract "spacer" components that also add unnecessary complexity and indentation in the templates.
The main issue with "spacer" components is that in the end, the design is never this regular, There are always exceptions to the rule, and they are much more easily handled as I said by using atomic css classes like tailwind's `mb-*` in the PARENT template of the components you want to "space".
In my experience this approach accounts for ALL scenarios I've run into in a simple elegant way. The PARENT component of those you want to space can always have a bit of logic in React/Vue for those cases where the spacings differ from the default in the app.
Ideally the parent component would just have some styles that only effect the children, so you add this CSS to the parent:
x-item {
margin: var(--space-3) 0 0 0;
}
This works fine for custom elements (web components), but I think the problem is that in React you don't know what element a component is going to render, so it's generally much more difficult to target children this way.This is exactly the problem "spacer components" solve.
[0] https://alistapart.com/article/axiomatic-css-and-lobotomized...
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:
<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 ;)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.
parent > * {
...
}
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.It really would be a shame if CSS didn’t have child selectors, and if they weren’t more specific than bare selectors.
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.
A ground up HTML rewrite is needed.
I accept that we come to this understanding through our early introduction to language, writing and art. Most people intuitively link structure and presentation when they compose content.
Separating them is incredibly useful. You can see this at a really basic level, in plain text. Lay out a single long paragraph of plain text into lines of max 80 chars, and then insert a word. The layout goes completely wrong and if you don't have tools to help you, you spend ridiculous amounts of time realigning the text. The line-length algorithm you're following (or using) is __totally__ separate from the content. HTML and CSS are this, scaled up to allow fine grained annotation and control of both semantics and presentation, independently.
The headless components need to be styled in the end, and the guideline present in the article still applies to those cases: outer margins in components causes some headaches.
I didn't suggest it did. It does mean "block that could be reused somewhere" though. Devs should consider that carefully when they're making components. If your block can't be lifted and reused because it relies on the parent structure or it expects something global that's not passed in as a param/prop/whatever then you need to think some more, because you're making something that's not reusable.
One day, someone, somewhere will want to use your component in a way that you didn't plan for. If you've created a hard link to a particular part of your app such as an expectation that the parent HTML structure will look a specific way, then you've made their job harder when you didn't have to.
The important concept is that HTML tags should convey the meaning of its content, rather than merely being a target for CSS to define its appearance.
For me it's the semantic meaning, a table is tabular data.
However, I must say that using CSS grid is just like the old table-based design days.
https://twitter.com/Martin_Adams/status/1477214581449793538?...
"table-like" layouts made using <div>s and CSS, including `display: table` are fine.
Basically, Google wants to kill XSLT, and normal web in favour of Javascript, and WASM based web. Why? They don't want people to turn off javascripts, so they can show more ads, and competitors cannot get an easier job making google.com alternative.
Yes, that's what the article is talking about.
Even with headless components you need to have CSS somewhere else in the end. I don't know how you're "[leaving] the way things look up to the app that uses them" to headless components, but most people do it with (surprise) other components that belong to the "app" itself. Or with CSS-components.
So, the advice in the article could still apply to them. If you apply a style that contains an outer margin, you might run into the problems discussed by the articles.
A layout component does not have to introduce DOM bloat, it could apply layout directly via CSS to it's children. Layout components in this regard are incredibly helpful.
You could do it that way too. However, when you reply on your component system to help assemble a UI, the components take on the encapsulation role you’d use rich selectors for in other situations. A layout component performs the same function as a utility css class, but is more idiomatic.
* which alone is utter nonsense
The fact that they can be misused does not make them useless.
In a better world, margins would work in a block context (where paragraphs live) but not in flex/grid ones, which take layout in their hands. Another context could be called “constraints-based”, which would space its children relatively, like A.r + 10px <= B.l; B.r = this.r - 10px. Children would expose their preferred margins, which these layout contexts could take or not take into account.
Typesetting something to a beforehand known format is radically different problem to a beforehand unknown format.
But TeX typesets to a beforehand known format (I assume you mean target document size) as much as HTML+CSS does (a bitmap canvas of <width> x infinity size): it's not optimized to reflow stuff live, but you can definitely change the page size before every run with a single command, and rendering should (mostly) reflow properly (this would be equivalent to resizing the window, or having differently sized/fine target like a cell phone).
It does not solve the problem of multiple page sizes any better than HTML+CSS, it just provides some nicer and more obvious tools for aligning stuff on the page/screen that could be more easily used to achieve such support.
Typesetting is a rendering a static copy a priori. You can expend a lot more time and resources rendering and reflowing the document. Rendering HTML+CSS, which can be dynamic, is a radically different problem.
I wouldn't be surprised if these posed very different constraints on the possibilities of the layout model
https://tailwindcss.com/docs/adding-custom-styles#arbitrary-...
If you want to place a slider between two text paragraphs, the slider isn't a good match for them (even if rectangular, it's far smaller) and you'll have to give it small or even negative margins under the assumption of the text paragraphs having normal, big enough margins.
And this highlights the issue with the idea that "children would expose their preferred margins, which these layout contexts could take or not take into account": a reusable and modular layout should be bottom-up, with components knowing what CSS rules make them look good and containers respecting them. Since the slider is small it can have smaller margins than the text paragraphs, but a container that knows that its middle child is a slider is a complicated and ad-hoc container.
I don’t, I want to put a slider between A and B. I don’t know what’s there from the bottom-up rule. A or B may even have a loadable content, so I know nothing until it’s rendered. If A and B are paragraphs, that’s usually a coincidence irrelevant to the container’s purpose.
So what is the correct way to put a slider between beforehand unknown type A and type B components?
a reusable and modular layout should be bottom-up, with components knowing what CSS rules make them look good and containers respecting them
This approach is good until it’s not. You can’t just write off some layouts as “bad” and call it a day. It’s not modular, it’s pre-styled in a way that involves intimate knowledge about all of components and how they “stack” in advance, which is opposite to being modular.
The components around the slider are not "unknown": it's enough to be sure that, for example, their margins are between 20 and 30 px so the slider is allowed to claim -5px off a collapsed margin without collisions.
TeX's box model is in no way less optimizable than the HTML+CSS (if anything, it's the opposite due to it being much simpler). It has had a stable algorithm and implementation since 1983, and that implementation was never optimized for reflow like HTML + CSS has. TeX itself is also Turing-complete, which makes processing more expensive by definition, and that's mostly the reason for the slow performance, rather than the box model — and it is from 1983, after all. For some things, the reflow model is even used with TeX today (eg. table of contents generation usually requires at least two passes to "reflow" the output).
>6.6.4 Inspiration from other formatting models In the early design phases of the CSS visual formatting model, other formatting models were frequently consulted for inspiration. In particular, TeX [Knuth 1984] was often brought up in white-board discussions.
As one of its foundations, TeX has a well-defined box model wherein all objects, including individual glyphs, are contained in boxes. The spacing between the boxes can be controlled through TeX commands. In addition to optimal spacing between boxes, TeX also allows maximum and minimum spacing to be expressed. This is referred to as glue (although Knuth suggests that springs is a better term [Knuth&Plass 1981]).
The visual formatting model in CSS is based on a box model, and all elements, both block-level and inline, are turned into boxes. Thus, CSS goes further than most other style sheet languages in creating boxes. For example, DSSSL and P94 do not use boxes for inline elements. However, CSS did not adopt TeX's glue. Although the issue was discussed, it was decided against in order to keep the VFM simple. Glue is very useful when breaking paragraphs into lines, but CSS leaves this problem to implementations. CSS allows, but does not demand, inter-paragraph line-breaking optimizations. Each box in CSS is, however, potentially richer than the boxes found in TeX since it can contain a padding, border and margin bands. CSS also borrows other features from TeX, including the em and ex units.
https://www.wiumlie.no/2006/phd/#ch-css
Håkon also goes into great detail on the historical context and technical requirements surrounding the development of CSS.
I think the reason the CSS box model differs from the TeX box model is because of the different problems they attempt to solve and various contextual difference the technologies developed under. Crucially, it's not because the authors were unaware of the TeX box model or because they thought they could do something better
That also supports my claim that reflowing in TeX's box model would be simpler ("CSS is richer").
I simply highlighted a few things where TeX's box model does a great job for what are commonly hard problems with CSS even today (how do you align columns on decimal points?): I imagine most people, even LaTeX users today, are unaware of the internals, and I hope it's valuable for people to learn of them.
Its performance was not just measurably worse, it was obvious as soon as I opened the revision on my phone.
Lesson learned: it's important to think twice before using most pseudo selectors
That sounds so cool! Do you have a version somewhere that I can take a look at? I always like fiddling with clever CSS, even if it is too clever for its own sake, as you managed to find out
And keep in mind, that pseudo selectors are not created equal. Things like :checked or :focused usually don't have that much of a performance impact
I think you're overestimating what I did though, the basic idea is this recreation.
https://codepen.io/wohlben/pen/gOGKejv
it was just a lot more elements in the navigation with subcategories etc, but basically the same methods
(remember to check the mobile view, as thats the one thats using most selectors. Though the desktop view is arguably better)
this recreation is probably very confusing though, as the only hint that there are more links hidden for the category are the missing corners - and there is nothing hinting you have to click the category names to show these hidden options. and i skimped on proper accessibility for this mockup, so the hidden options can't be selected without touch/mouse
The JS just emulates what opening the page would have done. It was SSR
I'm sure this hasn't been relevant for about 15 years. CSS rendering is _fast_.
This especially comes into play, if you deal with a lot of nested HTML elements or when you modify the site's HTML a lot.
It seems like > *+*, *:not(first-child), *:nth-child(1+n) and *:last-child { ...revert... } are all equivalent. I would be curious to know which is most efficient.
(this semi helpful advice brought to you by mst's complete lack of front-end chops meaning concrete help will have to come from somebody else ;)
It was never something I had thought about but I got curious so I did some digging.
It's true that some things are much easier in TeX, like aligning on decimals. HTML tables are used for all sorts of crazy so I'm not surprised it's harder to that kind of stuff