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 that wears many pretty masks. Before we begin, let’s get some details out of the way.

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.

@resource11 Twitter | Instagram | GitHub

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

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

Who are you, again?

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

Kathleen McMahon | Engineer • Designer • Speaker

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 sometimes race cyclocross. 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 bicycle — with a bunch of onlookers laughing at me, toasting with their beverages.

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

Now I think y’all agree these past few of years have been intense. Right?!

image: Group of penguins gathered on an iceberg with a few swimming and jumping in the ocean. One penguin attempts to jump atop an iceberg to join their penguin friends — and fails — splashing back into the water in ungraceful fashion.

See the penguin in the middle of the screen? The one that failed to jump on the iceberg? That was me. Burning out. Surviving Covid layoffs. Tech interviews. New jobs. The isolation of lockdown. More burnout. So.. I did a few things to survive. Like…

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

Spending my time getting crafty with lighting setups during the colder months.

image: Thor, my floofy beige-striped cat, rests in his bed in my colorfully-lit office

Photographing my cats Thor…

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.

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

…and Otis…

image: Otis and Thor — in perfect light and dark fur contrast — nestle together atop a soft red flannel throw.

As this photo of them snuggling clearly shows… they are always fighting. It’s a problem.

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

And in the summer, I go to the beaches!

image: Lush green foliage surrounding fiberglass boardwalk leading to the ocean at dawn. The bright orange "Public access 1" sign accenting the way to ocean bliss.

At different times…

image: Weathered wooden railings surround a raised boardwalk leading to an expanse of beach at low tide. Two individuals sit in beach chairs nearby glancing at the calm ocean water.

Different days, and…

image: Weathered beige wooden railings and tall sea grass surround soft light beige sand leading to a calm surf at peak low tide. The late morning sun reflecting off of wispy cirrus clouds.

Different locations…

image: Dark gray weathered wooden railing and tall sage green sea grass surround a fiberglass boardwalk leading to a deep blue ocean at low tide in the late afternoon. Two fluffy white clouds sail across the clear blue sky.

Always at low tide…

image: Calm waves lapping against the sand

Which is the best time…

image: Dark beige wet sand, ready to reveal a baby sand dollar

…to look for sand dollars

image: A sand dollar peeking out of a cluster of rocks, barnacles, and periwinkle shells in a crowded ocean tidal pool.

It’s like the Where’s Waldo of the ocean.

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.

So I get a serious…

image: A tiny pearlescent shell sits on the side of a dark gray sand dollar in the palm of my hand. Beneath my hand is the cool waters of a very crowded tidal pool full of rocks, shells and ocean water.

Collection of sand dollars!

image: A gathering of cream-colored sand dollars and a large periwinkle shell in a large open clam shell sitting atop a brown wooden bench at the beach

And I photograph <click>

image: A gathering of a dark gray sand dollar, large and small periwinkle shells in a large open clam shell sitting atop a weathered gray wooden bench at the beach

My treasure

image: A gathering of sand dollars of many colors and sizes in a large open clam shell sitting atop the sand next to some seaweed at the beach

Every time

image: A gathering of sand dollars of multiple shapes, sizes and colors on my dining room windowsill.

Trying to do this at home is a problem… because of the Otis factor.

image: Otis — black cat of magic — lovingly gazing at me with golden eyes while lying upside down. Paws outstretched.

Because Otis is especially needy…

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

See, during lockdown before burnout hit peak, I also started a heck of a crystal collection. And attempted…

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

To get good photos. But Otis was having none of that…

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

None…

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

…of it…

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

None of it.

image: A gathering of sand dollars of multiple shapes, sizes and colors on my dining room windowsill.

So my attempts at this…

image: Otis — always insistent black cat — standing on my dining table, inspecting my gathering of sand dollars on the dining room windowsill.

Yeah… nope.

image: Thor — ever curious fluffy beige Manx cat of Wonder — standing on my dining table, inspecting my gathering of sand dollars on the dining room windowsill.

And now he’s even taught Thor…

image: Thor bravely places his white-mittened beige paw on the windowsill gathering of sand dollars.

To do…

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

The same…

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.

Thor looks pretty satisfied.

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

Who could resist Otis though?

image: Otis expectantly gazing at me from his cat tree perch.

So here is Otis. Attending by proxy.

image: Penguin — running forward on an icy tundra — skids to a stop.

Anyhow… When it was time to head to here to see people in person, I felt so excited! People. Finally!

image: Group of penguins on tundra running away

Then I remembered… omg… talking to people. How many of you felt the same? My introvert did a little panic. Did any of you feel the same?

image: Penguin — running forward on an icy tundra — skids to a stop.

But excitement won out, y’all! And now I’ve shaken off my stage fright anxiety…

Thanks for your patience

Thanks for your patience.

So… you’re here to learn about Design systems carnivals

Northwestern Mutual

Currently, I’m working with an amazing group of people at Northwestern Mutual as a Senior Design Systems Engineer, helping them take the Luna Design System to the next level.

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’m a super fan of design systems, so this is exciting work!

image: Visual of the various intersecting parts of a design system.

If you’ve ever worked on a design system, you know there are a lot of things to consider. Which can sometimes feel like…

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

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

Similar to herding kittens, maturing a great design system can be tough with so many moving parts.

This means you have to be very strategic and choose what to tackle.

Design-systems are a Carnival!

It also means Design Systems are a Carnival!

What do I mean by that? OK …because they create cohesion and consistency like a design system does

image: Mannequin wearing Bauta mask adorned with navy Tabarro

So when I presented at ReactJSday Verona 2019, I visited Venice afterwards and was fascinated by the masks. I loved their beauty, their variety yet the way they created a sense of consistency.

image: Mannequin wearing plague doctor mask in muted purple costume

Similar to a design system. They provide a create a uniform look and a consistent set of actions for your team.

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

And… they are everywhere.

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

Everywhere you look in Venice.

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

In the shops.

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

In the souvenirs.

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

image: a person wearing a Jester mask smiling

And the masks have a rich history of providing a way for a person to blend into a situation…

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

…whether it be gender, identity, or social class

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

…Like the Baùta mask

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

The Moretta

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

The Gagna

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

The Harlequin

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

And the masks have a rich history of providing a way for a person to blend into a situation…

Wearing these masks gives you access to the best parties — like this Venetian masquerade parade on the Grand Canal during the 2019 Carnival.

Photo by Manuel Silvestri / Reuters

Why accessible components?

Now, why accessible design system components?

Our users are diverse

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

Which sends a poor message to our users.

I have a whole talk on accessibility-flavored react components make your design system delicious. This talk will cover a different aspect of accessibility. In fact, that talk was presented at reactJSday in 2019, and I’ll be using some of the primitive and modified components in this talk.

I’ll share those resources, though, so you can have all the notes.

One pattern, many uses

Now there’s one pattern that if built correctly, can have many uses in your design system. It’s called the Disclosure Widget.

Primitive masks

You can think of it as one of the more Primitive masks in your design system.

Anatomy of a disclosure widget

The anatomy of a disclosure widget is fairly straightforward.

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

A disclosure widget is a button that opens and closes a sibling container And returns focus to the button.

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.

<button />

Let’s begin building it with the JSX button. Why?

<button>

Under the hood, a JSX button renders as an HTML button, which already supports mouse clicks, spacebar, and enter keypresses for us. So we’ll use that as a basis for our pattern.

ARIA support

And… we’ll use ARIA attributes to let screen readers know when the container is being opened and closed — or in the case of if you have many widgets on the page — which container is being opened or closed.

First let’s add ARIA support to the button

The three ARIA attributes we’re adding to this component are aria-expanded, which conveys whether the widget is expanded or not

[jsx code]

aria-expanded={ariaExpanded}

aria-haspopup to tell assistive technology if there is an associated popup with the button

[jsx code]

aria-haspopup={ariaHasPopup}

And aria-label for cases where we need a more accurate button label

[jsx code]

aria-label={ariaLabel}

State & click handlers

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

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

[jsx code]

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

Or optionally, we could pass in a defaultExpanded prop, which should be a boolean.

[jsx code]

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

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

[jsx code]

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

Pass the toggleOpen function into the Button’s onClick synthetic event

[jsx code]

onClick={toggleOpen}

Pass the isOpen value into the Button’s ariaExpanded prop

[jsx code]

aria-expanded={isOpen}

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>

Focus management

Focus management. Let’s set our widget up to listen for ESC key presses and return focus to the button when the widget closes

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

[jsx code]

const buttonRef = useRef(null);

Then we’ll make an onKeyUpHandler function, which listens whether ESC key is pressed while the widget state isOpen.

And if the state isOpen, 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(); } };

Next, we pass in the onKeyUpHandler function to the onKeyUp synthetic event on the DisclosureWidget’s wrapper div, and 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

[jsx code]

return (

<div onKeyUp={onKeyUpHandler} className=”dwWrapper”> <Button ariaExpanded={isOpen} ariaHasPopup={true} ariaLabel={buttonAriaLabel} buttonRef={buttonRef} buttonClasses={buttonClassNames} icon={buttonIcon} iconOnlyBtn={iconOnlyBtn} onClick={toggleOpen} size={buttonSize} > {buttonText} </Button> <div> {isOpen && children} </div>

CSS focus states

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

Since browsers don’t give a consistent look for focus states, we need to 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 that is 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); }

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); }

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

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

[jsx code]

const contentRef = useRef(null);

Pass in the contentRef to the DisclosureWidget container div’s ref prop.

[jsx code]

<div ref={contentRef}>

Create 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); };

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); }

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

[jsx code]

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

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); };

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

[jsx code]

[isOpen]

Many pretty masks Now we can start using this pattern to making many pretty masks… so let’s make a toggle tip.

Toggle tips

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

Toggletips

Toggletips contain interactive content,

Use the aria-popup attribute

Are supported by touch devices, non-mouse pointers, and eye trackers, where tooltips are not

Toggle tips

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

Toggle tips

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

Many pretty masks

Toggle tip with icon button (add aria-label support)

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

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.

image: a heart and star icon, each labeled as "favorite"

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… that’s my recommendation.

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

[jsx code]

return ( <button {…props} aria-expanded={ariaExpanded} aria-haspopup={ariaHasPopup} aria-label={ariaLabel} className={clsx(styles.root, styles[size], buttonClasses)} disabled={disabled} onClick={onClick} ref={buttonRef} type={type}

<span className={styles.btnContentWrap}> 
  {children} 
  {icon && ( 
    <FontAwesomeIcon 
      icon={icon} 
      className={iconOnlyBtn ? iconBtnClasses : iconClasses} 
      size={size !== ButtonSizes.small ? "lg" : "sm"} 
    /> 
  )} 
</span>
</button> );

The FontAwesomeIcon React component. 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”} /> )}

We can pair an icon with visible button text

[jsx code]

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

And we can pair an icon with an aria-label in an icon-only button to support screen readers

[jsx code]

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

{buttonText} </Button>

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>

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> ); }

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

[jsx code]

aria-label=“Fruit trivia”

buttonIcon=”apple-alt”

iconOnlyBtn={true}

Then, we’ll add some styling to the button

[jsx code]

buttonClasses={styles.toggleTipButton)}

iconOnlyBtnClasses={styles.btnIcon}

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>

Toggle tip

Boom. ToggleTip

ToggleSearch

Let’s make a Disclosure with an embedded search form

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> ); }

…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 action onSubmit

[jsx code]

<DisclosureWidget buttonText=”Find a fruit” buttonIcon=”search”

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

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]

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

Targeted focus management

An embedded search widget is the perfect use case for some enhanced UX in the form of targeted focus management. In other words, when we open the widget, we can send focus right to the search input.

In our ToggleSearch component we’re going to use React’s useRef hook to create a DOM reference to our input. This will allow us to do some targeted focus management. First we’ll create a variable called firstItemRef, and assign it a value of useRef(null)

[jsx code]

const firstItemRef = useRef(null);

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>

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 to prevent component re-renders

[jsx code]

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

Embedded search widget

And now we have an embedded search widget.

Menu lists

Demo

Codesandbox demo of various Disclosure patterns, showing the dangers of what happens when adding too many features in a Disclosure Widget begins to break the rules of ARIA.

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

Whoa Nellie… Even though the one pattern can have many uses, it’s important to keep your Disclosure Widget uses separate. Otherwise, you begin to make unwise decisions… like…

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

Creating dishes that include jello and shellfish?

When your masks get too complex, like we did in our Codesandbox example, you break the rules of ARIA and then things can go wrong very quickly.

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

So… Don’t mix the masks. You may think your magical mask will give you access to that very exclusive Carnival masquerade, but instead you may end up at a completely different Carnival…

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

…like this one. The Viareggio Carnival with some extremely large floats…

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

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

Wrap-up

So to wrap up…

Design-systems are a Carnival!

Design Systems are a Carnival!

Our users are diverse

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

Don’t mix your masks

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

Or you’ll make bad choices

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

you may end up at a completely different Carnival…

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

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

…and a completely different experience than you wanted.

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.

Thank you.

https://noti.st/resource11/

Slide deck posted after the talk

@resource11 Twitter | Instagram | GitHub