9 ms·
Margin considered harmful
- williamdclt 7y agoSo, the point is that margins on the UI boundaries of a component are a bad idea. Margins are a visual side-effect, which break the idea of keeping components encapsulated. It's not anything new really, everybody has experienced pain to try to reuse a component but it's badly encapsulated so it's painful to fit in a new context. It's always worth making it clear and reiterating basic principle under different lights, but this "considered harmful" like you discovered a new law of physics always feels pretentious to me
- k__ 7y agoIf you avoid them at component definition time and only inject them when using a component, things should be fine.
- crooked-v 7y agoA simple example with React would be anything using the rebass library (https://rebassjs.org https://rebassjs.org). You can extend its Box/Flex components or do a pass-through (e.g. `({a, b, ...rest}) => <Box {...rest}>{... your stuff here ...}</Box>`), at which point when using your own component you can do `<MyComponentHere marginLeft="0.5em"/>` and have native CSS margins without embedding them into the component or into stylesheets for the component.
- gyrgtyn 7y agoYou could argue that margins are part of the element, just like padding and border are, so
- williamdclt 7y agoYou could, but this mental model probably won't be as helpful as thinking of margin as the element pushing its neighbors away (or pushing itself away). It's hard to define margin without any relation to neighboring elements, it's easy for padding and border. It seems logical that a reusable component shouldn't make assumptions about its neighbors, therefore avoid margins on its boundaries.
- city41 7y agoMargin is usually added in response to nearby elements. Making assumptions about nearby elements is what makes components hard to reuse.
- tcgarvin 7y agoMoving spacing concerns to the parent is an interesting idea, would be fun to try out. That said, I almost didn't read the post simply because "Considered Harmful" is the most obnoxious title format I know of.
- phlakaton 7y agoComplaints About "Considered Harmful" Titles Considered Harmful... I like the colorful history of this phrase, and how it usefully marks the following argument as an unbridled assault against purportedly common wisdom, so we can treat it with the huffy disdain with which we should normally treat such cheeky endeavors.
- DonHopkins 7y agoThen you haven't considered how obnoxious a title like "Make Margins Great Again" is.
- aphextron 7y agoCompletely agreed. Margin should only ever be used at the page layout level, where multiple components are being integrated. But spacer components are a terrible idea. Just use CSS.
- gkaemmer 7y agoCurious why you're so opposed to spacer components. In my experience they work quite well--often a designer has specified that two widgets lie a certain distance apart, either vertically or horizontally, and the programmer needs to express that in code. Makes much more sense to use a spacer component than to add a margin to one or both of the widgets themselves. If responsiveness is what you're after, you can easily make your spacers adaptable to different screen sizes.
- aphextron 7y agoBecause then the designer comes back and decides to change that spacing by 5% on one page, but not another. And now you're using the same spacer component across your entire application, so you're adding config to a component that needs to be documented and learned by the next developer using it. Instead, just express that spacing in CSS that is specific to a page. Use mixins if you like to set a base default spacing, then extend it whenever needed. Application/component structure should be completely divorced from presentation. It's literally the same problem we had with <table> based layouts.
- terrortrain 7y agoIn this scenario, you can add props/params to override defaults.
- aphextron 7y ago>In this scenario, you can add props/params to override defaults. Of course. But why? You're just adding complexity at that point. It's the same problem with frameworks like Bootstrap that use complex combinations of class names for styling. Now instead of having a semantic BEM style class name on the component that I can look up in a stylesheet and modify, I have to memorize an entire framework of esoteric 'col-md-whatever' class names and know how/where/when to apply them. It adds a massive cognitive load beyond just using CSS. In the instance of a custom spacer component described above, I'm now also on the hook for documenting and testing a component that is entirely visual, rather than just writing a couple lines of CSS.
- deleted 7y ago[deleted]
- Tade0 7y agoAm I missing something here? Why not just define the margin for those components in that place in the parent component?
- cousin_it 7y agoI agree that spacers are better than margins (and sometimes better than paddings, when people use paddings as margins). Since whitespace is almost always between A and B, ideally the piece of code defining whitespace would be between the pieces of code that are responsible for A and B. For example, it feels nice to define margin or padding on a <p> element, but what does that mean - distance between two paragraphs? Between paragraph and heading? Between paragraph and bulleted list? Using spacers seems like the only way out of this madness.
- seanwilson 7y ago> For example, it feels nice to define margin or padding on a <p> element, but what does that mean - distance between two paragraphs? Between paragraph and heading? Between paragraph and bulleted list? Using spacers seems like the only way out of this madness. Can you explain what spacers are and why they'd be better for this example? Why not use CSS rules that say e.g. "use 1em spacing between p tags", "adding 2em spacing between h1 tags and p tags"?
- cousin_it 7y agoYeah, my bad. I realized after writing that comment that pairwise spacing is possible in CSS: p+p{margin-top:1em} h1+p{margin-top:2em} This says more or less directly "1em between p and p, 2em between h1 and p". The values can be changed independently, with no need for collapsing margins and so on. It's a bit unusual though.
- seanwilson 7y agoThat's the approach I use though. Spacer divs is going to lead to lots of repetition and attaching the margin to a single element always leads you to having to undo it when there's an exception later.
- coffeefirst 7y agoIt means minimum distance. A key feature is margin is it can collapse. So if paragraphs have 1rem between them, and headings have 2rem above and 1rem below, you won’t wind up with 3rem between the paragraph and the following heading. Padding adds together, and space mechanisms seem like overkill when css can already do the thing in a straightforward way. What’s befuddling to me about these threads is that they keep trying to make an absolute case. It’s true, I use margin less now that I have grid gap, but there’s still a place for it.
- msla 7y agoIronic that this is on a page with grotesquely huge margins.
- Veen 7y agoLines of 60 characters are at the lower end of what's usually recommended by typographers, but I wouldn't say it's grotesque. A bit too narrow on large screens though.
- leeoniya 7y ago...and watch your DOM weight at least double in size and JS perf drop substantially due to a ton more components.
- tengbretson 7y agoI guess we're done trying the whole "semantic HTML" thing.
- jakelazaroff 7y agoMargin is a CSS property; using it (or not) is entirely consistent with semantic HTML. The "spacer" component in the article can be achieved with gaps using CSS grid.
- pixelbash 7y agoCSS grid is a lifesaver, but grid-gap does not replace margin unless you have a very regular design.
- the_other 7y agoYou are if you want to spend the entire project fighting the browser.
- seanwilson 7y agoYou should still write semantic HTML, but that doesn't mean every character in an HTML page has to be directly tied to some meaning that isn't to do with styling. Google and screen readers aren't going to be confused by you putting a < div class="spacer > < / div > tag in the middle of some < h1 > and < p > tags on a page. I think coders just need to accept that it's a failed experiment to keep all styling data out of HTML. We've been trying for decades now and it's not practical, especially when you're adding a high level of polish to the presentation of your page while dealing with current web standards.
- tylerchilds 7y ago<br /> <br /> <br />
- laurentdc 7y agoI'm more of a <p> </p> kind of guy. Frontend is all a hack anyways
- diegoperini 7y agoI know nothing but sometimes `!important` works.
- applecrazy 7y agoTailwind helped me break that bad habit with ‘mx-‘ and ‘my-‘
- oefrha 7y agoThere’s a component with awfully wide margins? No problem, -mx-?, -my-?.
- applecrazy 7y agoEvery element has 0 margin by default in Tailwind, meaning this is never a problem. The only other case where I can imagine an element being improperly spaced is when its parent’s padding is messed up.
- oefrha 7y agoWell, there’s no “component” by default in Tailwind. I was addressing the “component with margins” problem in the article.
- seph-reed 7y agoThis also responds well to changes in em size. I like doing it this way because it makes the sizing consistent on mobile vs desktop, as vertical spacing truly should be relative to the font size.
- pastelsky 7y agoIf you start thinking as margin as a relationship between two or more blocks instead of property on a block, some of the issues with it are solved. Whenever possible, I try to use the sibling selector for this – .header + .nav { margin: // } This way, both header and nav elements remain re-usable and the margin only comes into play if they're used in a specific layout, and it is defined in its parent.
- raphaelrk 7y agoI like the way @wongmjane put it, "Basically margin is like side-effect" https://twitter.com/wongmjane/status/1242370883320049664?s=20 https://twitter.com/wongmjane/status/1242370883320049664?s=2... I remember @jpochtar and @gablg1 had a similar take in "Technical lessons from building a compiler startup for 3 years". Under "Don’t trust standards", they wrote "We had no issue w/ using invisible spacer divs instead of the more “semantic” margins or paddings." https://medium.com/@gabriel_20625/technical-lessons-from-building-a-compiler-startup-for-3-years-4473405161cd https://medium.com/@gabriel_20625/technical-lessons-from-bui...
- winrid 7y agoBut the parent consuming a component can add margin to the child component without breaking encapsulation.
- klodolph 7y agoFuck it, I’m gonna use SVG for layout in my next web app.
- graemebenzie 7y agoAdam Wathan & Mark Dalgleish chatting about this http://www.fullstackradio.com/134 http://www.fullstackradio.com/134
- choward 7y agoI almost didn't read the article because of the horrible title. Anyway, I just want to say this is one of the things I like about elm-ui (a ui-library for the elm programming langauge that replaces HTML/CSS). It has the concepts of spacing and padding. No such thing as margin. https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/Element#padding-and-spacing https://package.elm-lang.org/packages/mdgriffith/elm-ui/late...
- bpsh 7y agoI was expecting a finance related article.
- jacobush 7y agoI thought it would be a lean management or economics piece of how low margins are essential to a smooth economy. Or a denouncing thereof. You know, in the days of hospitals being run on just-in-time deliveries.
- Saaster 7y agoOh my sweet summer child. As a pro having done this thing for decades, the industry standard in 2020 is to randomly throw margins, negative margins, and paddings on _everything_ (probably in JS/JSX) in the vicinity of the thing you're trying to position and visually iterate until things looks okay-ish. Probably by a dozen people simultaneously on the project, some of whom may have read an article on CSS once. After a year or two the whole thing is chucked out anyway. And that's all right, it gets the job done.
- 0xff00ffee 7y ago> randomly throw margins, negative margins, and paddings on _everything_ (probably in JS/JSX) in the vicinity of the thing you're trying to position and visually iterate until things looks okay-ish I wish this were not the case, but having researched the front page HTML of the 10 top trafficked websites during a new framework selection for a client, this is exactly how things are done.
- pestaa 7y agoWhat other things did you learn through this research, if I may ask?
- icedchai 7y agoThis made me crack up. I do this all the time and thought it was because I was "bad" at CSS, having not kept up with the times. Is there really no better way in 2020?
- hobofan 7y agoOf course there is a better way, but as he/she said most people don't know it. And for a lot of projects that are built to spec once, and maintained with minimal tweaks for a few years (traditional agency projects), the "bad" way is good enough. If you want a better way for a project that you will have to maintain for a longer time that should be more flexible, I would personally suggest something like BEM[0] + a few best practices (like the one suggested in the post). This will already get you a long way. [0]: http://getbem.com/ http://getbem.com/
- austincheney 7y agoI have never understood why developers think in absolutes and then try to force this absurdity on other people. It feels immature to me and makes me feel like I am in a prison or an insane asylum. Just use the correct tool for the given task.
- chickenpotpie 7y agoThe quest for absolutes makes a lot of sense to me. I understand they are rare, but when we find them they're a godsend. We have to consider so much at once and there's so many ways to do the same thing that having an absolute can make things much easier.
- naringas 7y agoI hope for a "considered harmful considered harmful" article
- goto11 7y agoOf course: https://meyerweb.com/eric/comment/chech.html https://meyerweb.com/eric/comment/chech.html
- devmunchies 7y ago"Considered Harmful articles are problematic, and that's a good thing"
- scarejunba 7y agoHaha, brilliant. If I may write the first paragraph: > Facebook has quietly marked Considered Harmful articles problematic, and that's a good thing. > When I was a child, my father often took me fishing as a way for us to get out of the house. I remember the dappled early Sunday sunlight playing across the dashboard as we drove up to the lake in our 1998 Subaru Forester, a car whose longevity is likely to be greater than mine. Subaru, as it turns out, tapped into a fundamental part of the American psyche that had, at the time, been left completely untouched out of sheer unawareness.
- henriquez 7y agoIt's funny how we've come full circle from using CSS the way it was intended to be used to that being "considered harmful" and now we have the Javascript framework equivalent of < img src="spacer.gif" />, an anti-pattern I was using back in 1999. The reality is that frontend is messy, browsers are still inconsistent with respect to how they treat CSS, and creating new layers of abstraction to handle trivial considerations like adding space on a display are just a way of denying that reality by fixing something that isn't broken. Creating new, completely useless components to "bring us closer to how designers think" completely flies in the face of separating view from the program layer. And in my experience, "designers think" everyone has a 27" Thunderbolt display, or now increasingly a Pro Display XDR, so I wouldn't really put too much stock in what they think. If you disagree, I would propose spacer.gif as a much more succinct and widely accepted alternative to all the harm being done here.
- jpochtar 7y ago> completely flies in the face of separating view from the program layer HTML is the view layer. The "program" layer is JS manipulating JSON (ultimately fetched from a DB). Separating HTML and CSS made sense when content lived in HTML, and styling lived in CSS. Content hasn't lived in HTML in a long time— it lives in a DB now. (Maybe markdown if you're making a static site.) Separating HTML from CSS is separating view layer from view layer— just an unnecessary and expensive indirection
- WrtCdEvrydy 7y ago> everyone has a 27" Thunderbolt display Insert Game of Thrones too dark meme here...
- quotemstr 7y ago> browsers are still inconsistent with respect to how they treat CSS Are they? CSS seems to be rock-solid and consistent now. The days of IE6 randomly breaking the box model are far behind us.
- nessunodoro 7y agogrid and flexbox are still not carved in stone, so catering to tbe lowest common denominator sometimes means e.g. nesting divs to mimic functionality in another browser
- superfrank 7y agoI semi-agree with the point being made, but I don't like the solution. Spacer components isn't the solution, IMO, separating the reusable styles from one time styles is. so instead of: <div class="link"/> <div class="spacer"/> <div class="link"/> <div class="spacer"/> <div class="link"/> We should be doing: <div class="link space-right"/> <div class="link space-right"/> <div class="link"/> This allows the reusable styles to be reused and the spacing to be one off. P.S. since I'm more of a react/styled-components kind of guy, this is how I would look to do it there const Link = styled.div` //... styles ` const LinkWithSpacer = styled(Link)` //... margin ` <LinkWithSpacer /> <LinkWithSpacer /> <Link />
- keithwhor 7y agoThe longer I spend engineering, the more I just want to get the job done and get it over with. I've built, and will continue to build, strongly-opinionated frameworks and platforms based on my experience. But this week's silver bullet is next week's anti-pattern: mostly because of a misunderstanding of when, how, and where to apply design patterns. Truthfully, it's increasingly my opinion that design patterns (and by extension, anti-patterns) only exist in the context of focused, internally consistent systems architected by a small group of individuals and not a committee. Rails, especially early on, is a great example. Actually -- a huge chunk of programming languages are built this way. The web, however, is designed by committee. With this framing, "avoiding margins" works for the way Max builds design systems -- and from what I know, he's very talented and the team(s) that work with him should adhere to the systems he constructs. I'd love to see an implementation of an entire framework / design system that avoids margins -- something like Tailwind CSS. I do not, however, really believe this is broadly applicable advice. Most people are just trying to ship something that looks nice, and margins can be overwritten and adjusted easily. NOTE: I have components I've written that have margins I have to overwrite nearly universally, but can't remove because of other UIs relying on the margin behavior. So I understand where the argument comes from. It's just -- I'd have a hard time if anybody tried to pick a bone with a software engineer on the team about the use of a margin if the initial intention was good / legitimate.
- tylerchilds 7y agoTo chime in on good intentions. Some branding guidelines specify "this logo must always have 20px of space around it". Using margin will guarantee it'll have at least 20px and likely only 20px top and bottom with the way margin collapsing works with CSS. Padding or spacers would just add potentially more space than the minimum design requirement.
- quotemstr 7y agoCan't you just override margin with an "!important" rule? I disagree with this article's exhortation. Cascading style sheets have priorities and override mechanisms for a reason.
- doublejosh 7y agoSorry to be unpleasant... but DUH. This was true long before React. It's a core idea in assembled layouts, that is to say all layouts. Margins need to come from the assembling component, rather than the detail item. Spacing requires awareness of multiple elements, which you usually don't have internally.
- jhpriestley 7y agoSpacer components + flexbox + components defined in JS/JSX is basically a copy of how TeX does things (single unified macro language, hspace/vspace, boxes and glue algorithm which is pretty much flexbox). Feels like we could have saved a lot of time if we'd considered at the outset that maybe Knuth, an actual genius who spent years on this problem, had some pretty good ideas?
- madjam002 7y agoBeen doing this for years! In addition to this I recommend a <Content> component which lays out margins for typography just like you would get in a vanilla HTML document for h1,p,etc, but outside of that everything should use spacing components
- neya 7y agoCall me old school if you like, but the cleaner solution in my eyes are to create well-defined CSS classes for such margins and use them in my designs. <div class="margin-10">..</div> <div class="margin-20">..</div> <div class="margin-30">..</div> is MUCH cleaner than: <div space={3}>..</div> <div space={4}>..</div> The difference being, first of all, I don't need to dig into the source to see what `space` does. Next, appearance layer is tied to CSS and the JS takes care of the rest of the structure of the component.
- hjanssen 7y agoWhat would stop you from calling the space attribute just "margin" in your react component? For me, the second approach does look much cleaner and if I really want to see what you are doing, I also have to open your CSS Stylesheet. I guess it does come down to preference and familiarity.
- simplify 7y agoThis is basically tailwindcss, and is an excellent approach when done well.
- the_mitsuhiko 7y agoEvery web frontend codebase I have every aged incredibly badly. System A was replaced (inconsistently) with system B after half a year. Ideas and concepts thrown out as well.
- hjanssen 7y agoUsing spacing components makes sense for small applications where there is a predictable set of spacings used everywhere in the app. Once you get to the size of an app which requires a dashboard, and many specific distances, some just a tiny bit smaller or bigger, this gets unwieldy very fast. You end up coding around the spacer component or not using it entirely for some situations which makes the whole approach kinda useless. The approach I take is the "pass your own classname"-approach. Im sure somebody has found a fancy name for it. Having a componenent A: const A = ({ className }) => ( <div className={`${COMPONENT_CLASSNAME} ${className}`}> Hello, World! </div> ) You can then just pass your own classname from the parent into the child component. If your component A does not define any margins, it should be easy to define those and not break anything. As simple as that. I find that to be way more flexible than a spacer component, because most of the time, spacing depends heavily on context. The spacing component therefore either becomes too complex and unwieldy or does not get used at all. The approach from above does have a drawback: if you dont pass a className to your component, the resulting class attribute will contain "COMPONENT_CLASSNAME undefined". I have not found that to ever be a problem, although if you really want to, you can also write the template string as follows to mitigate this: `${COMPONENT_CLASSNAME} ${!!className ? className : ""}` But I do find that to be unnecessarily verbose.
- ickelbawd 7y agoYour template string logic doesn't actually mitigate the problem. Short-circuit evaluation in JavaScript means that `falsey && something_else` will always evaluate to `falsey` and then you'll still get undefined in your className string. You would have to use a ternary to avoid this.
- hjanssen 7y agoYes you are right, I have edited my post. It is pretty late.
- __ryan__ 7y agoThis article is not advocating using spacer components like so: <MyComponent></MyComponent> <div class="spacer"></div> <MyComponent></MyComponent> They are describing using layout components to define how children should be oriented and spaced. The article's example: <Stack space={3}> <Item /> <Item /> <Item /> </Stack> Would be basically equivalent to this: <style> .layout-space-3 { > * { margin: 6px; // Or whatever spacing value to use } } </style> <div class="layout-space-3"> <Item /> <Item /> <Item /> </div>
- tylerchilds 7y agoeven better: Stack { display: grid; gap: 6px; } https://codepen.io/tychi-nflx/pen/OJVBZzV https://codepen.io/tychi-nflx/pen/OJVBZzV
- pikelet 7y agoThank you! There are so many knee jerk reactions here which completely miss the point of the article (especially the post about images as spacers). It's not really anything new, wildly complex, or hacky. It doesn't even require components or a frontend framework, it's just a good way to structure your code.
- dpc_pw 7y agoThis should be the top comment. It makes a lot of sense. Margin should be a property of the enclosing container, not the items in it.
- longnguyen 7y agoIn QML/QtQuick, the Positioner does exactly this[1] [1]: https://doc.qt.io/qt-5/qml-qtquick-column.html#spacing-prop https://doc.qt.io/qt-5/qml-qtquick-column.html#spacing-prop
- jpochtar 7y agoHTML is for styling. JSON is for content. If you've heard "separate content from presentation" all your career, you might autonomically hear it as "separate HTML from CSS". I used to. In a React app world, your content hasn't lived in HTML for a long time. It lives in your DB, and comes to you through JSON. Your React codebase exists to style that JSON. That includes "HTML" (JSX), CSS, and whatever else you've got in there. Since your HTML is for styling, and your CSS if for styling, why separate them? If you do, you're not separating content from presentation, you're separating presentation from presentation, and that's just wasteful. Personally, I'd prefer to use inline styles for everything, though of course will use CSS for :hover of @media queries where I have to.
- rustybolt 7y agoHa, I thought 'Margin' would be the name of the latest javascript framework. I'm not sure if the claim that margin is bad is funnier or less funny.
- the_other 7y agoI tried something similar to “spacer components” in a recent project. When I wrote them, I felt smug and clever: I had interpreted the suggestions in React about using components; I had separated my presentation concerns from content or functionality etc etc. It turned out to be a mistake. It added a component (with JSX file, an index.js and a folder, and imports and uses in the consuming file. 10s of lines of code across multiple files and folders. After I had made it, I kept seeing the layout pattern it encoded in other parts of my app. I’d reuse the component, break it, refactor it to cover multiple uses, which sometime broke the original. I’d give up and make a new component that was slightly different. In the end I’d have several of these things, all with long names that were hard to interpret and harder to use. And I was the only coder on that project. Imagine if you had several people doing this? This kinda thing is the same as the problems people complain about when working with large CSS codebases. Doing it in CSS is marginally easier, in my view. You probably have fewer files to maintain and fewer lines of code to work with. Ultimately, I think the problem is in neither the CSS or JS. It’s that layout is really hard. It’s as subtle as language. We strive for regularity and consistency, but if we stick it it strictly, we hamper our ability to communicate.
- deleted 7y ago[deleted]
- DaniAkash 7y agoI have a blank spacer component for react-native https://github.com/DaniAkash/react-native-blank-spacer https://github.com/DaniAkash/react-native-blank-spacer. I have been using this to eliminate margins in my existing projects
- disillusion 7y agoIn my experience, margin isn't harmful. Using a container to space components relative to each other is harmful. In fact, using margin correctly can prevent an explosion of exceptions on the relative positioning of your components. It also brings you more in line with how a designer thinks. Consider this: one of the things a designer takes into consideration is composition and whitespace. In effect, this means that distances between components might change, depending on what their layout is, and what components are situated around it. When you have a wrapper component, you can set some sensible spacing defaults for its children. But some components visually need more breathing room, while others need less. This depends on the design of the component. It also depends on what components comes before or after, so a simple padding won't suffice. Two visually heavy components need more space in between as compared to two that are visually airy. You'll eventually end up with a long list of + selectors to precisely tune every single combination. This quickly gets out of hand. With or without a wrapper element, those exceptions must be applied somewhere. However, when using margins, you avoid all that. Since margins collapse, you can be assured that any combination of components has the minimum amount of whitespace between them, determined by who needs the most. Now this isn't perfect, but it gets pretty close; any component that needs a bit more breathing room can force a larger distance. This way, you might still have one or two combination exceptions, but these will be rare. So, looking at it like this, margins _are_ a property of the component itself: it's the equivalent of a guy stretching his arms out and saying "don't come too close, I need my space!".
- aen 7y agoDesign for the sake of shipping, not for the sake of design.
- campsafari 7y agoHaving layout and component design decoupled is the way to go in largescale component driven systems. Not Necessary in your small wordPress theme like site though