From a83f7868f0c93cc8e9bd164b8bfb14c454d551c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Casal?= Date: Wed, 4 Oct 2023 22:47:30 +0100 Subject: [PATCH] add The Dark Side Of The Grid article --- app/routes/_marketing+/articles/_index.tsx | 8 +- .../the-dark-side-of-the-grid/index.tsx | 1236 +++++------------ 2 files changed, 381 insertions(+), 863 deletions(-) diff --git a/app/routes/_marketing+/articles/_index.tsx b/app/routes/_marketing+/articles/_index.tsx index 56c894a..1fdff5f 100644 --- a/app/routes/_marketing+/articles/_index.tsx +++ b/app/routes/_marketing+/articles/_index.tsx @@ -17,8 +17,8 @@ import { import fluidTypographySizingWithModularScalesImage from './fluid-typography-sizing-with-modular-scales/fluid-typography.png' import { title as fluidSpaceSizingTitle, description as fluidSpaceSizingDescription } from './fluid-space-sizing/index.tsx' import fluidSpaceSizingImage from './fluid-space-sizing/fluid-space-sizing.png' -/* import { title as theDarkSideOfTheGridTitle, description as theDarkSideOfTheGridDescription } from './the-dark-side-of-the-grid/index.tsx' -import theDarkSideOfTheGridImage from './the-dark-side-of-the-grid/the-dark-side-of-the-grid.png' */ +import { title as theDarkSideOfTheGridTitle, description as theDarkSideOfTheGridDescription } from './the-dark-side-of-the-grid/index.tsx' +import theDarkSideOfTheGridImage from './the-dark-side-of-the-grid/the-dark-side-of-the-grid.png' export type Post = { title: string @@ -30,13 +30,13 @@ export type Post = { export const loader = () => { const posts: Post[] = [ - /* { + { title: theDarkSideOfTheGridTitle, description: theDarkSideOfTheGridDescription, imageUrl: theDarkSideOfTheGridImage, href: '/articles/the-dark-side-of-the-grid', topics: ['Accessibility', 'CSS'], - }, */ + }, { title: fluidSpaceSizingTitle, description: fluidSpaceSizingDescription, diff --git a/app/routes/_marketing+/articles/the-dark-side-of-the-grid/index.tsx b/app/routes/_marketing+/articles/the-dark-side-of-the-grid/index.tsx index 96e116c..04e4caa 100644 --- a/app/routes/_marketing+/articles/the-dark-side-of-the-grid/index.tsx +++ b/app/routes/_marketing+/articles/the-dark-side-of-the-grid/index.tsx @@ -3,11 +3,132 @@ import { Text } from '~/routes/_marketing+/ui+/components/typography/text.tsx' import theDarkSideOfTheGrid from './the-dark-side-of-the-grid.png' import { Link } from '@remix-run/react' import { Kbd } from '../../ui+/components/typography/kbd.tsx' +import { CodeBlock } from '~/components/ui/code-block.tsx' -export const title = 'The Dark Side Of The Grid' +export const title = 'The Dark Side Of The Grid (and Flexbox)' export const description = 'Learn about the Grid and Flexbox features you should avoid' const TheDarkSideOfTheGrid = () => { + const gridWithoutDense = `
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
` + const gridWithDense = `
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
` + const gridTemplateAreasScrewUp = `
+
+ +
+
+ +
+
+ +
+
+ +
+
` + const flexRowReverseScrewUp = `
+
+ +
+
+ +
+
+ +
+
+ +
+
` + const flexColumnReverseScrewUp = `
+
+ +
+
+ +
+
+ +
+
+ +
+
` + const absolutePositionCorrect = `
+ Edit profile + Make changes to your profile. Click save when ready. +
+
+ + +
+
+ + +
+
+ + +
` + const absolutePositionScrewUp = `
+ + Edit profile + Make changes to your profile. Click save when ready. +
+
+ + +
+
+ + +
+
+ +
` + return ( <> @@ -19,924 +140,321 @@ const TheDarkSideOfTheGrid = () => { Introduction - This article assumes you've read the{' '} - - Fluid Typography Sizing and Scales - {' '} - first. - - - Spacing is the cement that holds together the bricks of components on a UI. The importance of good spacing is hard to overstate: it's the most important aspect of design. - And the difference between an exquisitely elegant design and a graceless one. So it's no wonder designers are rightly taught to pay great attention to whitespace. + Putting constraints in place is one of the most powerful principles of Software Engineering. When constraints are selected correctly, they allow you to develop + considerable faster while also avoiding a wide range of mistakes. - However, it remains one of the most unnecessarily unsystematized aspects of web design and development. Most designers still space things out by hand:{' '} - Shift + Left, Shift + Right, Shift + Up, and Shift + Down to move things around by increments of 10px and counting 10, 20, 30 - in their heads is the bread and butter of spacing. + In the case of the CSS Grid and Flexbox, there are a few features that dangerously disconnect visual order from DOM order. This creates an accessibility problem and + here's why. + + + When visual order and DOM order are disconnected + - As for developers, spacing is handled with hard-coded utility classes 🥴, infinitely growing BEM classes 🤢, or, God forbid, plain old ah hoc CSS 🤮. Out of all of these, - utility classes is the least worse: if there's a clear spacing scale agreed upon between developer and designer, it's relatively easy to implement a given design. But if - the designer decides to change a spacing rule that runs across the entire ui, you're going to have a hard time identifying all the instances that pertain to that spacing - rule and update them to something else. + When visual order and DOM order are disconnected this creates a frustrating experience in 3 circumstances: + - Why is that? Because, even if you're using utility classes, those spacing rules have been hard-coded in the UI's components. + So, long story short, if you want your website or app to remain accessible, add this constraint: do not disconnect visual order from DOM order. + + + Unaccessible CSS features + - We can do better. + So what CSS features create this disconnect? Here's the list. + - Let's give spacing the same well-thought-out treatment that we gave typography with a hand-picked scale and fluid space sizing. This enables clear communication between - designer and developer, preserves the designer's complete control over spacing, even after the implementation is complete, and greatly speeds up development. + Let's go through each, one by one. + - Modular scale + grid-auto-flow - For the same reason we're not going to use a modular scale for typography, we're not going to use a modular scale for spacing. The reason is that modular scales are too - limited and you could try different ratios and equations, but at that point you’re just trying to pick a scale that happens to match the sizes you already know you want. + When you set grid-auto-flow: row dense or grid-auto-flow: col dense, you're asking CSS to use an auto-placement algorithm that attempts to fill + in holes earlier in the grid if smaller items come up later. - - Hand-picked scale - - It's more pragmatic to hand-pick the values because this approach grants the designer complete control over the number and variety of space sizes. + This changes the visual order of your items disconnecting it from the DOM order. + + + Here's how that would look like without dense. + + + + Click anywhere on this paragraph and press Tab. Notice how the focus order is correct. Also take into account that this accessibility problem remains true even + if there is nothing to focus on, i.e. no buttons. Screen readers will still read things in DOM order. + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + And here's how that would look like with dense. + - Let's take a look at TailwindCSS's spacing scale. + Click anywhere on this paragraph and press Tab. Notice how the focus order is wrong, because visual order was changed. -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Name - - Size - - Pixels - - Visual representation -
00px0px
px1px1px -
-
0.50.125rem2px -
-
10.25rem4px -
-
1.50.375rem6px -
-
20.5rem8px -
-
2.50.625rem10px -
-
30.75rem12px -
-
3.50.875rem14px -
-
41rem16px -
-
51.25rem20px -
-
61.5rem24px -
-
71.75rem28px -
-
82rem32px -
-
92.25rem36px -
-
102.5rem40px -
-
112.75rem44px -
-
123rem48px -
-
143.5rem56px -
-
164rem64px -
-
205rem80px -
-
246rem96px -
-
287rem112px -
-
328rem128px -
-
369rem144px -
-
4010rem160px -
-
4411rem176px -
-
4812rem192px -
-
5213rem208px -
-
5614rem224px -
-
6015rem240px -
-
6416rem256px -
-
7218rem288px -
-
8020rem320px -
-
9624rem384px -
-
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
- This is a comprehensive enough scale, however a few notes. + Therefore, grid's auto-placement algorithm is a no-go. + + + grid-template-areas + - First, there's one major no-go: semantics are tied with implementation. The names of the steps in the scale (1, 1.5, 2, 2.5, etc) are tied to multiples of 4px, which is - an implementation detail. In order for the designer to have complete control over the design after the implementation is done, semantics and implementation have to be - decoupled. This is absolutely crucial for development speed. + Using grid-template-areas isn't necessarily a no-go. In fact, most of the time it's totally ok to use. You just need to be mindful of the fact that + grid-template-areas describes your layout visually, so you need to keep it in sync with your DOM order. - The other awkward thing is that we have half steps (0.5, 1.5, 2.5, etc) on the lower end of the scale because we need multiples of 2px, and we skip steps (64, 72, 80, - etc) on the higher end of the scale because we don't need those multiples of 4px. This inconsistency makes for a harder-to-communicate scale and an awkward developer - experience where if the designer asks you to step a component's spacing up, you don't go from 64 to 65, but from 64 to 72 🤦‍♂️. + Here's an example of how we could screw this up. + - Let's do better. + Click anywhere on this paragraph and press Tab. Notice how the focus order is wrong, because visual and DOM order have been disconnected. -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Name - - Size - - Pixels - - Visual representation -
00px0px
11px1px -
-
20.125rem2px -
-
30.25rem4px -
-
40.375rem6px -
-
50.5rem8px -
-
60.625rem10px -
-
70.75rem12px -
-
80.875rem14px -
-
91rem16px -
-
101.25rem20px -
-
111.5rem24px -
-
121.75rem28px -
-
132rem32px -
-
142.25rem36px -
-
152.5rem40px -
-
162.75rem44px -
-
173rem48px -
-
183.5rem56px -
-
194rem64px -
-
205rem80px -
-
216rem96px -
-
227rem112px -
-
238rem128px -
-
249rem144px -
-
2510rem160px -
-
2611rem176px -
-
2712rem192px -
-
2813rem208px -
-
2914rem224px -
-
3015rem240px -
-
3116rem256px -
-
3218rem288px -
-
3320rem320px -
-
3424rem384px -
-
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + + flex-direction + - This simple change is deceptively powerful. + flex-direction: column-reverse and flex-direction: row-reverse can also create this disconnect. - First, the designer doesn't have to think about absolute values (4px, 10px, 16px, etc), but relative ones ("this should be more spaced out than that by 2 steps", "that - should be more spaced out than this by 4 steps", etc). Eventually the designer has to converge on absolute values, of course, but as long as the designer starts with a - somewhat decent scale, the flexibility to think in relative terms is a huge speed booster, specially in the early phases of design. + Here's an example of how we could screw this up. + - Also, if the designer gets the relative values right - which is much easier than to get the absolute values right - the implementation will not change: step 30 will still - be step 30, whether the designer decides later that that means 240px or 244px. This frees the developer to develop at full speed and, given an initial mockup,{' '} - in parallel, without having to wait for the designer to get the values right. + Click anywhere on this paragraph and press Tab. Notice how the focus order is wrong, because the visual order was disconnected from DOM order. +
+
+ +
+
+ +
+
+ +
+
+ +
+
- Finally, by giving the designer complete control over the absolute values, we eliminate an entire category of back-and-forth work between designer and developer! The only - possible mistakes or changes that require coordination between the two are those that involve a different relative step. But this is a much smaller set of changes and it - would most likely involve changing a single class on a single component, for example. + And here's the same problem with column-reverse. - - Breakpoint-based space sizing - + - As is the case with typography, we're not going to use breakpoint-based sizing because it creates a jarring user experience and an unsustainable burden on designers and - developers. + Click anywhere on this paragraph and press Tab. +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ - Fluid space sizing + position: absolute - As is the case with typography, if we're going to change the sizing at different viewport widths, we don't need as many steps, because a space of 384px would always be - too overwhelming (it would overflow, in fact), on a 320px wide viewport. + Finally, there's position: absolute. This is one I've personally done thoughlessly for a long time. The thing to keep in mind with{' '} + position: absolute is that it may also change visual order, so you just need to change the order in the DOM to match the visual order. - And we need no variation in the lower steps, some from step 15 onwards, and more variation in the higher steps because of the two opposing forces between the need to - increase the spacing on larger screens to keep the design balanced and the fact that users don't resize their browser windows to see the same content bigger, but to see - more content - and therefore we don't want to just scale up the entire UI (that'd be a terrible UX). + Following Radix UI's{' '} + + dialog component + + , you'd want the cancel button to be the last one to have focus. Here's a correct example. Notice how the cancel button is the last one to have focus. + - So let's cap the scale at step 20 and add a min and a max space size. + Here's a correct example. -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Name - - Pixels (min) - - Pixels (max) - - Visual representation -
00px0px -
-
11px1px -
-
22px2px -
-
34px4px -
-
46px6px -
-
58px8px -
-
610px10px -
-
712px12px -
-
814px14px -
-
916px16px -
-
1020px20px -
-
1124px24px -
-
1228px28px -
-
1332px32px -
-
1436px36px -
-
1540px48px -
-
1648px64px -
-
1764px96px -
-
1880px144px -
-
19128px256px -
-
20160px416px -
-
+ + + Notice how the cancel button is the last one to have focus. + +
+ Edit profile + Make changes to your profile. Click save when ready. +
+
+ + +
+
+ + +
+
+ +
+ - Resize your browser window to see how the bars change size starting from step 15 onwards. + And here's what most developers incorrectly do - including myself for a long time. - - Note on inversion of control - + - The technique we're using here is generally referred to, in software engineering, as "inversion of control". We create an abstraction (the scale) and implement our UI - using it. This allows the designer to retain complete control over the specific values each step in the scale maps to. + Notice how the cancel button is, incorrectly, the first one to have focus. +
+ + Edit profile + Make changes to your profile. Click save when ready. +
+
+ + +
+
+ + +
+
+ +
+ Conclusion - In conclusion, as is the case with typography, between modular or hand-picked scales, hand-picked scales offer the right amount of control, while between breakpoint-based - and fluid space sizing, fluid space sizing offers a significantly more efficient design and development process. This allows the developer to start working right after - mockups are available, while giving the designer complete control over spacing, and offers a smoother (let's call it "fluid" 😉) user experience. + In conclusion, avoid disconnecting visual order from DOM order because this breaks accessibility and usability patterns. This pertains to grid-auto-flow, + incorrect use of grid-template-areas, flex-direction, and incorrect use of position: absolute. - The knowledge I'm sharing in these articles is encoded in my{' '} + All of these accessibility and usability concerns are encoded in  andrecasal/ui - {' '} - component library. All you need from the designer is a mockup and potentially some layout rules (i.e. what to do as the viewport width increases), and you can hit the - ground running building your app. Refine the design details later! + + - I've purposely removed these options from the {''} and {''} components. As for position: absolute, that's up to + you to implement correctly.