Design System Carnival! One accessible component, many pretty masks.

A presentation at React Summit US 2023 in November 2023 in Jersey City, NJ, USA by Kathleen McMahon

Slide 1

Slide 1

Design-systems are a Carnival! One accessible component Many pretty masks | Kathleen McMahon @resource11

Welcome everyone! I’m Kathleen McMahon and I’m here today to talk about a Design System Carnival! One accessible component, many pretty masks. Before we begin, let’s get some details out of the way.

Slide 2

Slide 2

https://noti.st/resource11

My presentation will be posted on Notist, that’s https://noti.st/resource11 — including links to resources — after my talk. I’ll also post the full URL on Twitter after my presentation.

Slide 3

Slide 3

@resource11 Twitter | Instagram | GitHub

You can follow me at resource11 on Twitter, Instagram, and GitHub.

Slide 4

Slide 4

Who are you, again?

Let’s back up so I can introduce myself better…

Slide 5

Slide 5

Kathleen McMahon | Engineer • Designer • Speaker

So I’m an engineer and a designer and I like to speak about things…

Slide 6

Slide 6

image: Me on a cyclocross bike, in Spam, Ninja Turtle, Nerds candy box, Medusa costumes

Occasional cyclocross racer. In costume, of course.

Slide 7

Slide 7

image: a wide-screen view of the colorful lamps and lighting strips adorning my home office desk and bookcases

Lover of lights

Slide 8

Slide 8

image: A wide-planked boardwalk — with soft sand and sea grass surrounding its weathered wooden railing — leads toward the calm ocean during a cloudy afternoon low tide

Beach goer.

Slide 9

Slide 9

image: A montage of sand dollars of various sizes and colors, next to a tiny and medium sand dollar in the palm of my hand.

Sand dollar collector, it’s the Where’s Waldo of the ocean!

Slide 10

Slide 10

image: Thor, resting on the windowsill of my kitchen window, enjoying the warm summer breeze and sunshine on his soft beige fur and dreamy blue eyes.

Lover of my cats Thor

Slide 11

Slide 11

image: Otis — my silky black haus panther — nestled between my purple bed pillows, gazing at me with calm golden eyes.

…and Otis.

Slide 12

Slide 12

image: A gathering of green aventurine, sodalite, rose quarts towers, and quarts diamond on the kitchen table

I’m a Crystal collector

Slide 13

Slide 13

image: Otis, the black wonder cat, steps into the frame of my crystal collection

And I photograph them, but Otis…

Slide 14

Slide 14

image: Otis' paws walk through my crystal arrangement on the kitchen table

…interferes often

Slide 15

Slide 15

image: grouping of citrine, amethyst, and celestite glowing in the sun-washed windowsill

…with opinions…

Slide 16

Slide 16

image: Otis — sassy black cat — sits exactly on top of all my windowsill crystals, seeking attention.

…about my attention. Thanks, Otis

Slide 17

Slide 17

image: In pure art director fashion, Thor sits distinctly on one windowsill sand dollar. Ready to provide his ultimate design judgement..

And now Thor does the same…

Slide 18

Slide 18

image: Thor looks up from the windowsill to the camera with a very satisfied look. His blue eyes gaze hypnotically my way as his pink nose points upward, whiskers pridefully twitching.

He’s very satisfied with himself.

Slide 19

Slide 19

image: Otis looking very cute as he lays upside down on my bed, gazing at me.

Who could resist them, though?

I’ve worked on a few design systems like…

Slide 20

Slide 20

O’Reilly Media

Slide 21

Slide 21

LaunchDarkly

Slide 22

Slide 22

Northwestern Mutual

Slide 23

Slide 23

Design Systems are ALWAYS the hotness image: Person in inflatable dinosaur costume flips into a raft floating on a pond and claps with glee.

And I love design systems; it’s exciting! Especially if you like…

Slide 24

Slide 24

image: Woman attempting to arrange 10 squirming kittens in a straight line, with varied success

…herding kittens. Always fun, with lots of moving parts.

Slide 25

Slide 25

Design-systems are a Carnival!

I think Design Systems are like a Carnival! What do I mean by that?

Slide 26

Slide 26

image: Mannequin wearing Bauta mask adorned with navy Tabarro

Well… I visited Venice last fall and was fascinated by the masks. I loved their beauty…

Slide 27

Slide 27

image: Mannequin wearing plague doctor mask in muted purple costume

their variety…

Slide 28

Slide 28

image: A crowd of Jester masks hanging in a Venetian vendor's market

And… they’re everywhere.

Slide 29

Slide 29

image: Rows of brightly-colored porcelain masks handing on a Venetian market wall

Everywhere you look in Venice.

Slide 30

Slide 30

image: A group of very elaborate and detailed bespoke masquerade art pieces in a Venetian gallery

In the shops.

Slide 31

Slide 31

image: Four tiny jester mask magnets hanging in a Venetian shop doorway

the souvenirs.

Slide 32

Slide 32

image: a white red and orange painted paper mache mask with ribbon ties on a paint-spattered newspapered table

I even made my own mask

Slide 33

Slide 33

image: a person wearing a Jester mask smiling

And I learned the masks have a rich history of providing access. And helping a person blend in…

Slide 34

Slide 34

image: A gathering of people in 18th century costume dancing at the Giacoma Casanova Carnival

…to hide their gender, identity, social class…

Slide 35

Slide 35

image: man walking through a corridor wearing a Baùta and Tabarro

Some of the well-known masks are the Bauta…

Slide 36

Slide 36

image: red-haired woman wearing a small oval mask, made of Velvet, which is held with the mouth thanks to a button.

There’s the Moretta…

Slide 37

Slide 37

mage: man and woman in 18th-century costume wearing cat masks

The Gagna…

Slide 38

Slide 38

image: A man in a Harlequin mask surrounded by two surprised woman and an amused man in 19th-century clothing

The Harlequin..

Slide 39

Slide 39

image: Venetians row during the Carnival masquerade parade on the Venetian Grand Canal.

…and wearing these masks gave you access to places with more freedom — like this parade of gondolas during Venice’s Carnival

Similar to a design system. When well-crafted, masks create consistency of experience.

Slide 40

Slide 40

Is it consistent for everyone?

So, is the experience consistent for everyone, though? Let’s talk about Accessibility.

Slide 41

Slide 41

Our users have different needs at different times.

Slide 42

Slide 42

Vision Sensory Hearing Language Motor Cognitive Low bandwidth

From vision hearing, motor skills, cognitive, sensory, language needs, bandwidth, speed, and more. Our users have so many needs, and so often, accessibility is what is handled last. We don’t want to leave our users behind.

Slide 43

Slide 43

Why accessible components?

Now, what does this mean in terms of accessible design system components?

Slide 44

Slide 44

February 2023 — WebAIM Million — An accessibility analysis of the top 1,000,000 home pages

Well, there is this report called the WebAIM Million report and it’s been released for the past five years now. If you’ve read the results, they’re a bit concerning. It’s an accessibility analysis of the top 1000 home pages. And I’m going to focus on the amount of ARIA present on the homepage.

Slide 45

Slide 45

February 2023 — 29% Increase in added ARIA attributes

There has been up to a 29% increase in the past year…

Slide 46

Slide 46

February 2019 — 60.1% of the 1 million pages had ARIA present

…and the amount of ARIA attributes has nearly quadrupled since 2019.

Slide 47

Slide 47

February 2020 — 64.6% of the 1 million pages had ARIA present

…and the amount of ARIA attributes has nearly quadrupled since 2019.

Slide 48

Slide 48

February 2021 — 68.1% of the 1 million pages had ARIA present

…and the amount of ARIA attributes has nearly quadrupled since 2019.

Slide 49

Slide 49

February 2022 — 74.6% of the 1 million pages had ARIA present

…and the amount of ARIA attributes has nearly quadrupled since 2019.

Slide 50

Slide 50

February 2023 — 80% of the 1 million pages had ARIA present

…and the amount of ARIA attributes has nearly quadrupled since 2019.

Slide 51

Slide 51

February 2023 — 68% More detected errors than those without ARIA present

And there are 68% more detected errors on home pages with ARIA present than those without. So that means that our developers are making the web worse in the spirit of best intentions.

Slide 52

Slide 52

What can we do about this?

So what can we do about this?

Slide 53

Slide 53

This is the W3C’s Web Accessibility Initiative page, and it has some fantastic guidelines for us to follow.

Especially for ARIA authoring practices, design patterns, and examples.

Slide 54

Slide 54

Disclosure widget

Like the Disclosure widget, if built correctly, it can have a few uses in your design system.

Slide 55

Slide 55

One pattern, a few uses

This pattern can be used for popups, toggleTips, and toggleSearches.

Slide 56

Slide 56

image: thumbnails of "accessibility-flavored react components make your design system delicious" talk

Now, I generally with all my talks will give you examples and how-tos on how to make a component accessible, This is not that type of talk.

However.

I will share code examples and resources at the end of my talk so you can dig in.

Slide 57

Slide 57

Anatomy of a disclosure widget

The anatomy of a disclosure widget is…

Slide 58

Slide 58

A button that opens and closes a container then returns focus to the button.

Slide 59

Slide 59

image: WAI site card view of various design patterns, highlighting the disclosure pattern

And the WAI site has a documented pattern for this basic disclosure widget

Slide 60

Slide 60

Primary keyboard interactions | Space bar/Enter key Escape key Mouse clicks

This pattern has three primary keyboard interactions:

• Space bar/Enter key — which comes natively when you use a button to open the disclosure —

• Mouse clicks, and

• And the ESC key, which is part of the extended disclosure pattern

Slide 61

Slide 61

<button />

So when you build a disclosure pattern, you begin with a JSX button because when this code is rendered…

Slide 62

Slide 62

<button> | Supports Mouse clicks Enter keypress Spacebar keypress

…you natively get an HTML button, and an HTML button under the hood gives us:

mouse clicks, enter keypress, and spacebar keypress support for free.

Slide 63

Slide 63

ARIA support

Now it’s time for ARIA support. And…we’ll add just enough ARIA attributes to let screen readers know when the container is being opened and closed — or if you have many widgets on the page — which container is being opened or closed.

Slide 64

Slide 64

On the button, the three ARIA attributes we’re adding are

Aria-controls to tell assistive technology the id of the associated element the button’s controlling. In this case it would be the name of the container’s id.

[jsx code] aria-controls={ariaControls}

Slide 65

Slide 65

aria-expanded, which conveys whether the widget is expanded or not

[jsx code]

aria-expanded={ariaExpanded}

Slide 66

Slide 66

And aria-label for cases where we need a more accurate button label, especially if we are doing icon buttons

[jsx code]

aria-label={ariaLabel}

Slide 67

Slide 67

State & click handlers

We also need state and click handlers to toggle the container open and closed.

Slide 68

Slide 68

We’ll use React’s useState hook to define the isOpen state variable and setIsOpen function, initializing isOpen to false.

[jsx code]

const [isOpen, setIsOpen] = useState(false);

Slide 69

Slide 69

Optionally, we could pass in a boolean defaultExpanded prop, for those cases when we need a widget to be open when a view is loaded.

Like a Terms of Service toggle tip, for example.

[jsx code]

const [isOpen, setIsOpen] = useState(defaultExpanded);

Slide 70

Slide 70

We then make a toggleOpen function that uses setIsOpen to toggle the state between true and false

[jsx code]

const toggleOpen = () => { setIsOpen(!isOpen); };

Slide 71

Slide 71

In the Button, we pass the toggleOpen function into the onClick event

[jsx code]

onClick={toggleOpen}

Slide 72

Slide 72

…the isOpen value into the ariaExpanded prop

[jsx code]

aria-expanded={isOpen}

Slide 73

Slide 73

And add isOpen to the children JSX expression so the children only render in the div if isOpen is true

[jsx code]

<div> {isOpen && children} </div>

Slide 74

Slide 74

Focus management

If you want to take it further, you can add UX enhancements like targeted Focus management.

Meaning, we’ll set our widget up to listen for ESC key presses and return focus to the button when the widget closes

**You should test this with your assistive technology users.

Slide 75

Slide 75

We’ll start by creating a buttonRef using React’s useRef hook, initializing to null

[jsx code]

const buttonRef = useRef(null);

Slide 76

Slide 76

…make an onKeyUpHandler function, listening for ESC key presses while the widget isOpen, And if the widget is open, we’ll set IsOpen to false and send focus to the buttonRef

[jsx code]

const onKeyUpHandler = (e) => { if ((e.key === “Escape” || e.keyCode === 27) && isOpen) { setIsOpen(false); buttonRef.current.focus(); } };

Slide 77

Slide 77

Next, we pass in the onKeyUpHandler function to the onKeyUp synthetic event on the DisclosureWidget’s wrapper div, and

[jsx code]

return ( <div onKeyUp={onKeyUpHandler} className=”dwWrapper”> <Button aria-controls={ariaControls} aria-expanded={ariaExpanded} aria-label={ariaLabel} id={buttonId} buttonRef={buttonRef} buttonClasses={buttonClassNames} icon={buttonIcon} iconOnlyBtn={iconOnlyBtn} onClick={toggleOpen} size={buttonSize} > {buttonText} </Button> <div> {isOpen && children} </div>

Slide 78

Slide 78

Pass in the buttonRef to the Button’s buttonRef prop (say that three times fast) Your Widget will now close on ESC keypress and return focus to the Button

This is the extended disclosure pattern. You should test with users

[jsx code]

buttonRef={buttonRef}

Slide 79

Slide 79

CSS focus states

CSS focus states. Now that we have focus management done, let’s get our CSS focus states normalized.

Slide 80

Slide 80

Since browsers don’t give a consistent look for focus states, we should make it clearly visible for our users when our interactive controls receive focus.

Let’s add some default styling for our :focus pseudo selector throughout our app, and pick a color with at least a 3:1 contrast ratio or greater.

[css code]

*:focus { outline: 0; outline-offset: var(—s-5); box-shadow: 0 0 0 var(—s-5) var(—purple-orchid); }

Slide 81

Slide 81

We can always override focus styles in our individual CSS files to customize them even more, yet it’s always important to show them.

[css code]

.button { font-weight: var(—font-weight-bold); border: 0; border-radius: var(—border-radius); cursor: pointer; display: inline-block; line-height: 1; transition: background-color 0.2s ease;

:hover { background-color: var(—color-primary); border: 1px solid var(—color-primary); }

:focus { outline: 0.1em solid var(—color-primary-reverse); outline-offset: -2px; box-shadow: 0 0 0 0.2rem var(—color-focus); }

:active { outline: 0.1em solid var(—color-aquamarine-500); }

Slide 82

Slide 82

Mouse click management

Mouse click management. If you have many widgets on the page, you’ll only want to have one widget open at a time, so let’s set up a handler to make that happen.

Slide 83

Slide 83

Like our buttonRef, we’ll create a contentRef with useRef, initializing to null

[jsx code]

const contentRef = useRef(null);

Slide 84

Slide 84

Like our buttonRef, we’ll create a contentRef with useRef, initializing to null

[jsx code]

const contentRef = useRef(null);

Slide 85

Slide 85

Create an onClickOutside event Handler function. This will check if the buttonRef or contentRef contains the event target, keep the widget open, otherwise, set IsOpen to false to close the widget

[jsx code]

const clickOutsideHandler = (e) => { if ( contentRef.current.contains(e.target) || buttonRef.current.contains(e.target) ) { return; } setIsOpen(false); };

Slide 86

Slide 86

Now that we have that clickOutsideHandler set up, we can user React’s useEffect hook and some if statements For example, If isOpen is true, we’ll add some document.addEventListener functions for mouseup and keyup and call the clickOutsideHandler for each.

[jsx code]

if (isOpen) { document.addEventListener(“mouseup”, clickOutsideHandler); document.addEventListener(“keyup”, clickOutsideHandler); }

Slide 87

Slide 87

If isOpen is false, we’ll remove those event listeners.

[jsx code]

} else { document.removeEventListener(“mouseup”, clickOutsideHandler); document.removeEventListener(“keyup”, clickOutsideHandler); };

Slide 88

Slide 88

We’ll also do a cleanup function to remove any stray event listeners from running

[jsx code]

return () => { document.removeEventListener(“mouseup”, clickOutsideHandler); document.removeEventListener(“keyup”, clickOutsideHandler); };

Slide 89

Slide 89

Lastly, we’ll pass in isOpen as the second array argument in our hook to be sure useEffect only runs if the isOpen state changes value.

[jsx code]

[isOpen]

Slide 90

Slide 90

image: Arrangement of handcrafted Venetian masks displayed in a shop window

Now we can start using this pattern to make many pretty masks… like, for example…

Slide 91

Slide 91

Toggletips

Toggletips

Slide 92

Slide 92

Toggletips are NOT Tooltips

One important thing to note. A tooltip is NOT a toggle tip; the interactions are different.

A tooltip’s interactivity revolves around hovering over the tooltip and maybe focusing on it with a keyboard.

Slide 93

Slide 93

Toggletips

Toggletips contain interactive content,are supported by touch devices, non-mouse pointers, and eye trackers, where tooltips are not.

Slide 94

Slide 94

Toggletips

But technically, what we’ve built will support what we need with a little styling, and I know you’ll go back to your work and do something like this instead…

Slide 95

Slide 95

Toggletips

Mmm hmm. OK y’all. Fine! We’ll make a Toggle tip with an icon-only version.

But let’s talk about icons for a minute.

Slide 96

Slide 96

Icons — Informative or decorative

Icons can be informative or decorative.

Informative icons need to be paired with descriptive text to be perceivable by screen readers.

Decorative icons need to be hidden from screen readers, because they don’t add significant value to your app.

Slide 97

Slide 97

image: code snippet of our button

Since we want to support icons in our Button component, we’ll add in a new component to the mix.

Slide 98

Slide 98

The FontAwesomeIcon React component. What I like about the ReactFontAwesome icon under the hood, it uses an SVG. Their team preps their SVG in a way where they set it to be focusable=false so they hide it as a decorative icon already. So it’s up to you to use the ARIA labels/everything else to make it informative, if you want.

It’s a nice way to use a decorative icon, then pair it with children into a span and use CSS for positioning.

We’ll gate them behind the icon prop to render them only when we need them.

[jsx code]

{icon && ( <FontAwesomeIcon icon={icon} className={iconOnlyBtn ? iconBtnClasses : iconClasses} size={size !== ButtonSizes.small ? “lg” : “sm”} /> )}

Slide 99

Slide 99

We can pair an icon with visible button text

[jsx code]

<Button icon={buttonIcon}> {buttonText} </Button>

Slide 100

Slide 100

And since we added that aria-label support in earlier, we can pair an icon with an aria-label here and support screen readers

[jsx code]

<Button aria-label={buttonAriaLabel} icon={buttonIcon} iconOnlyBtn={true}

{buttonText} </Button>

Slide 101

Slide 101

And we have a way to add extra CSS to override Button styles in icon-only scenarios

[jsx code]

<Button iconOnlyBtnClasses={styles.btnIcon} > {buttonText} </Button>

Slide 102

Slide 102

Now that we have the button adjusted, we can use our DisclosureWidget and build our ToggleTip

[jsx code]

export default function ToggleTip() { return ( <DisclosureWidget aria-label=“Fruit trivia” buttonClasses={styles.toggleTipButton)} buttonIcon=”apple-alt” iconOnlyBtnClasses={styles.btnIcon} iconOnlyBtn={true} > <div className={styles.floatingContainer)}> <p className={styles.toggleTipContainer}> Hungry for more interesting fruit facts? View a{” “} <a href=”https://google.com”>full list of fruit trivia</a> in at our virtual farm stand. </p> </div> </DisclosureWidget> ); }

Slide 103

Slide 103

First we’ll pass in the buttonIcon, set iconOnlyBtn to true and pass in an aria-label

[jsx code]

<Button aria-label={buttonAriaLabel} icon={buttonIcon} iconOnlyBtn={true}

{buttonText} </Button>

Slide 104

Slide 104

Then, we’ll add some styling to the button and the icon

[jsx code]

<DisclosureWidget buttonClasses={styles.toggleTipButton)} iconOnlyBtnClasses={styles.btnIcon} </DisclosureWidget>

Slide 105

Slide 105

Then, we’ll pass in a div with a paragraph and a link to our widget.

[jsx code]

<div className={styles.floatingContainer)}> <p className={styles.toggleTipContainer}> Hungry for more interesting fruit facts? View a{” “} <a href=”https://google.com”>full list of fruit trivia</a> in at our virtual farm stand. </p> </div>

Slide 106

Slide 106

Toggletip

Boom. ToggleTip

Slide 107

Slide 107

ToggleSearch

Let’s make a Disclosure with an embedded search form.

Slide 108

Slide 108

First…

[jsx code]

export default function ToggleSearch() { return ( <DisclosureWidget buttonClasses={clsx(styles.searchMenuButton)} buttonText=”Find a fruit” buttonIcon=”search” > <form onSubmit={(e) => e.preventDefault()} > <Input label=”Search” name=”value” type=”search” /> <Button>Go</Button> </form> </DisclosureWidget> ); }

Slide 109

Slide 109

We’ll pass in some buttonText and an icon to our disclosureWidget button For the purposes of this prototype, we’ll pass in a form and prevent the default onSubmit action

[jsx code]

aria-label=“Fruit trivia”

buttonIcon=”apple-alt”

iconOnlyBtn={true}

Slide 110

Slide 110

Add an Input component with a label, name attribute and a input type of search, and a Button component. We don’t need to specify a submit type for the button, since it’s embedded in a form and we get that attribute for free.

[jsx code]

buttonClasses={styles.toggleTipButton)}

iconOnlyBtnClasses={styles.btnIcon}

Slide 111

Slide 111

Targeted focus management

An embedded search widget is the perfect use case for some enhanced UX… like targeted focus management.

In other words, when we open the widget, we’ll send focus right to the search input.

Slide 112

Slide 112

To do that, we’ll use the useRef hook to create the DOM reference to our input, create a variable called firstItemRef, and assign useRef to null

[jsx code]

const firstItemRef = useRef(null);

Slide 113

Slide 113

We’ll pass firstItemRef into the DisclosureWidget prop …and our Input component’s inputRef prop

[jsx code]

<DisclosureWidget firstItemRef={firstItemRef} buttonClasses={clsx(styles.searchMenuButton)} buttonText=”Find a fruit” buttonIcon=”search”

<form onSubmit={(e) => e.preventDefault()} > <Input inputRef={firstItemRef} label=”Search” name=”value” type=”search” /> <Button>Go</Button> </form> </DisclosureWidget>

Slide 114

Slide 114

In our DisclosureWidget useEffect’s first if statement, we’ll add a nested if statement checking if the firstItemRef is defined,

And if so, send focus to that firstItemref

We’ll also pass in firstItemRef as second argument to the useEffect function array to prevent component re-renders

[jsx code]

if (firstItemRef) { firstItemRef.current.focus(); }

Slide 115

Slide 115

Embedded search widget

And now we have an embedded search widget. These are some great patterns based on the enhanced disclosure widget.

Slide 116

Slide 116

This is where it gets tricky

This is where it gets tricky because this is the point where things get tempting for a developer.

Slide 117

Slide 117

Hamburger navigation [image: navigation with apple, orange, grape link that visually present as buttons

What if your designer comes up with this hamburger navigation pattern? Hmm. Looks like a good case for a disclosure pattern, right?

Well, if it’s navigation, those items should look like links not buttons, so we should adjust them…

Slide 118

Slide 118

Hamburger navigation

…to have underlines and less of a button-y look to communicate intent. Because cognitive accessibility is important too. Let’s get back to building it.

Slide 119

Slide 119

Hey Links Whoa

Let’s add some links in there.

Slide 120

Slide 120

image: code snipped of unordered list of links

Cool. Let’s add some links into our disclosure widget now. We’ll find a quick way of doing this, like, passing an unordered list.

And mapping those links. Yeah, this is cool. We could just… map in some links.

And hey! We can have a widget with a hamburger menu, and make this toggle menu links just to have a quick thing without even making it a new component. That could be fun.

[jsx code]

export default function ToggleMenuLinks() { return ( <DisclosureWidget aria-label=”fruit choices” buttonClasses={clsx(styles.disclosureButton, styles.chooseFruitButton)} buttonIcon=”bars” iconBtnClasses={clsx(styles.btnIcon)} iconOnlyBtn={true} > <ul className={clsx(styles.floatingContainer, styles.disclosureMenuList)}> {items.map((item, i) => { return ( <li className={styles.disclosureMenuListItem} key={i}> <a href={item.link}>{item.title}</a> </li> ); } )} </ul> </DisclosureWidget> ); }

Slide 121

Slide 121

Cool. Let’s add some links into our disclosure widget now. We’ll find a quick way of doing this, like, passing an unordered list.

And mapping those links. Yeah, this is cool. We could just… map in some links.

And hey! We can have a widget with a hamburger menu, and make this toggle menu links just to have a quick thing without even making it a new component. That could be fun.

[jsx code]

<li className={styles.disclosureMenuListItem} key={i}> <a href={item.link}>{item.title}</a> </li>

Slide 122

Slide 122

How about Buttons Whoa

How about buttons?

Slide 123

Slide 123

image: code snipped of unordered list of buttons

We could pop in buttons in this list as well…

Slide 124

Slide 124

What else could we do with this?

[jsx code]

<li className={styles.disclosureMenuListItem} key={i}> <Button>{item.title}</Button> </li>

Slide 125

Slide 125

Yes… Roving Focus Whoa

How about roving focus?

Slide 126

Slide 126

image: code snipped of roving focus

We could add up and down arrow key support and optional home and end key support, because you see some of those patterns. You see that on components sometimes and even on the WAI site, so that’s kind of nice.

Slide 127

Slide 127

We could add up and down arrow key support We’ll add that to our component. That’ll be fun.

[jsx code]

const handleKeyDown = useCallback( (e) => { if (e.keyCode === 40) { // Down arrow e.preventDefault(); setCurrentFocus(currentFocus === length - 1 ? 0 : currentFocus + 1); } else if (e.keyCode === 38) { // Up arrow e.preventDefault(); setCurrentFocus(currentFocus === 0 ? length - 1 : currentFocus - 1); } }, [length, currentFocus, setCurrentFocus]

Slide 128

Slide 128

Yo TagPalooza I’m clever

Oh wait.

Slide 129

Slide 129

image: code snippet of Tag component

We’re doing a list, but inside this list… how about — instead of passing in a link or button — let’s make this funky Tag component.

Then, we can pass in whatever element we want into the tag prop, and the component will render that element.

Slide 130

Slide 130

image: code snippet of Tag component

We could pass in an “a” and render a link.

Pass in a “button” and render a button.

Pass in an “input” and render an input.

This could be really cool. This could be really powerful!

[jsx code] //don’t do this, yo

const Tag = ${tag};

<Tag> role=”menuitem”

tabIndex={focus ? 0 : -1}

Slide 131

Slide 131

Check out my Fancy List Separation of concerns, baby.

Check out my fancy list, separation of concerns, baby

Slide 132

Slide 132

image: code snippet of List component

And while we’re here we can start working on like some separation of concerns.

So then we can like okay, now that we have like this fancy Tag component, we can make a list item.

Now we can make a list and we have this roving focus and we can start building this pattern.

Oh this is getting fun. Now the component’s getting kind of big.

Slide 133

Slide 133

Some Ternary magic? …Hey now.

Maybe some ternary magic? Hey now…

Slide 134

Slide 134

Oh! And then… in our uber disclosure widget…. we could have our enhanced widget but then if it’s a list we could pass it a list prop, …and we could render a List, …and we’d have have ListItems where we could pass in a tag prop. …and render whatever element we want. What could go wrong?

[jsx code] {isOpen && isList ? (

<div ref={contentRef} id=”dwContainer”> <List /> </div>) : ( <div className={styles.dwContainer} ref={contentRef} id=”dwContainer”> {isOpen && children} </div> )}

Slide 135

Slide 135

image: Jim Halpert slowly reclines in the car passenger seat, hiding in embarrassment

Maybe this is a bad idea.

Slide 136

Slide 136

What pattern is this?

Because at this point, what pattern is this? Now, we’re starting to dip into different patterns. We went from a Disclosure pattern…

Slide 137

Slide 137

image: Menu and Menubar pattern highlighted on WAI page

…into a Menu and Menubar pattern.

Slide 138

Slide 138

image: WAI-ARIA Roles, States, and Properties – WAI page

…and the menubar pattern needs menuitem roles, menuitem checkbox, and menuitem radio roles, and all these different states are needed for unordered lists and their children to support assistive technologies.

A hamburger navigation, however, is the disclosure navigation menu pattern and a different use case, so ARIA is not needed. Yes, you could optionally add arrow and home/end key functionality, but it’s optional.

This is a common place where devs can start incorrectly mixing WAI patterns.

https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/

Slide 139

Slide 139

image: Menu and Menubar Pattern – WAI page

But when you start thinking about those mega menus at the top of a nav that have those fly outs that go to the right, those patterns are simulating desktop menus or Native Electron app behavior with all the checkboxes and the radios in the dropdowns.

Slide 140

Slide 140

image: Mythical University mega menu example – WAI page

That is what a mega menu pattern is, and there’s this confusion between when a designer presents this beautiful pattern, and they don’t understand the logic behind the two patterns that are being coded versus what you see in a design.

Slide 141

Slide 141

image: Menu button pattern highlighted – WAI page

And then it gets mixed into your disclosure widget, and it doesn’t get documented.

And then you get this mega component.

And then people start using it.

And then you get into accessibility audit.

And then there’s this mess.

And so you have these components with this pattern.

Slide 142

Slide 142

image: Menu Button Pattern – WAI page

That mix one pattern

Slide 143

Slide 143

image: Disclosure (Show/Hide) Pattern – WAI page

With another pattern

Slide 144

Slide 144

image: brown squirrel sitting on a picnic table, shouting with outstretched paws

Whoa Nellie… Even though one pattern can have many uses, it’s important to keep your pattern uses separate.

Slide 145

Slide 145

image: Shrimp and lime jello mold, plated with apple slices

And you begin to make unwise decisions… like creating dishes with jello and shellfish. And things can go wrong very quickly.

Slide 146

Slide 146

Don’t mix the masks. [image]: Red squirrel with one paw outstretched in a "please stop!" gesture

SO… You shouldn’t be mixing your masks. And breaking the rules of ARIA.

You should be separating your components out into their purposeful intents.

You may think that your magical mask will give you access to that very exclusive Carnival masquerade, but instead you may end up at a completely different Carnival…

Slide 147

Slide 147

image: vibrant Carnival, with sweeping bright lights of chaos and a clown, Paolo Nicolello — Unsplash

Like this… and then things can go wrong very quickly.

Slide 148

Slide 148

image: Carousel illuminated at night, Lorenzo Dominici — Unsplash

And then it can go even more wrong and…oh god. And here comes the carousel.

Slide 149

Slide 149

image: Open UI homepage

I do have to say, though, the Open UI W3C Community group is working on a solution for native components for this.

Slide 150

Slide 150

A solution is coming

I am very happy a solution is coming.

Slide 151

Slide 151

image: 60-foot inflatable viking monster float lurks over a crowd

Avoid chaotic carnivals like this one. The Viareggio Carnival with some extremely large floats in Italy.

http://www.grandvoyageitaly.com/travel/instead-of-heading-to-venice-try-the-viareggio-carnival

Slide 152

Slide 152

image: 60-foot inflatable viking monster float holding a beheaded Caesar

…and a completely different experience than you wanted. Don’t mix your masks.

Slide 153

Slide 153

Wrap-up

So, to wrap up…

Slide 154

Slide 154

Design systems are a Carnival!

Design Systems are a Carnival!

Slide 155

Slide 155

Our users are diverse

Our users are diverse

Slide 156

Slide 156

image: Red squirrel with one paw outstretched in a "please stop!" gesture

Don’t mix your masks

Slide 157

Slide 157

image: Shrimp and lime jello mold, plated with apple slices

Or you’ll make bad choices

Slide 158

Slide 158

image: 60-foot inflatable viking monster float lurks over a crowd

Or you may end up at a completely different Carnival…

Slide 159

Slide 159

image: 60-foot inflatable viking monster float holding a beheaded Caesar

…with a completely different experience than you wanted.

Slide 160

Slide 160

image: Person in inflatable dinosaur costume flips into a raft floating on a pond and claps with glee.

And Design Systems are always the hotness.

Slide 161

Slide 161

Thank you.

Slide 162

Slide 162

https://noti.st/resource11/

Slide deck posted after the talk

@resource11 Twitter | Instagram | GitHub