This is great, thanks for sharing!
I picked up most of this over time, but wouldn't have needed as much with such a great overview as this-- matches all the best practices I learned trying to make sure things looked right c:
What I didn't pick up along the way was accessability... anyone got recommendations for lesser known, quality, maybe even first-hand resources on eg. ARIA?
I'm currently going through https://practical-accessibility.today course by Sara Soueidan. She is really knowledgeable and, most importantly IMO, she practices what she teaches. Her Twitter feed also has some nice tips on accessibility.
Took me a long time to wrap my head around CSS grid to use it like the amazing tool it is.
Then chatGPT came around. Having GPT-4 scaffolding the CSS grids I want by just describing them is one of my top-5 uses for it.
This would be more accurately expressed with negation (though the difference will probably never matter to even a single human):
@media not all and (min-width: 480px)
This is using widest-compatible syntax, since WebKit only very recently (Safari 16.4) got the Media Queries Level 4 stuff to let you write it in clearer ways. The “not” applies to the remainder of the query: parse it as “not (all and (min-width: 480px))” rather than “(not all) and (min-width: 480px)” which would never match.
(As far as I can tell, compatibility charts don’t show this Level 4 query syntax boolean logic stuff at all. Anyone want to do me a favour and file MDN data bug about it? If you do, mention it because otherwise I might finally get round to reporting it later.)
You shouldn't need arcane knowledge of Safari bugs to evaluate a simple media query. This will break on first encounter with a well intentioned maintainer. The option with the least outside knowledge required is usually the most robust choice I feel.
I’m confused by your defensiveness. What I express has the same browser support, but is correct (rather than leaving a tiny gap that is theoretically possible to encounter, though vanishingly improbable in reality), doesn’t depend on esoteric knowledge of the meaning of a 0.02px difference, and sticks with just the one magic number which is obviously desirable for various maintenance purposes. On the downside, “not all and (min-width: 480px)” is an idiosyncratic spelling, and you run the risk of people reaching for “not (min-width: 480px)” instead, which works in current browsers but sheds support for somewhat older browsers (with Safari being the last to get it, only a couple of months ago).
(In the wild, I’ve encountered exact-value overlap, which is obviously wrong and very easy to trigger, −1px, which is obviously wrong and fairly easy to trigger, −0.1px which is fairly safe, and −0.01px, but I don’t recall ever encountering −0.02px.)
If the article had used a negated media query, would you be arguing for using a non-negated .98?
The CSS Media Queries Level 4 spec actually includes fractional pixel values as part of the examples for browsers which do not yet support the range syntax: https://www.w3.org/TR/mediaqueries-4/#mq-min-max
> One approach to work around this problem is to increase the precision of the values used for the comparison. Using the example above, changing the second comparison value to 320.01px significantly reduces the change that a viewport width on a device would fall between the cracks.
The way I tend to work with media queries is not writing them directly but rather use a mixin (when using SCSS) or a media function (CSS-in-JS). That way, this weird quirk can be documented just once in an application, leaving anyone curious enough with the explanation. There tends to be an 'upTo(breakpoint)' function which is an exclusive range, and a 'from(breakpoint)' which is inclusive.
If you have only one clause, using a negated clause is strictly superior. The only reason to go fractional is if you have multiple clauses, since Level 3 only supported negation of the entire thing. I think that’s why using negation has never been particularly common, because people sometimes like to basically do `(width >= 480px) and (width < 768px)`. (In my experience, you’re normally better stacking the queries—e.g. mobile default, then (min-width: 480px), then (min-width: 768px)—rather than attempting to apply them exclusively, though there are certainly sometimes reasons to do it otherwise.)
It's because you say something is superior, and that "not all and (min-width: 480px)" negation syntax is by any programming language standard unreadable. The .98 thing is terrible too, but for me, I don't like when I can't easily parse using parens and PEMDAS. I don't think there is a single correct answer so I felt the need to point out that the breakpoints you disagree with are fine. I think it's fine if you use something else in your code but I don't really want the authors of frameworks I use to move over to "not all and X" that is really "not (all and X).
The author did a great job on that article. But please stop it with the 768px nonsense already. It makes no sense at all and is unnecessarily complicated. The breakpoints should be 600px, 900px etc https://www.freecodecamp.org/news/the-100-correct-way-to-do-...
Indeed, the mythical Standard Tablet Size doesn't exist, none of the device psuedo standard sizes are actually standard.
Devices come in myriad sizes, and resized browser windows come in many more (even on mobile devices)
The most pragmatic solution in my opinion, pick a handful of major layout shift points and fix the layout when it breaks as you resize it, not based on an imaginary universal device size.
I personally prefer sizes that are multiple of 8 because it works well with grid-based layouts and popular CSS libraries and design tools like Tailwind and Figma also adopt this system, although not strictly. However, I agree with your general sentiment that the exact number does not matter much. It will matter less even more when Container Queries become a common practice.
Nice overview, though I would advice against using device-specific breakpoints even beyond giving them device-specific names. Phones continue to get larger every year so designs made for 320px specifically started to become unoptimized when phones became 375px wide, and those in turn started becoming unoptimized when 414px became the norm, then iPhone 14 came out at 428px wide and that'll just continue.
The best strategy then is to let your site's content and layout guide where your CSS breakpoint should be, and designing for the _range_ between them.
That is similar to what I was trying to convey. Breakpoints are just that though, the _point_ at which style rules _break_ into different groupings. Viewport _ranges_ exist between breakpoints.
It is also a pet peeve of mine that breakpoints get referred to as "mobile", or "tablet". Very much agree on making content work at all screen sizes, and picking points where it makes sense to "break" at different viewport sizes depending on the content. At the end of the day, it is very arbitrary.
The other mistake I've seen with the "mobile" and "tablet" mindset is that "desktop" navs that rely on hover don't translate well when the "tablet" is desktop-esque in size.
In general I prefer to presume the user is always touch-based. At the very least, that's their mindset / exportation. Personally, I get annoyed with desktop experience that use hover but don't make it mindlessly obvious that you need to use hover (to get the info I need).
Clever might be clever but it's too often a shite UX.
In theory, you can choose appropriate breakpoints for each element individually, and flow stuff based on automatic sizing perhaps with appropriate growing and shrinking. In practice, almost no one does this, because it’s more work—even though it produces obviously better results.
Well… I did contemplate mentioning this in my comment, but decided to keep it simpler to begin with. It is generally more work, but a part of that is that it basically forces you to do it properly, or the results will be obviously worse; whereas the few-breakpoints approach allows you to be more lazy, and generally has less-bad failure modes, and much more commonly in the direction of wasting absurd amounts of space rather than unusable, broken layout (since hand-crafting breakpoints and such is likely to lead you to dancing at the boundaries of what works). And these mostly-mild failures are definitely encountered more frequently.
Personally, I do custom breakpoints and content-guided layout absolutely everywhere, because I can’t bear to do worse. I haven’t worked with many people in these kinds of areas, none of those I have worked with have been like me in this, and I’ve only rarely spotted work done in this kind of way. The simple fact of the matter is that most people are pretty careless about these kinds of things, and don’t tend to do a thorough job of things. Give them a shortcut and they’ll take it, and “these are the three breakpoints we use” is a convenient shortcut. The trouble is that it’s generally good enough.
It’ll be interesting to see if any of this shifts with container queries, which necessarily work more this way. Ultimately I don’t think it’ll have a big impact, because most places don’t particularly benefit from the complexity of container queries, even though they have apparently been a strongly-desired feature. I’d be delighted to be proven wrong.
But since you push: in the end, yes, most of it is just a different way of thinking and executing. But it is also harder, and pushing to do better is fighting human nature.
I’m still intending a blog post about these and related aspects of my design implementation philosophy (among which is probably my favourite non-mainstream opinion: why I actively prefer `box-sizing: content-box` and believe it’s what you should aim to use). Most people will continue using their few fixed breakpoints, but maybe it’ll help some.
It's still not clear to me why it's harder. Any give page is some series of components. Those have (I presume) a min width and likely a max width as well. With Tailwind and React being all the rage isn't such component-specific styling what's being invested in currently?
Those (by definition) dumb and interchangeable components then exist within a layout. And with flexbox and/or grid you can define "flow" of the components. (Note: I realize I'm over-simplifying but for the sake of discussion I want to be brief.)
Perhaps there should be (another) CSS framework to support this idea? Perhaps then it would be easier then? But conceptually, given the unprecedented powers of grid and flexbox this make better sense than the old static breakpoint model. Given the grid and flex box superpowers there's got to be a way to unlock a better way.
It’s harder because there are more factors to consider, and a lot more subjectivity. Also because it fairly consistently leads to more involved layout calculations, meaning you do definitely need to check how things appear at diverse sizes, and the effects of changing parameters are often less obvious.
IDK. With breakpoints there must not be a lot of checking going on then :)
Much like "mobile first", I guess I'm suggesting "component first". On the other hand, breakpoints are more "layout first". Perhaps this explains why we still see (e.g.) 4 col rows on desktop remain 4 col rows on even smaller screen, to the point the context within each cell gets messy.
I still see it as an education and experience issue* but with ultimately improved UX.
Unfortunately, even though 320px iphones look to be less than IE users for us, 320px may we'll be required for a long time for WCAG (400% zoom at 1280px).
Why is it not a thing to query the literal screen size in cm? Instead we have to work with fake pixel measures and scaling factors and a whole bunch of bullshit and edge cases.
> Besides, you don't want the screen size, you want the viewport size and the DPI.
...I can think of at least one case where you really would want the screen size, although perhaps it's specialized enough to warrant a separate API.
As a user, I have often wished there was a reliable way to view an image on the web at "actual size". Examples would include purchasing band-aids on Amazon, or reading a Wikipedia article about insects. As far as I know, there's currently no good way for websites to offer this, and there really should be!
Device diversity is such that that’s useless for general content. People use devices at less than arm’s length and at multiple metres away. Firefox used to have an experimental resolution-independent millimetre unit mozmm, but devices simply don’t tend to expose what you need so most of the time it wasn’t accurate. Browser makers considered standardising such a thing, but consensus was that (a) it wasn’t possible to make it accurate far too much of the time, and (b) it’s almost never what you want anyway (about the only legitimate uses: simulating a ruler, and physically-accurate sizing for comparison with physical artefacts; certainly never on general-purpose web pages), so it was just dropped.
It's also worth noting that CSS "pixels" are not real pixels. There are somewhat scaled to the relative size. But even better you can set your breakpoints in terms of `rem` which is the user's default font size. This usually gives a pretty good intuition to how big UI components need to be to be easily readable to the user. This can also handle differences such as a user with great vision on a small device (they set a tiny font to get the most of the limited screen space) compared to a user with limited vision on a large screen far away (their text size may be cranked high so that they can see it).
Seems logical, but as a user, I can already choose the size of the content of the screen, I don't want you to choose it for me. So we all agreed that "16px text" is ok and we started designing the world around that.
As a practical example: my iPhone lets me chose between "native" and "scaled up" sizing. This affects the whole OS including the browser. Where does your "cm" measurement fit in that? Do you want to override my (user) choice?
Additionally, every browser lets me scale the content up and down between 50% and 200%, so "cm" is broken once more.
"pixels" haven't been "pixels" for a very long time, and that's ok, they're now just a logical unit.
> To tell the browser that a site is providing an optimised experience for all viewport sizes, you can include the following meta tag in the document <head>:
I see this a lot, but it's actually cargo culting, and all you need is initial-scale.
"You do not need to set every viewport property. If only a subset of the properties are set, then Safari on iOS infers the other values. For example, if you set the scale to 1.0, Safari assumes the width is device-width in portrait and device-height in landscape orientation." https://developer.apple.com/library/archive/documentation/Ap...
It looks like that advice is specific to iOS. Recommendations elsewhere specify that both device-width and initial-scale should be included for compatibility with all mobile devices, e.g. https://developer.chrome.com/docs/lighthouse/pwa/viewport/
To begin with: that documentation looks to be ancient, so be careful as it may not reflect reality any more, though nothing stands out as obviously wrong.
You’ve skipped over the difference:
> Safari assumes the width is device-width in portrait and device-height in landscape orientation
Whereas width=device-width means device-width when in landscape (device-width now being the longer axis), which is I think always going to be what you actually want. If what they document is correct, and it does sound right for how I recall it being described some years back, that the page effectively zooms in when you rotate, then it’s just not what you want.
Testing in desktop Chromium’s responsive design mode, it does seem to behave reasonably in the presence of <meta name=viewport content=initial-scale=1>. I haven’t checked anywhere else.
But if you specify width=device-width, then initial-scale=1 is actually superfluous, that then being the default. I understand that it was part of the common incantation in order to work around Safari orientation change bugs which I think have been fixed for a few years now, but I don’t have anything to confirm it on (really wish they’d provide an emulator for non-Apple platforms) and have never had anyone answer it when I’ve mentioned it here on HN.
> To begin with: that documentation looks to be ancient, so be careful as it may not reflect reality any more, though nothing stands out as obviously wrong.
I've also tested it. In fact I came upon that document in the process of testing, to determine what exactly "width=device-width, initial-scale=1" means.
> But if you specify width=device-width, then initial-scale=1 is actually superfluous, that then being the default.
This is not true. If you have wide non-text content, for example an img, then the page will not necessarily appear at scale 1.
> If you have wide non-text content, for example an img, then the page will not necessarily appear at scale 1.
Hmm, interesting. Thanks for the correction; I confirm this in Chromium’s responsive design mode. Also that (as expected) if you’ve got suitable `max-width: 100%; height: auto` incantations so that it doesn’t actually overflow, this doesn’t apply, and you stay with an initial scale of 1. Frankly, I think that removing initial-scale=1 while developing might be a good thing, since it’ll make overflow more obvious, but I now have a clearer understanding of initial-scale’s purpose and default value. I suppose that means that it’s probably still desirable in general, as most causes of overflow are probably better handled by inducing scrolling rather than scaling the whole document down.
Need to file an issue to fix https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_m... which claims the default is 1, then. Going to add a blog post about this to my TODO list as well, since I had erroneously thought it was solely about that Safari orientation change bug, and have mentioned that belief a few times here on HN and probably misled others thereby.
(Found some potentially relevant spec, too: https://www.w3.org/TR/css-device-adapt/#handling-auto-zoom, though I’m not sure quite what the situation is, HTML Standard seems to cite that spec but a csswg draft, and that link’s a 404, and the @viewport stuff hasn’t ever been implemented and has been retired <https://github.com/w3c/csswg-drafts/issues/4766>, so I’m not sure if there’s actually any nominally-active spec for meta viewport at present.)
Screen size is good for layout but don't forget "pointer". If (not (pointer: fine)) then you really want all your links to be more like button (big target areas easy to hit with a finger), otherwise you're forcing your users to do the zoom-and-click dance, and often completely give up. On the other hand, big button-like links are a waste of space if your users have a mouse, track point or track pad.
I generally disagree with this; the sorts of changes you might make for coarse pointers will tend to help the users of coarse pointers too; my mild inclination is to view any use of any interaction media feature as a nudge to reconsider aspects of your design. They’re really just too coarse to be particularly good, hints of how the user might interact with the document. You probably also want (not (any-pointer: fine)) instead of (not (pointer: fine)), as the spec notes suggest: https://www.w3.org/TR/mediaqueries-4/#mf-interaction. (These spec notes are fairly extensive, because these are delicate matters where it’s easy to make a mess of things.)
When I tried phones and tablets were positive for "any-pointer" (not good!). From my tests the most reliable way to check for a mouse/track point/track pad are (pointer: fine) and (not (hover: none).
Personally, as a user, I don't like a menu that take the whole page when on a desktop, I find a top bar or a hamburger work much better, but those don't work with a small enough screen and without a pointer device (mobile devices), where a vertical menu with big buttons is more appropriate.
But I'm not a UX specialist nor a designer, so this is what I suggest but obviously follow their specs.
49 comments
[ 5.3 ms ] story [ 107 ms ] threadWhat I didn't pick up along the way was accessability... anyone got recommendations for lesser known, quality, maybe even first-hand resources on eg. ARIA?
(As far as I can tell, compatibility charts don’t show this Level 4 query syntax boolean logic stuff at all. Anyone want to do me a favour and file MDN data bug about it? If you do, mention it because otherwise I might finally get round to reporting it later.)
> Why 0.98px specifically? 0.02px is the smallest division of a CSS pixel that an earlier version of Safari supported. See WebKit bug #178261.
(In the wild, I’ve encountered exact-value overlap, which is obviously wrong and very easy to trigger, −1px, which is obviously wrong and fairly easy to trigger, −0.1px which is fairly safe, and −0.01px, but I don’t recall ever encountering −0.02px.)
If the article had used a negated media query, would you be arguing for using a non-negated .98?
> One approach to work around this problem is to increase the precision of the values used for the comparison. Using the example above, changing the second comparison value to 320.01px significantly reduces the change that a viewport width on a device would fall between the cracks.
The .02/.98 values might seem strange, but many popular libraries use these offsets (including Bootstrap: https://github.com/twbs/bootstrap/blob/main/scss/mixins/_bre...).
The way I tend to work with media queries is not writing them directly but rather use a mixin (when using SCSS) or a media function (CSS-in-JS). That way, this weird quirk can be documented just once in an application, leaving anyone curious enough with the explanation. There tends to be an 'upTo(breakpoint)' function which is an exclusive range, and a 'from(breakpoint)' which is inclusive.
It's because you say something is superior, and that "not all and (min-width: 480px)" negation syntax is by any programming language standard unreadable. The .98 thing is terrible too, but for me, I don't like when I can't easily parse using parens and PEMDAS. I don't think there is a single correct answer so I felt the need to point out that the breakpoints you disagree with are fine. I think it's fine if you use something else in your code but I don't really want the authors of frameworks I use to move over to "not all and X" that is really "not (all and X).
@media (width < 480px)
Devices come in myriad sizes, and resized browser windows come in many more (even on mobile devices)
The most pragmatic solution in my opinion, pick a handful of major layout shift points and fix the layout when it breaks as you resize it, not based on an imaginary universal device size.
The best strategy then is to let your site's content and layout guide where your CSS breakpoint should be, and designing for the _range_ between them.
If you want a full overview of CSS Media queries, I maintain an evergreen article with everything there is to know: https://polypane.app/blog/the-complete-guide-to-css-media-qu...
It is also a pet peeve of mine that breakpoints get referred to as "mobile", or "tablet". Very much agree on making content work at all screen sizes, and picking points where it makes sense to "break" at different viewport sizes depending on the content. At the end of the day, it is very arbitrary.
Thanks for the link!
In general I prefer to presume the user is always touch-based. At the very least, that's their mindset / exportation. Personally, I get annoyed with desktop experience that use hover but don't make it mindlessly obvious that you need to use hover (to get the info I need).
Clever might be clever but it's too often a shite UX.
If static brrak points still aren't producing reliable and consistent results, maybe we need a rethink?
Personally, I do custom breakpoints and content-guided layout absolutely everywhere, because I can’t bear to do worse. I haven’t worked with many people in these kinds of areas, none of those I have worked with have been like me in this, and I’ve only rarely spotted work done in this kind of way. The simple fact of the matter is that most people are pretty careless about these kinds of things, and don’t tend to do a thorough job of things. Give them a shortcut and they’ll take it, and “these are the three breakpoints we use” is a convenient shortcut. The trouble is that it’s generally good enough.
It’ll be interesting to see if any of this shifts with container queries, which necessarily work more this way. Ultimately I don’t think it’ll have a big impact, because most places don’t particularly benefit from the complexity of container queries, even though they have apparently been a strongly-desired feature. I’d be delighted to be proven wrong.
But since you push: in the end, yes, most of it is just a different way of thinking and executing. But it is also harder, and pushing to do better is fighting human nature.
I’m still intending a blog post about these and related aspects of my design implementation philosophy (among which is probably my favourite non-mainstream opinion: why I actively prefer `box-sizing: content-box` and believe it’s what you should aim to use). Most people will continue using their few fixed breakpoints, but maybe it’ll help some.
Those (by definition) dumb and interchangeable components then exist within a layout. And with flexbox and/or grid you can define "flow" of the components. (Note: I realize I'm over-simplifying but for the sake of discussion I want to be brief.)
Perhaps there should be (another) CSS framework to support this idea? Perhaps then it would be easier then? But conceptually, given the unprecedented powers of grid and flexbox this make better sense than the old static breakpoint model. Given the grid and flex box superpowers there's got to be a way to unlock a better way.
Much like "mobile first", I guess I'm suggesting "component first". On the other hand, breakpoints are more "layout first". Perhaps this explains why we still see (e.g.) 4 col rows on desktop remain 4 col rows on even smaller screen, to the point the context within each cell gets messy.
I still see it as an education and experience issue* but with ultimately improved UX.
* And yes, perhaps a library / framework/
...I can think of at least one case where you really would want the screen size, although perhaps it's specialized enough to warrant a separate API.
As a user, I have often wished there was a reliable way to view an image on the web at "actual size". Examples would include purchasing band-aids on Amazon, or reading a Wikipedia article about insects. As far as I know, there's currently no good way for websites to offer this, and there really should be!
As a practical example: my iPhone lets me chose between "native" and "scaled up" sizing. This affects the whole OS including the browser. Where does your "cm" measurement fit in that? Do you want to override my (user) choice?
Additionally, every browser lets me scale the content up and down between 50% and 200%, so "cm" is broken once more.
"pixels" haven't been "pixels" for a very long time, and that's ok, they're now just a logical unit.
> <meta name="viewport" content="width=device-width, initial-scale=1" />
I see this a lot, but it's actually cargo culting, and all you need is initial-scale.
"You do not need to set every viewport property. If only a subset of the properties are set, then Safari on iOS infers the other values. For example, if you set the scale to 1.0, Safari assumes the width is device-width in portrait and device-height in landscape orientation." https://developer.apple.com/library/archive/documentation/Ap...
You’ve skipped over the difference:
> Safari assumes the width is device-width in portrait and device-height in landscape orientation
Whereas width=device-width means device-width when in landscape (device-width now being the longer axis), which is I think always going to be what you actually want. If what they document is correct, and it does sound right for how I recall it being described some years back, that the page effectively zooms in when you rotate, then it’s just not what you want.
Testing in desktop Chromium’s responsive design mode, it does seem to behave reasonably in the presence of <meta name=viewport content=initial-scale=1>. I haven’t checked anywhere else.
But if you specify width=device-width, then initial-scale=1 is actually superfluous, that then being the default. I understand that it was part of the common incantation in order to work around Safari orientation change bugs which I think have been fixed for a few years now, but I don’t have anything to confirm it on (really wish they’d provide an emulator for non-Apple platforms) and have never had anyone answer it when I’ve mentioned it here on HN.
I've also tested it. In fact I came upon that document in the process of testing, to determine what exactly "width=device-width, initial-scale=1" means.
> But if you specify width=device-width, then initial-scale=1 is actually superfluous, that then being the default.
This is not true. If you have wide non-text content, for example an img, then the page will not necessarily appear at scale 1.
Hmm, interesting. Thanks for the correction; I confirm this in Chromium’s responsive design mode. Also that (as expected) if you’ve got suitable `max-width: 100%; height: auto` incantations so that it doesn’t actually overflow, this doesn’t apply, and you stay with an initial scale of 1. Frankly, I think that removing initial-scale=1 while developing might be a good thing, since it’ll make overflow more obvious, but I now have a clearer understanding of initial-scale’s purpose and default value. I suppose that means that it’s probably still desirable in general, as most causes of overflow are probably better handled by inducing scrolling rather than scaling the whole document down.
Need to file an issue to fix https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_m... which claims the default is 1, then. Going to add a blog post about this to my TODO list as well, since I had erroneously thought it was solely about that Safari orientation change bug, and have mentioned that belief a few times here on HN and probably misled others thereby.
(Found some potentially relevant spec, too: https://www.w3.org/TR/css-device-adapt/#handling-auto-zoom, though I’m not sure quite what the situation is, HTML Standard seems to cite that spec but a csswg draft, and that link’s a 404, and the @viewport stuff hasn’t ever been implemented and has been retired <https://github.com/w3c/csswg-drafts/issues/4766>, so I’m not sure if there’s actually any nominally-active spec for meta viewport at present.)
This can be stopped as in the article:
html { -moz-text-size-adjust: none; -webkit-text-size-adjust: none; text-size-adjust: none; }
Personally, as a user, I don't like a menu that take the whole page when on a desktop, I find a top bar or a hamburger work much better, but those don't work with a small enough screen and without a pointer device (mobile devices), where a vertical menu with big buttons is more appropriate.
But I'm not a UX specialist nor a designer, so this is what I suggest but obviously follow their specs.