I want to replace some third party generated content using pure CSS. There are various techniques covered by other questions but the basic idea is hide the exiting content and add new content with pseudo selectors.
I'm interested in whether this is an acceptable thing to do from an accessibility point of view. Would a screen reader see both bits of text or only the original? Should the CSS be wrapped in #media screen {} to avoid both being used?
In my situation I can only use CSS. I can't edit the HTML or use any JavaScript. Is there any way to present only the replacement text to a screen reader?
.third-party-content {
font-size:0;
}
.third-party-content:before {
content:"lovely replacement";
font-size:initial;
}
<div class="third-party-content">unwanted garbage</div>
Don't use CSS generated text to convey meaningful information at all.
Screen readers don't behave the same. Some of them always read it, some of them never, and some of them sometimes do or don't depending on other properties.
For example, NVDA reads CSS generated text most of the time; Jaws rarely does (if ever it does sometimes).
Anyway, according to the base separation principle: content/structure=HTML, behavior/action=JavaScript, appearance=CSS, it isn't semantically correct to let CSS generate meaningful text.
It should be done in HTML. In fact the CSS Content propertiy should never be used for something else than text icons and other pure visual things that don't really matter for general understanding if they are skipped / not shown / not read.
Changing the font-size to 0 just changes the visual appearance of the text but does not hide it from a screen reader. A screen reader does not care what size the font is. It's just text that will be read.
When a screen reader reads text, it's called the "accessible name". The accessible name is computed according to these rules - https://www.w3.org/TR/accname-1.1/#step2. In particular, see step 2.F.ii
"ii. Check for CSS generated textual content associated with the current node and include it in the accumulated text. The CSS :before and :after pseudo elements can provide textual content for elements that have a content model."
One way to hide text from a screen reader is to use the aria-hidden attribute but that would require changing the HTML, which you stated you can't do. Text is also hidden using standard display:none, but that would obviously hide your :before text too.
Using CSS tricks to visually hide text, such as clip, position, width, etc, again just visually hides the text but does not hide it from a screen reader.
I'm not a CSS expert but building on your attempted solution of font-size, I added visibility:hidden and it seemed to work. I know hidden just makes the text invisible but still takes up space on the screen, but when combined with your font-size:0 (and inherit) it prevents the "unwanted garbage" from taking up any space so if you had an element adjacent to your original, you wouldn't have a big gap between the two.
visibility:hidden does hide the element from the screen reader.
.third-party-content {
font-size:0;
visibility:hidden;
}
.third-party-content:before {
content:"lovely replacement";
font-size:initial;
visibility:initial;
}
You cannot use display:none in a similar solution because as soon as the parent is hidden, the :before is hidden too.
Update: This solution works with NVDA (screen reader) on firefox and VoiceOver on iOS. (Chrome with screen readers is typically not tested because Chrome has sporadic support for screen readers.) It does not work with JAWS on Internet Explorer but IE seems to have two problems. The first is that your font-size:initial does not work on the :before. I had to set it to font-size:16px. Second is that IE does not correctly compute the text to be read as noted in step 2.F.ii above. IE is not honoring the content property so "unwanted garbage" is still read (and "lovely replacement" is not read). Since step 2.F.ii is a W3 spec, I'd say IE has a bug.
If content were correctly read by IE, then there might still be a problem. Since font-size:inherit doesn't work in IE, visibility:inherit might not work either. (I tried a simple case and it didn't work. Setting visibility:visible on :before also didn't work on IE.)
Related
I am trying to optimize a few components for screen readers, however Android Talkback proves to be a challenge....
Here is a very simplified example for the code:
<div class="wrapper">
<form>
<span role="presentation" aria-hidden="true">
This should not be read by Talkback
</span>
<input aria-label="This should be read by Talkback" />
</form>
</div>
The text inside the span is updated dynamically, and is positioned absolutely over the input - just to appear like an animated placeholder, without actually being read by screen readers. That is what the aria-label is for. However, TalkBack still seems to recognize the span - so it reads the content of the aria-label first, then continues reading the text in the span... role "presentation" or role "none" did not prevent this, neither did moving the text even further from the input. (For example, outside the form). Is there any way to prevent this?
The role attribute only changes the type of element that Talkback and other screen readers announce. Setting it to presentation or none just removes the semantic type of element. A <span> does not have a native role by default so it's essentially presentation/none implicitly and won't have any effect.
aria-hidden is the key. It will hide the element from the screen reader. (CSS display:none and visibility:hidden will also hide an element from the screen reader but it also makes the element invisible to sighted users too.)
Your code example should work just fine with Talkback. However, you mentioned that you dynamically change the contents of the <span>. That's not a problem but is there a chance that when you updated the text, the aria-hidden got removed?
I have used aria-hidden on Android without any trouble.
The solution in my example was already enough to fix the issue.
aria-hidden prevents the span being focusable, and if the span is located before (and not after), TalkBack will not interpret the text as being part of the input.
I have an audio-element in my HTML whose only purpose is to make an announcement to blind users via a Screen Reader. It's a DIV, but it's invisible to regular users.
The way to announce something is by creating an element with role=alert (no other way to do it, there's no JS function to directly "speak" to a reader, for example):
<!-- This element can be dynamically added OR shown (via JS) to make a Screen Reader announcement -->
<div role="alert">This will be announced to Screen Readers.</div>
However, I can't have this "audio assistant" element be visible to regular users.
1) Can't use display: none; -> the Screen Reader won't pick it up
2) Can't use visibility: hidden; -> the Screen Reader won't pick it up
3) Can't use opacity: 0; -> because space is taken up, layout must be exactly the same
I found this solution:
https://stackoverflow.com/a/25339638/1005607
div {
position: absolute;
left: -999em;
}
This works great, it solves my problem. But it's a bit of a hack. I wanted to ask: Is there a better, more standard way to solve this problem?
It's a common practice to use CSS to visually hide an element but allow it to be discoverable by screen reader users. It's not necessarily considered a "hack".
There are more CSS properties needed than what you tried. See What is sr-only in Bootstrap 3? for details.
Also, you can search for the "visually-hidden" class.
Both sr-only and visually-hidden are common names used to name the class that visually hides elements.
Also, your understanding of role="alert" isn't quite accurate.
The way to announce something is by creating an element with role=alert (no other way to do it, there's no JS function to directly "speak" to a reader, for example):
role="alert" has an implicit aria-live="assertive". Elements with aria-live will announce changes to that element to screen readers. It will not automatically announce that element. For example,
<div id="foo" aria-live="polite"></div>
<button onclick="document.getElementById("foo").innerHTML = 'hello'">update</button>
When I click on the button, text will be injected into the <div> and the new text will be announced.
In general, you want to use aria-live="polite" and not aria-live="assertive". When you use role="alert", you get aria-live="assertive".
So if your page is updating its contents, then using aria-live is the right thing to do. But it does not cause something to be announced just because your page loaded.
The screen reader user has many ways to navigate a website using quick navigation keys defined in the screen reader (such as H to go to the next heading [h1, h2, h3, etc], or T to go to the next table, or L to go to the next list, etc.), provided your HTML is using semantic elements (such as <h1>, <table>, <ul>, etc). If you have text that is hidden to sighted users, then that text can be found by screen reader users without you forcing it to be read automatically.
I'm not sure if this is a "better" way, but this will hide it in place.
div {
overflow:hidden;
height:0;
width:0;
}
I'm developing a desktop software application which looks for errors in web pages and can highlight them in the browser. Highlighted areas are somewhat arbitrary. They could be one word in a p tag, an entire a tag or an img.
In the past I've done this by rewriting the html and adding styled span tags around the highlighted area. The downside is that quite often the highlights can be obscured. For example where in image is in a div exactly its size with no overflow, any applied border, background etc. will be obscured.
What's the best way to approach this? Are there any good examples of this being done in popular software / webapps?
Limitations: I can't use JS (files are local and browsers often block this). I can however user the latest standards. The output doesn't have to validate, as long as it works on common modern browsers.
Since background colors and borders can't be used, I think you'll need to place something on top of the offending element or text. Perhaps you can use an absolute or fixed position <div> element with a partially transparent background.
Of course, this could get tricky with getting the coordinates. But you might be able to use the same thing you used to do with the span and add some dummy elements within it to trick it into thinking that 0,0 is right where your span element is.
image
question
Is there any way I can shift the "Vancouver, BC" text down a little bit so it aligns better with "CITIES" and "CHANGE"?
I know input elements are finicky to style across browsers; it doesn't have to be perfect but if it could work in at least some browsers, that would be great.
fiddle
Customizing a browser's default rendering of some form elements--especially dropdown select lists--is generally not recommended for a few reasons:
Your customization options are naturally limited
It's extremely difficult to get the form element to look the way you want--as you noted, they're finicky
Even if you can get the input to look the way you want in one browser, its almost impossible to do it cross-browser (I realize you said that's not a concern, but still, it's worth knowing)
From a usability perspective, customizing the default rendering of a form element almost always reduces the usability of the form somewhat
NOW, all of that being said, if you want more control over the visual styles of your form elements, I'd recommend using a jQuery plugin. Typically these work by hiding the form elements and replacing them with easily-customizable CSS--usually regular ol' unordered-lists--and then sending the user interactions with the unordered lists to the hidden forms. Here's one you could check out to get started.
I would adjust the font-size or the height to fix the alignment issue:
#city_picker {
height: 26px;
font: 19px Arial,Helvetica,sans-serif;
}
That would make the text about the same size as the select box, and force it to appear aligned. Otherwise it seems rather difficult if not impossible to adjust styles of the option elements across browsers.
i'm solving a problem to make select inputs look the same in all browsers (Chrome and Safari on Mac renders them differently) how to do that ?
The ONLY way to make them look the same right now would be to hide the original inputs, and replace them with appropriately styled html equivalents (of god forbig Flash objects), which would act as proxies, passing the functionality over to the hidden inputs.
That may be automated with JavaScript. But that would be WRONG. You are not supposed to force a different look on to OS styled elements of the webpage. It conflicts with a lot of usability and accessibility practices.
So, the only way is to make your design flexible enough to support differently looking control elements on a web page, and also use different stylesets for different browsers, to ease the adjustment of the styles (at the moment there are no inputs that would look and act the same on all browsers with the same style rules applied).
Unfortunately, life just kinda sucks on this one. Just wait till you need to style a file input...now that's some fun!
if you dont mind using js you can simply design your own look (a jpg img it can even be the same img as the original select element or if you wish you can model parts of it in css)
Then place a div on top of that image that div will contain the text which select element would usually contain
<div id="selectTxt" >
then set another div on top of that with the select element inside it.
<div id="transparentSelect" class="transparent">
<select id="selectCar" name="selectCar">
<option>Volvo</option>
<option>Saab</option>
<option>Mercedes</option>
<option>Audi</option>
</select>
</div>
Now the trick is to set the select element opacity to zero
you can do this by adding by adding a class transparent
and then applying the class to the div
.transparent
{
filter:alpha(opacity=0);
-moz-opacity: 0;
opacity: 0;
}
now the element is hidden but when you click on it the list will still show up.
So the list will always look like the default list in the browser
now use js to extract the select value every time you click on the select
and set the inner html of selectTxt div to its value.
This way you get the text of the select on top of an image you want
you can make the image animated with the hover effect in css or with js
I also make a select that looks the same in all browsers but it doesnt work when you click directly on the arrow...
so its an inferior version but if you wish to look at it here it is
http://jsfiddle.net/fiddlerOnDaRoof/LM73V/
it also lacks the arrow image but you can print screen that from your browser
good luck
You should apply a CSS to reset the styles (not just for the inputs, this is a highly recommended practice for all element so that your page looks almost the same in all browsers) there are many, just google a little, for example this one, and then apply your desired styles (border color and width, background, etc...) take a look at this tutorial on how to style form elements