DEV Community

Cover image for Building an Accessible Reveal-Card
Andrew Bone
Andrew Bone

Posted on

Building an Accessible Reveal-Card

Recently, I found myself craving to make something interesting. So I sat down and scrolled through dribbble to see if there was anything on there that I'd like to make. I came across a card that could be swiped to reveal some action buttons and set to work.

As you've, no doubt, gathered I'll be building a reveal card, I have no idea if they have an official name, in this post but I must preface this with, I'm not an A11y expert I like to think I have some idea what I'm doing but I could well have got some of this wrong. If you notice something wrong or want to ask questions about why I've done certain things feel free to leave a comment and I'll get back to you.

This is what I'll be making. It works with mouse, touch, keyboard and, I hope, screen readers read from it correctly too.

animation of reveal-card

The structure

For the structure, which we will later describe in HTML, we'll need a base that can be tabbed to, a layer for options, or buttons in our case, and the info card that sits on top of the options obscuring them until we slide them out of the way.

For a11y think the base should also have aria-expanded to indicate the section can be expanded also the buttons should have aria-hidden on them or a wrapper to prevent screen readers from reading them.

I think something like that matches my description but really doesn't look great as of yet. Which neatly leads us on to styling.

The look

For the look I'll be sticking to a quite material design, which means we'll use open sans fonts, buttons are transparent circles that become translucent on mouse over and we have a box shadow on the while thing. Quite simple when you explain it like that really isn't it. Here's the SCSS:

Whilst this now looks passable there are a few things to consider, the card is white but behind it needs colour too and we really should let potential users pick from a few colours, the default outline is ok but it different across browsers and doesn't really look that great and finally other than the cursor changing there's no real indication to the user that the panel slides.

Adding variants

You may have noticed in the above SCSS I was using BEM which is just a style guide. In BEM when you add a variant you use -- followed by the variant name. So let's add success, info and warn as our three colour variants.

Because we're using SCSS we can easily just add the variants to our material-slide styles like so.

.material-slide {
  position: relative;
  margin: 0.8em 0;
  border-radius: 4px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
  overflow: hidden;

  &--success {
    background-color: #1B5E20;
    color: #fefefe;
  }

  &--info {
    background-color: #0D47A1;
    color: #fefefe;
  }

  &--warn {
    background-color: #B71C1C;
    color: #fefefe;
  }
}
Enter fullscreen mode Exit fullscreen mode

I've stated the background colour and the text colour so if someone wants to add a new variant to the code they can follow the pattern and add a better contrast colour if they need one.

Nice outline

Now this is quite an interesting topic lots of A11y purest say the default outline should be left alone and for a long time I agreed with that but over the years I've considered alternatives and I think as long as you let the user know they have focus of your element it's ok to be a little adventurous.

The method I've been using for a while now is to use a box shadow in an after, though in this case I've had to use a div, which has opacity 0 until the element is focus-visible.

Again we add this to the material-slide styles.

// inside .material-slide
& .material-slide__outline {
  content: '';
  z-index: 2;
  border-radius: 4px;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px #fefefe, inset 0 0 0 3px #01579B, inset 0 0 0 4px #fefefe;
  opacity: 0;
}

// inside .material-slide__options
&:focus-visible {
  outline: none;

  & ~ .material-slide__outline {
    opacity: 1;
  }
}
Enter fullscreen mode Exit fullscreen mode

The outline is made of three lines a two white ones sandwiching a blue one, this helps with keeping the outline noticeable on top of different colours.

outline example

Gesture hints

Finally for the styles I don't think it's obvious when you see a card that it can be dragged even if you cursor changes to 'grab'. I had a think about this for a while and decided that if someone hovers over the card we should move it over by 5px just so show that the card moves.

To do this I simply added the transform to hover inside material-slide__info.

&:hover:not(.material-slide__options--user-control) {
  transform: translatex(-5px);
}
Enter fullscreen mode Exit fullscreen mode

You'll also notice there is material-slide__options--user-control this is a class we will be adding with JS so let's move on to that now.

The functionality

We've made it to the final part adding some JS to make the whole thing function. Mostly this will be event listeners with a little bit of state management. For the listeners we'll want to listen for keyboard events, mouse events and touch events.

State management

For this I will just have an object which contains all the variables I need, then I will update and check against that object as I need to.

const state = {
  isActive: false,
  isOpen: false,
  isOpenLast: false,
  startPos: null,
  currentPos: null,
}
Enter fullscreen mode Exit fullscreen mode

Event listeners

I'll need to listen for mouse down, move, leave and up to keep track of how far the card has been dragged. I've also decided to add double click to open an close fortunately there is a event for that. For keyboard support I'll just need to listen of key up and for touch I'll listen for touch start, move, end and cancel which are basically the same as mouse but slightly different (because of multitouch).

Sign off

Well there we have it we've made a cool little sliding panel that we can hide buttons behind. We've made it so a keyboard user can use it and so that a screen reader can read it.

I was thinking I might do another part to this series where I convert this to a React component and put it on Git/NPM, would there be any interest for that?

Thank you for reading to the end I hope you got something from this and as I said at the top of the article feel free to leave any comments or come shout at me over on twitter.

Thanks again 🧠🧙‍♂️❤️🦄🧠🦄❤️🧠🧙‍♂️🧠🧙‍♂️

Top comments (11)

Collapse
 
grahamthedev profile image
GrahamTheDev • Edited

Unfortunately this pattern won't work (make that doesn't work, I did give it a quick try) properly with a screen reader.

It breaks the ability to open the buttons once a screen reader is involved due to how the click handler behaves.

Also you will never get the "expanded" state announced as it is not valid on a <div>, you could possibly remedy this with role="button" on the <div> but then you have a big issue with nested buttons.

In fact nested interactive elements is something that should be avoided as it can cause havoc with click handlers once a screen reader gets involved.

The other issue is that aria-hidden=true will not hide focusable / interactive elements.

As such when you focus the top panel in a screen reader it still reads out the two buttons (on number 2). You can fix this by adding tabindex="-1" to the buttons when the front section is closed as this removes the items from the focus order (or just use display: none on the buttons until such time as the front cover is moved slightly, which would be preferable).

One last minor point, visible text rather than a title would be great on the buttons for people who can't associate abstract iconography with actions.

How to fix it?

One suggestion on how to fix these issues would be to restructure the HTML so it looks more like an accordion (in the HTML I mean) and then use absolute positioning or a similar technique to position the draggable top section on top of the button holder.

So your button (as you could now make the front card an actual <button>) would open the hidden section which is a sibling of the button.

It might take a little bit of JS trickery to handle touch start and (if you think a mouse slide action is better than a click) the mouse position movement but for screen readers and keyboard only users the behaviour would be much better.

Sorry I can't find any quick fixes with this one 😞 but the nested structure is where the issue lies.

Despite all of that I love the design, and obviously it is still very much worthy of a ❤ and a 🦄! 😁

p.s. I gave an answer on Stack Overflow that you could use the principle from that was about a button on top of another button where they appeared to be nested. Not sure if it is useful or not but I thought I would include it just in case.

Preword

Don't nest interactive elements

There is a reason that it isn't valid HTML to nest buttons or hyperlinks, it causes nightmares for knowing which action should be performed on a click (for a start) and for assistive technology this makes things even worse as it can confuse the accessibility…

Collapse
 
link2twenty profile image
Andrew Bone

I had hoped you'd leave a comment, thank you!

I've update my structure slightly and now the screen reader is reading out the collapsed state correctly. 😁

Collapse
 
demkantor profile image
demkantor

I only got a chance to view on my phone so couldn't fully test accessibility, I'm no expert either but have worked with accessibility agencies on some projects.
Just some quick notes, you don't want the title attribute and the aria-label at the same time, this can confuse some screen readers. Also I wouldn't suggest an h4 tag, assuming this is to be used on page along with other components it's unlikely that it would line up with an h4, if these arent to be headings I would consider a different tag, comes to mind but maybe there is a better choice yet
Glad to see you taking the initiative to test your skills while making accessibility a focus, not many bother to do much here so great job!

Thread Thread
 
link2twenty profile image
Andrew Bone

I've updated the h4 to another styled span. I can't see anything that says not to use a title though, it appears most screen readers ignore titles by default, and others don't support it at all.

Collapse
 
grahamthedev profile image
GrahamTheDev • Edited

Not many people hope I come along and write a massive list of things to tackle 😜🤣

At a glance on my phone it looks way better, if I get chance tomorrow I will have a look on PC!

If you get the pattern right I’m stealing it as I like the design so it isn’t anything other than selfishness 😉

Collapse
 
wparad profile image
Warren Parad

This is UX so bad it is worthy of being included in one of Google's web apps.

Collapse
 
link2twenty profile image
Andrew Bone

Oh, what's wrong with the UX?

Collapse
 
wparad profile image
Warren Parad
  • A user would never know that there is functionality hidden behind the movement
  • There is no cursor
  • Requires double click to open/close. No one is going to think about that.
  • It isn't clear what the colors means, colors should indicate something
  • The UI display of the text is horrific when the card is opened
  • Cards are frequently bad by design, they often have bad css built in, preventing them being used.
  • Doesn't offer anything of value on mobile, it actually detracts. If I know I need to slide the card, then the slide should be the action, not open more buttons...

That's only the UX, I didn't take a close look at the code, but a quick look reveals the implementation uses events to change classes/css, which is really bad.

Thread Thread
 
link2twenty profile image
Andrew Bone

Ok, I'm not sure I agree with any of those points to be honest 😅

  • there is a hint when you mouse over it but if it's included in a site you'd expect the site to explain the expectation to the user
  • there is a grab cursor because you grab the card (again a hint to the user it can be dragged)
  • It does not require a double click you just can use it if you like
  • the colours are arbitrary but the colours I've used are used in many UI and have understood meanings
  • the text is partially hidden as it is no longer the focus, if you wanna read it again feel free to close the panel again
  • cards are just divs with rounded corners and a box shadow, I have no idea how that means it has bad CSS built in
  • it can offer more than one option perhaps this card wouldn't be used if there was only one option (or perhaps it can be used as a warning to make a user think before they perform an action)

Though I do want to ask about why you say using "events to change classes/css" is "really bad"? It's the first time I've heard someone say this is bad.

Thread Thread
 
wparad profile image
Warren Parad • Edited

there is a hint when you mouse over it but if it's included in a site you'd expect the site to explain the expectation to the user

How? If you have to make excuses for your libraries in your UI, you picked the wrong library.

there is a grab cursor because you grab the card (again a hint to the user it can be dragged)

That doesn't show on my screen.

It does not require a double click you just can use it if you like

Your example requires a double click.

the colours are arbitrary but the colours I've used are used in many UI and have understood meanings

They don't have any meaning to a user that is using the card nor the developer selecting which colors they should be. Actually it's worse, the color and display is almost 100% the toast display from bootstrap which indicates the "level" of the message.

the text is partially hidden as it is no longer the focus, if you wanna read it again feel free to close the panel again

That's irrelevant and doesn't make it good UI.

cards are just divs with rounded corners and a box shadow, I have no idea how that means it has bad CSS built in

Cards are frequently discouraged in most frameworks, UX designs, so requiring their use is frequently problematic.

Though I do want to ask about why you say using "events to change classes/css" is "really bad"? It's the first time I've heard someone say this is bad.

Writing JS to do something that css can do natively is an indication of lack of css knowledge. It's also way slower and requires management of unnecessary JS resources which provides another area where it's easy to get it wrong.

Hope that helps.

Thread Thread
 
link2twenty profile image
Andrew Bone

Which browser are you using? The cursor and drag work fine for me on mobile and on desktop in chrome, Firefox and edge?

What am I doing in JS that CSS can do natively?