Accessibility-flavored React components make your design system delicious!

Welcome, everyone! I’m Kathleen McMahon and I’m here today to show how Accessibility-flavored React components make your design system delicious!

Before we begin, let’s get some details out of the way.

My slide deck will be posted on Notist, that’s https://noti.st/resource11, including links to resources I briefly touch upon. The full URL will be available later today on Twitter.

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

Now… here’s an outline of what we’ll be covering today.

Good grief, there’s an agenda...

Why accessibility first? Design systems are a cookbook. Design systems and React Icons, Buttons, Inputs, disclosure patterns Documentation

Who are you, again?

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

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

[image] Me on a cyclocross bike, hopping over barriers and riding through mud.

And… I race bikes. Very badly.

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

Mostly you’ll see me in costume, racing two laps to your six, at the back of the pack, on a singlespeed, — with a bunch of onlookers in the crowd laughing at me, toasting with their beverages.

Mostly. Unless something happens

[image] Big Bird kicks open the apartment door

Like… a pandemic kicking in our doors like a dude wearing a big bird costume. Then your racing season is postponed.

[image] Pictures of me on a cyclocross bike, in costume

So while I’m an engineer, and a super slow bike racer…

[image] A group of dinousaurs grazing, lift their heads up in unison

I’m also a dev dinosaur. And I find it fascinating to see…

[image] Amiga computer from the 1990s

…how far we’ve come from the tools we had for computing like the Amiga 2000…

[image] Photoshop 3 user interface featuring an image of a tiny baby monkey!

And software like Photoshop 3… Anyone remember that?

[image] Macromind Director user interface

What about Director?

[image] box of older storage tech, including 3.5" floppy disks, Zip disks, Bernoulli disks, SyQuest disks

This is storage we used, 3 1/2” storage drives, Bernoulli drives, Syquest drives, Zip drives I still have my Zip drive. It’s pretty awesome.

[image] Arrangement of all my software reference books from the 1990s through early 2000s

We also used a lot of books for reference. We were not using Google. So this is my library of books, including hits by O’Reilly, DeBabelizer, Director, Flash, CSS, Infini-D…

[image] Netscape logo

While our browser choices were minimal… Netscape, anyone?

HTML + CSS + JS

…our stack has stood the test of time. HTML, CSS, and JavaScript

JavaScript Framework of the week

Fast forward to now and the industry is moving at a really fast pace and it feels like there is a JavaScript framework released every week.

[image] Stanley Spadowski shouts "OPEN WIDE!" and douses boy with a fire hose

…and it can feel overwhelming to keep up, much less find the place where you can thrive.

Especially if you have both design and engineering skills, and love that fundamental stack.

But fear not!

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

Dinosaurs are always the hotness. I have to describe the image because it makes me laugh so many times every time I see it. This is a person in an inflatable dinosaur suit jumping off appear doing a backflip into a floaty raft into a lily pad-filled pond. Enjoy. It’s a joyful dinosaur and that is who I am. And that is why dinosaurs are always the hotness.

Anyhow… those old-school HTML/CSS/JavaScript skills are highly valuable and transferrable — no matter which framework you use — and they will give you an edge in the industry where the div has become the reluctant king.

So before COVID flattened pretty much everything… I was the Tech Lead for O’Reilly Media’s design system.

I learned a lot about streamlining component libraries during my time there.

If you’ve ever worked on a design system, you know there are a lot of things to consider.

And great design systems combine two key factors: user experience, and END user experience.

[image] woman attempting to arrange 10 squirming kittens in a straight line, with varied success

However. Similar to herding cats, creating a great design system can be tough with so many moving parts.

If you are rebooting a design system, will have to choose what to tackle first.

If your design system team is small, you have to be very strategic. For example…

If any business logic in the components, your first priority should be to extract that out, and build with accessibility in mind.

Fix your colors, your components, then reboot your docs.

You may ask, why accessibility first?

Well, our users have varied needs, and… because so often, that’s what is handled last.

Which sends a poor message to our users.

If you’ve read the WebAIM Million report, the results are depressing

With the amount of errors found on pages, 97.8%…

…Unlabeled inputs, 59%

…and 60.1% unnecessary ARIA attributes, we’re making the web worse in the name of good intentions.

While we have things like the Web Content Accessibility Guidelines to follow which is a really low bar, we’ve been missing the mark in the industry when it comes to making sure all our users can use our apps.

Imagine, though, if you had accessibility baked into some commonly-used components.
A design system is the perfect place for this.

I like to say your design system is a cookbook

And cookbooks have personality.

My Mom is a serious fan of cooking, and lately, I’ve been enjoying digging through the cookbooks she’s collected over the years to read how recipes evolved over time.

If you take a look at some of the cookbooks published in the 1940s-60s…

[image] Betty Crocker cookbook from the mid 20th century, "Why nearly 9 million women cherish this cookbook" prominently featured as a callout

And look past the outdated views…

[image] Betty Crocker cookbook from the mid 20th century displaying various dishes

You’ll find interesting recipes…

[image] Shrimp and lime jello mold, plated with apple slices

Questionable food combinations that included Jello and shellfish.

[image] Meats and poultry cookbook reference page

…and an impressive level of…

[image] Soups and sauces reference page

…detail paid to the structure of…

[image] Vegetables reference page

…every single part…

[image] Desserts reference page

…of the cooking process.

[image] Table settings and enertaining reference page

There’s even a section on table settings and entertaining!

This is very similar to how a design system works.

Design Systems and React

Now, what does that have to do with React?

There’s always some debate about how using a Javascript library or framework creates inaccessible apps

Yet… React fully supports building accessible websites, by using standard HTML/CSS techniques.

React is a kitchen utensil

A better way to think of React is to consider it a kitchen utensil. It’s not the only utensil in your kitchen.

You are the cook

In my opinion, It’s up to the developer to have that standard HTML/CSS/JavaScript and accessibility knowledge to be able to leverage a utensil correctly.

And that includes React.

That said, if your developers are unsure how to start building inclusive apps,

Empower with your design system.

Build some features into your components to help them along.

Then you can start celebrating when your co-workers make those apps accessible without you having to ask for it

Components are your tried and true recipes

WCAG is Your reference material

And creating a component is like following a recipe

First, you start with high-quality ingredients (semantic HMTL)

Mix in seasonings (just a touch of ARIA)

Follow the directions in your documentation

And provide helpful hints as best practices Let’s take those principles…

<Thor, cat of mystery, starts meowing insistently> Hi, Thor … that is my cat Thor…

and apply them to some components.

Let’s talk Icons.

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

What does an icon mean, anyway?

But wait! Icons can be interpreted in so many different ways!



True.

For example, you may see the heart symbol used to favorite something in an app

Other apps may use a star. This leaves way too much up to interpretation by the user.

Pair icons with text …when possible

Ideally, you should be pairing any icons you use with visible text.

This way, users will have a clear context on its purpose.

Of course, there is a big BUT in here, and depends on the buy-in you get for icon usage in your designs. And whether you can get a design approved that uses a label paired with an icon like this.

That said, in general terms…

There is more than one way to create an accessible icon. Two of the most recent ways are… SVGs and icon fonts.

At O’Reilly, we initially used SVG icons in our design system combined into a sprite sheet, yet we ran into some problems when we started testing.

[image] Natalie Portman, weeping in the corner

We discovered a bug in Safari on High Sierra, where VoiceOver would announce every single one of those 100 or so icons in that sprite sheet.

It made me sad. So…we had to think fast to find a different solution.

Icon fonts to the rescue

We converted all our icons into a font set for the time being, and chose to revisit SVG icons later, since that HighSierra bug has now been fixed.

Let’s go over an example of how we make an accessible icon component using semantic HTML and the icon font technique.

Icon font pattern

This is what you typically see as an icon font pattern in the wild, a span with a class, but it’s not accessible.

[code] <span class=”icon icon-email”></span>

Accessible icon pattern

This is an accessible icon pattern. Let’s break it down.

[code] <span class=”root”> <span class=”icon icon-email” aria-hidden=”true”></span> <span class=”visuallyHidden”>email</span> </span>

Icon font

The span containing our icon font

[html code] <span class=”root”> <span class=”icon icon-email” aria-hidden=”true”></span>

[css code] .icon { color: currentColor; font-family: “ORM Icons”; } .icon-email::before { <span </span> content: “\f12f”; class=”visuallyHidden”>email</span> }

Hide icon font from screen readers

Has been sprinkled with a pinch of aria-hidden=”true”, to hide the icon font from screen readers

Descriptive icon name

This second span contains the descriptive name for our icon

[code] <span class=”visuallyHidden”>email</span>

Visually-hidden text

And has a visuallyHidden class added to it. This removes the visual presentation of that text, yet keeps that text available to screen readers.

[html code] <span class=”visuallyHidden”>email</span

[css code] .visuallyHidden { position: absolute; overflow: hidden; clip: rect(0 0 0 0); height: 1px; width: 1px; margin: -1px; padding: 0; border: 0; }

Margin/padding support

In the wrapping span, we use a CSS class to convert the span’s native display property from inline to inline-block.

This allows us to support margin/padding customization on all four sides of the element

[html code] <span class=”root”></span>

[css code] .root { display: inline-block; }

Spans all the way down

Notice that spans are being used for all three elements. This is on purpose for when we pair this component with a button

[html code] <span class=”root”> <span class=”icon icon-email” aria-hidden=”true”></span> <span class=”visuallyHidden”>email</span> </span>

Informative or decorative?

Now before we refactor this pattern into React, let’s consider whether this icon is informative or decorative?

Informative icons should announce

If our icon is informative, we keep this markup as is, because informative icons should announce

Hide decorative icons from screen readers

If our icon is decorative, we’d add this aria-hidden attribute to the wrapping span to ensure the entire group is not announced to a screen reader.

Now that we have an accessible icon pattern, let’s pop this into a functional component and convert this to JSX

[html code]

<span class=”root”> <span class=”icon icon-email” aria-hidden=”true” /> <span class=”visuallyHidden”>email</span> </span>

Change class to className, convert that “true” string to a boolean value, and self close that empty span element

Now this is a static icon component in JSX. Let’s make this component more flexible

[code] const Icon = props => { return ( <span className=”root”> <span className=”icon icon-email” aria-hidden={true} /> <span className=”visuallyHidden”>email</span> </span> ); } export default Icon;

…and expand that syntax to support three incoming props: iconHidden, iconName, iconTitle and add some guardrails

Guardrails

It’s important to create ‘guardrails’ for your components so you can be sure that devs are always using the accessibility features you’ve mixed in.

For our icon component, we added three guardrails.

The first guardrail we’ve set up is a check if the iconName the dev passes in to the component is in our icon library.

If the icon doesn’t exist in the library, the component doesn’t render in the app.

[code] if (!icon) return null;

The second guardrail we’ve set here is to ensure if the dev doesn’t pass in descriptive text to that iconTitle prop, the icon’s default name is always exposed a fallback

[code] <span className=”visuallyHidden”> {iconTitle || iconName} </span>

The last guardrail is the iconHidden prop.

If the dev passes in iconHidden true, the containing span’s will render in the DOM with an aria-hidden=”true” attribute.

If no iconHidden prop is passed in, the aria-hidden attribute isn’t attached to the wrapping span at all.

[code] <span className=”root” aria-hidden={iconHidden ? true : null}>

We’re guaranteeing that the icon will read out to screen readers no matter what unless the dev purposely specifies otherwise by passing in iconHidden value.

This true or null pattern works well with HTML attributes that only need to be added if the value exists. aria-hidden is one of those

…but SVGs!

You may still be saying… what about SVGs? What if I haven’t rolled my own icon set?

There are great options out there to do this with SVGs as well.

Like…

Fontawesome is still a great option for this.

In fact, Fontawesome now has an official React component you can use.

And it has been built with accessibility in mind.

It’s pretty straightforward to use this component…

[code] <FontAwesomeIcon icon=“coffee” />

…and it adds the right properties for you under the hood by default. There is a slight bug if you want to use the component as a standalone informative icon.

[code] <svg data-icon=“coffee” aria-hidden=“true” focusable=“false” /> <path>…</path> </svg>

Say you want the icon to announce as a beverage in this case…

[code] <FontAwesomeIcon icon=“coffee” ariaLabel=“beverage” />

…and you pass in an ariaLabel prop

[code] ariaLabel=“beverage”

Yes that aria-label will render. But!

Because the SVG element already has an aria-hidden attribute attached, that aria label will do nothing here.

[code] <svg data-icon=“coffee” aria-hidden=“true” focusable=“false” aria-label=“beverage” /> <path>…</path> </svg>

However. You can get around this by using the Icon component we built and swapping out that span using the CSS class…

[code] <span className={icon icon-${iconName}} aria-hidden={true} />

Then you can remove the unnecessary aria-hidden from the wrapping span. <Thor meows another protest> Thor! It is my kitty.

[code] <span className=“root”> <span className={icon icon-${iconName}} aria-hidden={true} /> <span className=”visuallyHidden”> {iconTitle || iconName} </span> </span>

…and swapping in the FontAwesome component

[code] <FontAwesomeIcon icon={iconName} />

…then you can leverage that visually-hidden span to make your icon informative.

[code] <span className=”visuallyHidden”> {iconTitle || iconName} </span>

Buttons

Buttons

Buttons perform an action on the page

Buttons should look and act like a button

Buttons get screen reader and keyboard functionality for free

Button

Our high quality ingredient here is the button element.

[code] <button class=”root”> Send an email </button>

Button

We sprinkle in an aria-label to support instances where we have multiple buttons with the same name on the page to give context to screen reader users. Like those times when you have 10 ‘read more’ buttons on the page. With this label, you know that this button allows you to read more about dinosaurs.

[code] <button class=”root” aria-label=”Read more about Dinosaurs”> Read more </button>

Button

It’s important when using an aria-label like this to match the first words in the aria-label with the button text to support voice recognition software

[code] <button class=”root” aria-label=“Read more about dinosaurs”> Read more </button>

Button

If your app needs to support localization, Adrian Roselli just wrote a great article with a different pattern that uses the aria-describedby attribute. That article will be shared in the resources later.

Button

This is an accessible button in JSX syntax

[code] <button className=”root” aria-label=“Read more about Dinosaurs”> Read more </button>

If we want to support button text with icons, we mix in our Icon component

[code] <button className=”root” aria-label=”Read more about Dinosaurs> Read more <Icon iconName={iconName} iconHidden={true} /> </button>

And we wrap button contents in a span for positioning Note: we’re using inline level elements inside buttons No nesting buttons or other controls inside a button’s children That’s not valid HTML

[code] <button className=”root” aria-label=”Read about Dinosaurs> <span className=”btnContentWrap”> Read more <Icon iconName={iconName} iconHidden={true} /> </span> </button>

The JSX is popped into the Component’s render method…

[code] export const Button = props => { const { ariaLabel, buttonRef, children, disabled, iconName, onClick, size, type } = this.props; return ( <button className=”root” aria-label={ariaLabel} ref={buttonRef} size={size} onClick={onClick} disabled={disabled ? true : null} type={type}> <span className=”btnContentWrap”> {children} <Icon iconName={iconName} iconHidden={true} /> </span> </button> ); }

and we add props for flexibility, including…

[code] const { ariaLabel, buttonRef, children, disabled, iconName, onClick, size, type } = this.props;

ariaLabel, and the children prop to support custom aria-label and button text

A buttonRef to support focus management use cases.

The iconName for whichever icon we want to use in our Icon component.

An onClick handler to call when the button is clicked, and disabled button support using the true or null method

And support for button size and type

The one guardrail we add ensures if no iconName is passed in, no icon will render in the button

[code] {iconName && ( <Icon iconName={iconName} iconHidden={true} /> )}

Inputs

Inputs need labels and error messages

Labeled inputs give all users more context

Placeholders are NOT labels

Placeholders are NOT labels

Avoid using placeholders instead of labels, users will lose context

Hard to style across browsers

Placeholders aren’t auto translated

Avoid Horizontal Scrolling

We’ve built our input components to minimize horizontal scrolling, and stack our labels above the input, error messages below the input to support screen magnification users.

You can also place the error message right below the label for even better discoverability. Adam Silver has a really great pattern for that in an article on pairing labels and error messages right above the input.

This is an accessible input pattern. It’s a bit hard to read so let’s zoom in.

[code]

<div className=”root”> {label && ( <label htmlFor={id} className=”inputLabel”>{label}</label> {iconName && ( <Icon iconName={iconName} iconHidden={true} /> )} <input className={inputClasses} id={id} name={id} type={type} value={value} disabled={disabled ? true : null} onChange={onChange} onKeyPress={onKeyPress} aria-describedby={invalid && error ? `error-${id}` : null} aria-invalid={invalid ? true : null} aria-required={required ? true : null} /> } {invalid && error && ( <div className=”errorWrapper”> <Icon iconName=”warning-fill” iconHidden={true} /> <span id={`error-${id}`} className=”errorTxt” aria-live=”polite” > {error} </span> </div> } </div>

We start with our high-quality ingredients…

<Thor meowing> I’m going to back up for a second I’m distracted by a cat meowing…

[code]

<div class=“root”> <label for=”inputFoo” class=”inputLabel”> First name </label> <input class=”inputClasses” id=“inputFoo” name=”inputFoo” type=”text” /> <span id=“errorFoo” class=”errorTxt”> Whoops! Error. </span> </div>

…Pairing labels and error messages to the input

[code] <label for=”inputFoo” class=”inputLabel”> First name </label> <input class=”inputClasses” id=“inputFoo” name=”inputFoo” type=”text” /> <span id=“errorFoo” class=”errorTxt”> Whoops! Error. </span>

And in JSX we associate the label with the input by pairing the label’s htmlFor prop value with the input id value

Mix in key ARIA spices for validation.

Aria-invalid, aria-required, and the pair the aria-describedby and error text id values

We also add an aria-live=“polite” prop to the error message span to announce errors to screen readers

To make this more flexible… mix in disabled attribute support.

onChange and OnKeypress synthetic events to capture keyboard actions.

Add true/null handling for the boolean props.

Add your guardrails. If the dev doesn’t pass in a label, the whole input won’t render. Neither will the icon.

This will help guide the developer to follow best practices when implementing a component, and it will become second nature

Same goes for the error handling, if the developer doesn’t pass in both the invalid prop and the error message prop to the component, it won’t render.

Disclosure Widgets

Disclosure widgets. A disclosure widget is an interactive pattern that uses a button to control the visibility of a section of content.

Disclosure Widgets

This pattern is great, because it’s flexible, keyboard/mouse operable, and its base functionality is very similar for more than one type of interactive component.

Menus & Toggle Tips

Some common patterns are nav menus, toggle tips, and settings disclosures.

Other types of widget patterns are custom select menus and autocompletes. We won’t be talking about those today because — in addition to enter/spacebar/escape keys — these two patterns also need up/down arrow keys and the home/end key support.

Tooltips are NOT Toggle Tips

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 tool tip and maybe focusing on it with a keyboard.

Tooltips can be problematic

This can be problematic for any sort of touch device which doesn’t support hover, like a non-mouse pointer or eye tracker.

Also, anyone using a screen magnifier may accidentally dismiss a tooltip by moving their field of view and hovering away.

Sarah Higley has lots of articles on why tooltips are problematic and I will share those resources later on.

Primary keyboard interactions

The three primary interactions we will focus on are the space bar/Enter key — which comes natively when you use a button to open the disclosure — the ESC key, and mouse clicks.

Targeted focus management

For special cases, like a search menu that toggles into view, you can optionally send focus to the first item in the open container.

This is an accessible disclosure widget pattern. I’m going to zoom in and break it down piece by piece and then we will go into demo.

[code] export const DisclosureWidget = ({ buttonAriaLabel, buttonClasses, buttonIcon, buttonSize, buttonText, children, defaultExpanded, firstItemRef, iconClasses, iconOnlyBtn }) => { const [isOpen, setIsOpen] = useState(false); const buttonRef = useRef(null); const contentRef = useRef(null); onKeyUpHandler = (e) => { if ((e.key === “Escape” || e.keyCode === 27) && isOpen) { setIsOpen(false); buttonRef.current.focus(); } }; const clickOutsideHandler = (e) => { if ( contentRef.current.contains(e.target) || buttonRef.current.contains(e.target) ) { return; } setIsOpen(false); }; useEffect(() => { if (isOpen) { document.addEventListener(“mouseup”, clickOutsideHandler); document.addEventListener(“keyup”, clickOutsideHandler); const buttonClassNames = clsx( styles.button, buttonClasses ); if (firstItemRef) { firstItemRef.current.focus(); } } else { document.removeEventListener(“mouseup”, clickOutsideHandler); document.removeEventListener(“keyup”, clickOutsideHandler); } return ( <div onKeyUp={onKeyUpHandler} className={styles.dwWrapper}> <Button ariaExpanded={isOpen} ariaHasPopup={true} ariaLabel={buttonAriaLabel} buttonClasses={buttonClassNames} buttonRef={buttonRef} icon={buttonIcon} iconClasses={iconClassNames} iconOnlyBtn={iconOnlyBtn} onClick={toggleOpen} size={buttonSize} type=“button > {buttonText} </Button> <div ref={contentRef} hidden={isOpen ? null : true}> {isOpen && children} </div> </div> ); return () => { document.removeEventListener(“mouseup”, clickOutsideHandler); document.removeEventListener(“keyup”, clickOutsideHandler); }; }, [isOpen, firstItemRef]); const toggleOpen = () => { setIsOpen(!isOpen); }; }; export default DisclosureWidget;

First, we want to support the following passing in an ariaLabel, button classes, an Icon, Size, and text to the button.

We also want to pass in, children to the widget, classes to the icon, whether the button will be an icon only button, and a ref for focus management.

…and apply the props to a Button component, sibling div, and wrapping div.

The button needs to control the visibility of the sibling container, so we’ll pass in the aria-expanded attribute and an ariaHasPopup prop through to the Button component.

Now, leverage React’s useState hook to control the visibility of the sibling contains passing an isOpen state variable and the setIsOpen method to the useState hook, setting isOpen to false as an initial value.

Then, create a toggleOpen method to use the setIsOpen motion to toggle isOpen between true and false. This will control two things.

The button needs to control the visibility of the sibling container so what we are going to do here is use the React useState hook and use it to set an isOpen variable and declare a setIsOpen function to control the visibility of the DIV’s state.

First we are going to declare a toggleOpen function and in the function whenever the function is activated we are going to toggle the state of isOpen from true to false and back using the setIsOpen function. And we’re going to take the toggleOpen function and pass it into our Button’s onClick prop.

We are also going to pass the isOpen state to the ariaExpanded prop because we need screen reader users to understand that the button, that the aria, we have these two… sorry my cat is distracting me we have these two attributes here that we are going to add to the Button component. AriaExpanded and ariaHasPopup.

The ariaExpanded attribute added to the button allows the button to announce to screen readers whether it is controlling a sibling container and whether the container is expanded. Or not.

If that ariaExpanded is paired with the ariaHasPopup prop and the ariaHasPopup prop is set to true here. Those two props are very important to have in our but component to have a disclosure widget pattern.

If you notice with our is open state is passed into the aria-expanded prop so whenever is open is true aria-expanded is true. Also in our DIV the children in our DIV basically the content of our but container that is opening and closing for our disclosure only shows if the isOpen state is true. We are also adding this hidden attribute to the disclosure widgets container DIV that will only toggle on if the state of is open is set to true. Otherwise the hidden attribute will not attach to the DIV at all.

Next is focus management. React’s useRef is used here to initialize a reference to pass into the button.

Then we declare onKeyUp handler function to check if the escape key is pressed. If it is, the handler will close the widget and return focus to the activator button.

Mouse clicks outside of the widget need consideration as well. React’s useRef is used here to initialize a reference to pass into the button.

Then we declare onKeyUp handler function to check if the escape key is pressed. If it is, the handler will close the widget and return focus to the activator button.

Add use Effect for component mount. If open, added event listener,

Remove event listeners for any other widgets on the page, and only re-render if isOpen changes,

If component is unmounted, remove listeners

If open and firstItemRef passed in to a focusable element in the widget, focus that item.

Disclosure WIdgets Demo

Let’s take a look at some examples of this disclosure widget pattern in this Codesandbox

Document, document, document

Documentation. Those massive design systems out there definitely have some drool-worthy patterns for us to dream about. If your team is small, showing examples of the many ways your component can be used is a good first step.

When we were rebooting the docs, we used Gatsby for our style guide to leverage the power of MDX, and Storybook to document our component playground.

and deployed static instances of our docs using Zeit’s Now…

…otherwise known as Vercel these days.

I want to touch upon Storybook for a moment.

It’s a great way to Sandbox your components in isolation, and play with UI logic without the complexities of business logic.

Storybook supports MDX as well. The Accessibility addon is a must here.

The accessibility add-on uses axe-core engine, does a quick audit of your components Identifies errors, tells you how serious they are, and gives you steps to fix them before you start your manual accessibility testing.

When documenting components, you should add helpful hints to help developers choose how to implement your components. For example, how to make an informative icon.

Or a decorative icon


Be sure to add prop tables for your components, so your developers know which prop does what, and whether it’s required.

Component dos and don’ts are essential.

Dedicate a page to accessibility resources and links. The WCAG success criteria is really intense to parse.

If you have curated any articles that help explain how to implement common accessibility patterns, add those to your docs.

So to wrap up…

Our users are diverse

Your Design system is a cookbook

Cookbooks have Personality

React is a kitchen utensil

You are the cook

Components are your tried and true recipes

WCAG is Your reference material

Document, document, document. And remember…

Dinosaurs are always the hotness.

Thank you.

https://noti.st/resource11 Slide deck posted after the talk @resource11 Twitter | Instagram | GitHub