-
-
Notifications
You must be signed in to change notification settings - Fork 370
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add basic ARIA mark-up #12
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good PR, thanks.
But I think that we can make it even better.
@@ -306,6 +307,8 @@ export default class Comment extends Component { | |||
<span className={b('comment__score', {}, { view: o.score.view })}> | |||
<span | |||
className={b('comment__vote', {}, { type: 'up', selected: scoreIncreased, disabled: isGuest || isCurrentUser })} | |||
role="button" | |||
aria-disabled={isGuest || isCurrentUser ? 'true' : 'false'} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use just isGuest || isCurrentUser
as a value here, can't you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about it. However, in all specifications I always saw aria-something="true"
or "false"
(note the quotes). So would it be OK to leave it as you suggest? If so, I'll be glad to remove those unnecessary boolean values :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you leave just a boolean value in attribute, it will be processed to string: "true"
or "false"
. Check this demo: https://codepen.io/igoradamenko/pen/QrxvGe?editors=0010
So, isGuest
and isCurrentUser
are always boolean, and I think you can remove unnecessary ternary operator here :-)
@@ -286,6 +286,7 @@ export default class Comment extends Component { | |||
<a | |||
className="comment__link-to-parent" | |||
href={`${o.locator.url}#${COMMENT_NODE_CLASSNAME_PREFIX}${o.pid}`} | |||
aria-label="Parent comment" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Go to parent comment
? And maybe it's better to add title
here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Did that just for conciseness, our screen readers tend to say quite a lot of things at once :).
@@ -244,7 +244,7 @@ export default class Comment extends Component { | |||
|
|||
if (mods.view === 'preview') { | |||
return ( | |||
<div className={b('comment', props, defaultMods)}> | |||
<div className={b('comment', props, defaultMods)} role="listitem article" aria-level={mods.level}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of role="article"
I think you should change div
with article
.
But don't forget to add display: block
to .comment
in this case.
@@ -46,7 +46,7 @@ export default class AuthPanel extends Component { | |||
<strong className="auth-panel__username" onClick={this.toggleUserId}>{user.name}</strong> | |||
{isUserIdVisible && <span className="auth-panel__user-id"> ({user.id})</span>}. | |||
{' '} | |||
<span className="auth-panel__pseudo-link" onClick={props.onSignOut}>Sign out?</span> | |||
<span className="auth-panel__pseudo-link" role="link" onClick={props.onSignOut}>Sign out?</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you add role="link"
you also should add tabIndex="0"
because if you don't it's senselessly, isn't it?
@@ -244,7 +244,7 @@ export default class Comment extends Component { | |||
|
|||
if (mods.view === 'preview') { | |||
return ( | |||
<div className={b('comment', props, defaultMods)}> | |||
<div className={b('comment', props, defaultMods)} role="listitem article" aria-level={mods.level}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, why do you think that listitem
must be used here? Are you sure that it's OK to mix roles here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I've reread your first comment. As far as I understand, it's the experimental part what you were talking about.
What if we use article
only for Comment component, list
for .root__threads
in Root? And for Thread we can always use listitem
and list
only when it has some replies inside. So in this case we will have structure like this one:
list (.root__threads)
— listitem list (Thread with Threads inside)
— — article (Comment)
— — listitem (Thread without Threads inside)
— — — article (Comment)
— — listitem (Thread without Threads inside)
— — — article (Comment)
— listitem (Thread without Threads inside)
— — article (Comment)
I'm not an expert in a11y, but it sounds OK for me. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds perfect to me, thanks! And, it seems, this way we don't need to bother ourselves with manually setting aria-level
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yes, and I also need some help, sorry (I'm not a (P)React expert so far). How do we separate those threads with nested threads and without them in the thread
component?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, it's pretty simple.
Here we have Threads inside Thread: https://github.com/umputun/remark/blob/master/web/app/components/thread/thread.jsx#L33-L41
So, you can see that nested Threads generated from replies
array. You can use replies.length
as a condition for adding list
role. For example:
const roles = ['listitem'].concat(replies.length ? 'list' : []);
return (
<div className={b('thread', props)} role={roles}>
or even simpler:
return (
<div
className={b('thread', props)}
role={['listitem'].concat(replies.length ? 'list' : [])}
>
But I'm not sure should we add list
if child Threads are collapsed? If we shouldn't then conditional must be like this: (!collapsed && replies.length) ? 'list' : []
. I don't know what the usual a11y pattern for situation like this is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Usually if something is expanded or collapsed, we add aria-expanded
which is either true
or false
. BTW, where are the Expand/Collapse (pseudo-)links located?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it is: https://github.com/umputun/remark/blob/master/web/app/components/comment/comment.jsx#L343-L350
It's a bit messy, but we have it inside Comment in Thread, and when you click on it, Thread hides / shows nested Threads. But the expand/collapse button is inside Comment ¯\_(ツ)_/¯.
web/app/components/root/root.jsx
Outdated
@@ -223,7 +223,7 @@ export default class Root extends Component { | |||
|
|||
{ | |||
!!pinnedComments.length && ( | |||
<div className="root__pinned-comments"> | |||
<div className="root__pinned-comments" role="region" aria-label="Pinned Comments"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pinned comments
, please (second word is lowercased).
web/app/components/root/root.jsx
Outdated
@@ -268,13 +268,13 @@ export default class Root extends Component { | |||
|
|||
{ | |||
isBlockedVisible && ( | |||
<div className="root__main"> | |||
<div className="root__main" role="region" aria-label="Blocked Users"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blocked users
.
And may be it's better to move all this things to BlockedUsers component root node.
LGTM. @Menelion, have you finished with it? May I merge it? |
@igoradamenko, Yes, please. However, I still don't get why the CI is failing :). |
That's because we don't have CI for pull requests now. Somewhen we will — #14. But now — thank you for PR, it's awesome 🐨 |
I've added basic ARIA mark-up for links and buttons.
The only thing I'm doubtful about is how to organize threads and replies. I experimented a bit, but this functionality needs to be tested and might be reverted in future.
I also didn't mark up user name links that toggle user IDs: I might implement it in future, but it needs also a description, otherwise a blind user won't understand what the click on a user name does.