VoiceOver + Safari does not read aria-label as expected - safari

I am trying to build a Toggle button which has two states, Edit and Preview.
My demo is here:
https://codepen.io/yu-zhang/pen/rNmPyxG?editors=1111
<button aria-pressed="true"
id="speakerPower" class="switch">
<span aria-label="edit">edit</span>
</button>
button.switch {
margin: 0;
padding: 0;
width: 70px;
height: 26px;
border: 2px solid black;
display: inline-block;
margin-right: 0.25em;
line-height: 20px;
vertical-align: middle;
text-align: center;
font: 12px "Open Sans", "Arial", serif;
}
button.switch span {
padding: 0 4px;
pointer-events: none;
}
button[aria-pressed="true"] {
background: #262;
color: #eef;
}
button[aria-pressed="false"] {
color: #a;
}
label.switch {
font: 16px "Open Sans", "Arial", sans-serif;
line-height: 20px;
user-select: none;
vertical-align: middle;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
}
document.querySelectorAll("button").forEach(function(theSwitch) {
theSwitch.addEventListener("click", handleClickEvent, false);
});
function handleClickEvent(evt) {
let el = evt.target;
if (el.getAttribute("aria-pressed") == "true") {
el.setAttribute("aria-pressed", "false");
el.textContent= 'preview';
el.setAttribute("aria-label", "preview");
} else {
el.setAttribute("aria-pressed", "true");
el.textContent = 'edit';
el.setAttribute("aria-label", "edit");
}
}
But the strange thing is: VoiceOver + Chrome(92.0.4515) reads out the aria-labels while toggling but VoiceOver + Safari(14.1.2) does not read out the aria-labels.
Any ideas?

You are changing both the aria-pressed state and the aria-label property. It's important to know the difference between a state and a property, even though both begin with "aria".
When changing a state, you typically get an automatic announcement by the screen reader ("pressed/unpressed", "checked/unchecked", "expanded/collapsed") of the state change.
When you change a property, no such "free" announcement is made.
You'll see/hear differences depending which screen reader and which browser you're using. Sounds like you're getting the desired behavior in chrome but not safari. It's undefined which one is correct since changes in properties don't have to be announced.
To work around it, you can have a hidden aria-live="polite" region. In your click handler, just update the innerHTML of the live region with the new label and it'll be announced. I don't know if that will cause double announcements on chrome.
You'll also want to visually hide the live region so you can't see it but you can't make it display:none because then the aria-live won't be announced.
<div class="sr-only" aria-live="polite" id="foo"></div>
(See What is sr-only in Bootstrap 3? for info on the "sr-only" class.)
In your click handler, get the "foo" element and change its innerHTML to "preview" or "edit".

Related

styling npm vuejs-paginate component

I am using the npm package https://www.npmjs.com/package/vuejs-paginate
to handle pagination in a vuejs application.
I would like to style this pagination component.
My styling successfully sets the background of page number buttons yellow when the user hovers over them, but fails to set the background of the current page to green. Why?
Here is my component tag with the props.
<paginate
:pageCount="totalPages"
:click-handler="paginateCallback"
:prevText="'Prev'"
:nextText="'Next'"
:containerClass="'pagination'"
class="pagination"
v-model="pageNumber"
></paginate>
And here is the css...
.pagination a {
float: left;
padding: 8px 16px;
text-decoration: none;
border: 1px solid #ddd;
background-color: white;
}
.pagination a.active {
background-color: green;
}
.pagination a:hover:not(.active) {background-color: yellow;}
.pagination a:first-child {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.pagination a:last-child {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
By the way, in case it is relevant information, the application uses bootstrap-vue elsewhere.
Thanks to the first answer below, I was able to resolve this.
Here is the working css after adding the active-class prop to the component...
.pagination li {
float: left;
padding: 8px 16px;
text-decoration: none;
border: 1px solid #ddd;
color: white;
background-color: white;
font-size: 1em;
}
.pagination li.pagination-active {
background-color: green;
}
.pagination li:hover:not(.active) {background-color: yellow;}
Now, however, there is a border around the number of the active page button until the user clicks again anywhere on the page. How can we eliminate this border?
As the documentations says: there is an active class prop that you can set and style that class. see the props in the link above.
<paginate
:pageCount="totalPages"
:click-handler="paginateCallback"
:prevText="'Prev'"
:nextText="'Next'"
:active-class="myActiveBtn"
:containerClass="'pagination'"
class="pagination"
v-model="pageNumber"
></paginate>
style:
.myActiveBtn{
background-color: green;
}

Adding scroll down button to Shopify custom theme

I am customizing shopify motion theme. I added a scroll down svg on bottom of the slider. But having problem with scrolling down to the next section
I have added the scroll down button with that code;
div.hero__image-wrapper:after {
content: url({{ "icon-scroll-down.svg" | asset_url }}) ;
position: absolute;
display: block;
z-index: 34560;
bottom: 20px;
left: 48%;
font-size: 2em;
border-radius: 1em;
font-weight: bold;
border: 3px solid gray;
padding: 0.1em 0.1em 0;
animation-name: color_change;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
#keyframes color_change {
0% { color: gray; bottom:20px;}
10% { color: black; bottom:10px;}
20% { color: gray; bottom:20px;}
100%{ color: gray; bottom:20px;}
}
But currently it's only an icon. I need to make it scroll to down
I would do it with JS instead. Let's assume your button is an actual element instead of pseudo ::after:
<div class="scroll-down-button"></div>
.scroll-down-button {
// whatever style you prefer
}
Then the JS code would look like this:
(function() {
// Get the button element
var button = document.querySelector('.scroll-down-button');
// Get current section
var currentSection = button.closest('.shopify-section');
// Get next section (the very next element after current section container)
var nextSection = currentSection.nextSibling();
// Get the coordinates of the next section
var nextSectionCoordinates = nextSection.getBoundingClientRect();
// Get the top Y axis coordinates of next section
var nextSectionTop = nextSectionCoordinates.top;
// Scroll to the top of next section (0 means no scroll on X axis)
window.scrollTo(0, nextSectionTop);
})();
The above is not tested, so let me know if it doesn't work or you can console.log any values. You should get the idea though!

Bootstrap Vue Animate Dropdowns

I am using bootstrap vue and am trying to animate/transition the drop downs. This is proving to be fairly difficult as they do not use v-if or v-show so the transition will not work. Alternatively because the way the components work if you use v-if the drop down trigger will be hidden. I can't find anything online to bootstrap vue specifically on this but I feel this shouldn't be as tough as it has turned out to be. thanks for any help you can give
<div id="app">
<b-navbar type="dark" fixed>
<b-navbar-nav class="ml-auto">
<b-nav-item-dropdown text="Tools">
<b-dropdown-item to="/navItem1">Item 1</b-dropdown-item>
<b-dropdown-item to="/export"> Item 2</b-dropdown-item>
</b-nav-item-dropdown>
// This won't work as it hides the main dropdown trigger right form the start
<b-nav-item-dropdown text="Tools" v-if="toggleDropdown">
<b-dropdown-item to="/navItem1">Item 1</b-dropdown-item>
<b-dropdown-item to="/export"> Item 2</b-dropdown-item>
</b-nav-item-dropdown>
</b-navbar-nav>
</b-navbar>
</div>
<script>
export default {
name: 'nav',
data () {
return { toggleDropdown: false }
},
mounted: function () {
// I can listen for events here but I still can't trigger the transition
this.$root.$on('bv::dropdown::show', bvEvent => {
this.toggleDropdown = true
})
this.$root.$on('bv::dropdown::hide', bvEvent => {
this.toggleDropdown = false
})
}
}
</script>
<style lang="scss">
.navbar {
.dropdown-menu {
transform-origin: top;
transition: transform 10s ease-in-out;;
}
}
.dd-slide-enter,
.dd-slide-leave-to { transform: scaleY(0); }
</style>
It's pretty hard to achieve a clean slide-up/down animation because BootstrapVue uses display:none/block to hide/show the dropdown menu. What you can do it's manipulate the max-height of the element as explained here.
I added an 'animated' class to the parent element, for example your b-navbar to select which dropdown has to be animated. Then i removed display: none from the default status of the dropdown and hidden it setting its max-height and padding to 0 and its border to none. When you click the button the dropdown gets the class 'show'so you can give it a max-height different than 0, as explained in the answer i've linked to you, you have to set it higher than the actual height of the dropdown menu otherwise it gets cropped out.
.animated {
.dropdown-menu {
overflow: hidden;
display: block!important;
max-height: 0!important;
&:not(.show) {
padding: 0;
border: none;
}
&.show {
transition: max-height 300ms ease-in-out;
max-height: 500px!important; //this must have to be higher than the max height of the dropdown list
}
}
}
Just came across this same issue.
Ended up following with previous example, but this one works for both up/down transitions and doesn't mess with overflows in case you want to add triangles.
.dropdown-menu {
border: 1px solid #ebeef5;
box-shadow: 0 5px 25px 0 rgba(0, 0, 0, 0.25);
// Slide down transtion
display: block !important;
&:not(.show) {
padding: 0px;
border-width: 0px;
border-color: transparent;
box-shadow: none;
transition: padding 1.3s ease, border-width 1.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}
> li {
max-height: 0px;
overflow: hidden;
transition: max-height 0.3s ease;
}
&.show {
> li {
max-height: 100px;
}
}
// Add chevron to top
&[x-placement^="bottom"] {
&::before {
content:"";
position: absolute;
right: 11px;
top: -5px;
width: 0;
height: 0;
border-style: solid;
border-width: 0 5px 5px 5px;
border-color: transparent transparent #fff transparent;
z-index: 99999999;
}
}
// Add chevron to bottom
&[x-placement^="top"] {
&::after {
content:"";
position: absolute;
right: 11px;
bottom: -5px;
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #fff transparent transparent transparent;
z-index: 99999999;
}
}
}

Media query fails for only one property

I am unable to change padding for one element on mobile devices. The queries are working for several properties, but padding will not work (neither will line height if I try to use that). Basic styling in custom css is:
#topright {
font-size: 20px;
letter-spacing: 4px;
color: rgba(255,255,255,1.0);
padding-top: 8px !important;
padding-bottom: 8px !important;
font-weight: 200;
}
Media query for phone is
/* Custom, iPhone Retina */
#media only screen and (min-width : 320px) {
.header-2 .logo {
width: 250px;
}
.footer-widget ul li {
width: 100%;
}
.footer-widget ul {
padding-left: 0;
padding-right: 0;
}
div.vc_column-inner vc_custom_1476556729591 {
padding-left: 0;
padding-right: 0;
}
.footer-widget .textwidget p {
text-align: center;
}
#topright {
padding-top: 1px;
padding-bottom: 1px
}
}
The smaller padding number will not be applied. If I remove !important from main css, then the phone query gets applied to all devices. It's weird because all the other properties for the phone query are working fine.
From this helpful page on media queries, min-width: 320px means:
"If [device width] is greater than or equal to 320px, then do {...}"
In other words, the media query you think you created to target only iPhone will actually be firing for all devices which have a width of 320px or greater. Instead, I think you intended to use max-width
So use this CSS:
/* Custom, iPhone Retina */
#media only screen and (max-width : 320px) {
.header-2 .logo {
width: 250px;
}
...
#topright {
padding-top: 1px;
padding-bottom: 1px
}
}
And you should also remove the !important directives from your main.css file.

Less navbar a:active does not work

Gentle men (and likewise women),
I'm stuck with a nasty Less problem I can't figure out.
Here's the code:
.navbar-collapse {
background-color: #ff6600;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
margin-bottom: 5px;
}
.nav > li > a:link {
color: #fff;
font-weight: bold;
}
.nav > li > a:visited {
color: #ccc;
background: blue;
}
.nav > li > a:hover {
color: #000;
background: pink;
}
.nav > li > a:focus, .nav > li > a:active {
color: #000;
background: yellow;
}
.nav > li .current {
color: black!important;
background: skyblue!important;
}
I'm new to Less, my first (minor) problem is how to nest this code properly.
But the real problem is that a:active and a:focus do not work. Nothing whatsoever. a:link does, but not what it's supposed to do. I want the font-colour to be white, it shows up blue. Checking the generated code shows in line 1089, but weirdly enough Firebug shows that colour crossed out - it shouldn't work.
I want a:active and/or a:focus with font-colour #000, but that does not work.
a:active does show up but goes away in a split second.
I tried the .current class, as it shows up in Firebug, but that one doesn't work at all.
You can check the code out on http://www.test.dgdesk.com.
It's a Joomla site based on Joostrap, with Bootstrap 3
This is a css conflict.
The color from .sidebar-right's nav styles overrides the color from the general nav styles.
You can fix the issue by defining a more specific class for the nav you are updating, or by updating the sidebar styles directly.