Tweaking Text Level Styles, Reprised

In 2017 I wrote Tweaking Text Level Styles (terrible name in retrospect) and I made regular updates over the years. Stop reading it. Remove it from your bookmarks. Unlink it from your posts. Print it onto paper and then burn it.

The conclusions and techniques in that post were based on the state of the art when I wrote it. If you have been treating that post as the current ideal approach then I failed to convey that the post, and all its embedded demos and code samples, are really just a place to start your own testing.

So now I am being explicit — this post is a starting point. You need to take it from here.

I include the corresponding ARIA roles because sometimes people do terrible things they later regret. ARIA role does not bring any styles with them, so I add the browser defaults from their HTML counterparts.

Jump to the Wrap-up at the end of the post if you just want guidance on what to do.

Demo

Use this for your own testing (visit it directly). Or make a better one.

Testing kit:

The <mark> Element

Use <mark> to indicate a run of text as some form of reference. Get more detail from the WHATWG HTML spec for <mark>.

Default Styles

The browser default styles are fine. They lean on CSS keywords, so they’ll adapt for forced-colors mode. You probably should not change them unless you have a genuine contrast issue with your own styles.

If somebody has tricked you into using the ARIA mark role, then you will need to replicate the browser’s default styles:

[role="mark"] {
  background-color: Mark;
  color: MarkText;
}

Print Styles

Not every system can print (background) color and not every user wants to. You may want to add a custom print style. An outline is probably not too disruptive and it can scale with the text. I don’t set a text color because my testing showed it wasn’t necessary when the background color wasn’t being printed.

@media print {
  mark {
    border: 1pt dotted;
  }
}

If someone is still forcing you to use the ARIA role, blink twice for help and add a [role="mark"] selector. I won’t do it for you.

Screen Reader Output

This represents the spoken output from a few screen readers using default settings. You may experience different results depending on your screen reader voice, TTS engine, language pack, custom dictionary, settings, or if you use a Braille display.

NVDA / Firefox
Precedes with “highlighted”, follows with “out of highlighted”.
Applies for both read-all and virtual cursor
JAWS / Chrome
Precedes with “mark” in a raised pitch, follows with “end mark” in a raised pitch.
Applies for both read-all and virtual cursor
Narrator / Edge
No announcement, but precedes with a pause, follows with a pause.
Applies for both read-all and virtual cursor
VoiceOver / macOS / Safari
When using read-all, it ignores all text in the paragraph up to the marked-up text, then announces as the virtual cursor does.
With the virtual cursor, announces “highlighted” at the start, follows with a pause.
With the virtual cursor, when the text wraps it announces “highlighted” at the start of each new line.
Orca / Firefox
Precedes with “highlight start”, follows with “highlight end”.
Applies for both read-all and virtual cursor
TalkBack / Chrome
Pauses at the start of the node, follows the node with “highlight”.
Applies for both read-all and virtual cursor
VoiceOver / iPadOS / Safari
Nothing exposed using read-all.
Using the virtual cursor, follows with “highlighted”.

The <del> Element

Use <del> to indicate a run of text has been deleted or removed or stricken. Get more detail from the WHATWG HTML spec for <del>.

Default Styles

Both <del> and <s> are assigned the same style in browsers. It takes the color of the text, so contrast should never be an issue.

If somebody is holding your favorite mug hostage and making you use the ARIA deletion role, then you will need to replicate the browser’s default styles:

[role="deletion"] {
  text-decoration: line-through;
}

Enhanced Styles

The default thickness of that strike-through line might be too thin for your tastes. Before changing the color or position, instead try it a bit thicker.

del {
  text-decoration-thickness: .15em;  
}

If someone is still forcing you to use the ARIA role, signal for help by quietly tapping a Fibonacci sequence and add a [role="deletion"] selector. I won’t do it for you.

Screen Reader Output

This represents the spoken output from a few screen readers using default settings. You may experience different results depending on your screen reader voice, TTS engine, language pack, custom dictionary, settings, or if you use a Braille display.

NVDA / Firefox
Precedes with “deleted”, follows with nothing.
Applies for both read-all and virtual cursor
JAWS / Chrome
Precedes with “deletion” in a raised pitch, follows with “end deletion” in a raised pitch.
Applies for both read-all and virtual cursor
Narrator / Edge
No announcement, but precedes with a pause, follows with a pause.
Applies for both read-all and virtual cursor
VoiceOver / macOS / Safari
When using read-all, it ignores all text in the paragraph up to the marked-up text, then announces as the virtual cursor does.
With the virtual cursor, announces “deletion” at the start, follows with a pause.
With the virtual cursor, when the text wraps it announces “deletion” at the start of each new line.
Orca / Firefox
Precedes with “deletion start”, follows with “deletion end”.
Applies for both read-all and virtual cursor
TalkBack / Chrome
Pauses at the start of the node, follows the node with “deletion”.
Applies for both read-all and virtual cursor
VoiceOver / iPadOS / Safari
Nothing exposed using read-all.
Using the virtual cursor, opens with “deletion”.

The <ins> Element

Use <ins> to indicate a run of text has been inserted. Get more detail from the WHATWG HTML spec for <ins>.

Default Styles

The default style for <ins> is an underline. It takes the color of the text, so contrast should never be an issue.

If somebody is sneaking into your code at night and swapping it with the ARIA insertion role, then you will need to replicate the browser’s default styles:

[role="insertion"] {
  text-decoration: underline;
}

Enhanced Styles

Since underlines are generally reserved for links (except for fancy sites that are trying to fail WCAG), this could be confusing for users if the context isn’t clear. Instead of changing the color or position, try a style variation. Like wavy.

ins {
  text-decoration-style: wavy;
}

If someone is still forcing you to use the ARIA role, and the police won’t respond, add a [role="insertion"] selector. I won’t do it for you.

Screen Reader Output

This represents the spoken output from a few screen readers using default settings. You may experience different results depending on your screen reader voice, TTS engine, language pack, custom dictionary, settings, or if you use a Braille display.

NVDA / Firefox
Precedes with “inserted”, follows with nothing.
Applies for both read-all and virtual cursor
JAWS / Chrome
Precedes with “insertion” in a raised pitch, follows with “end insertion” in a raised pitch.
Applies for both read-all and virtual cursor
Narrator / Edge
No announcement, but precedes with a pause, follows with a pause.
Applies for both read-all and virtual cursor
VoiceOver / macOS / Safari
When using read-all, it ignores all text in the paragraph up to the marked-up text, then announces as the virtual cursor does.
With the virtual cursor, announces “insertion” at the start, follows with a pause.
With the virtual cursor, when the text wraps it announces “insertion” at the start of each new line.
Orca / Firefox
Precedes with “insertion start”, follows with “insertion end”.
Applies for both read-all and virtual cursor
TalkBack / Chrome
Pauses at the start of the node, follows the node with “insertion”.
Applies for both read-all and virtual cursor
VoiceOver / iPadOS / Safari
Nothing exposed using read-all.
Using the virtual cursor, opens with “insertion”.

The <s> Element

Use <s> to indicate a run of text is no longer accurate (but really this means stricken). Get more detail from the WHATWG HTML spec for <s>.

Default Styles

Both <del> and <s> are assigned the same style in browsers. It takes the color of the text, so contrast should never be an issue.

If for some reason you hate HTML and prefer ARIA (or, I dunno, React) and use the ARIA deletion role, then you will need to replicate the browser’s default styles:

[role="deletion"] {
  text-decoration: line-through;
}

Enhanced Styles

The default thickness of that strike-through line might be too thin for your tastes. Before changing the color or position, instead try it a bit thicker.

del {
  text-decoration-thickness: .15em;  
}

If someone is still forcing you to use the ARIA role, it’s too late for you, but before you expire add a [role="deletion"] selector. I won’t do it for you.

Screen Reader Output

This represents the spoken output from a few screen readers using default settings. You may experience different results depending on your screen reader voice, TTS engine, language pack, custom dictionary, settings, or if you use a Braille display.

NVDA / Firefox
Precedes with “deleted”, follows with nothing.
Applies for both read-all and virtual cursor
JAWS / Chrome
Precedes with “deletion” in a raised pitch, follows with “end deletion” in a raised pitch.
Applies for both read-all and virtual cursor
Narrator / Edge
No announcement, but precedes with a pause, follows with a pause.
Applies for both read-all and virtual cursor
VoiceOver / macOS / Safari
When using read-all, it ignores all text in the paragraph up to the marked-up text, then announces as the virtual cursor does.
With the virtual cursor, announces “deletion” at the start, follows with a pause.
With the virtual cursor, when the text wraps it announces “deletion” at the start of each new line.
Orca / Firefox
Precedes with “deletion start”, follows with “deletion end”.
Applies for both read-all and virtual cursor
TalkBack / Chrome
Pauses at the start of the node, follows the node with “deletion”.
Applies for both read-all and virtual cursor
VoiceOver / iPadOS / Safari
Nothing exposed using read-all.
Using the virtual cursor, opens with “deletion”.

Wrap-up

Here are the key takeaways:

Ok bye.

8 Comments

Reply

Please be aware: in some language, such as zh-hant-TW, the wavy underline is used to mark book titles. Please refer to https://language.moe.gov.tw/001/Upload/FILES/SITE_CONTENT/M0001/HAU/h12.htm (content in zh-hant-TW).

In response to Jedi. Reply

And that right there is exactly why I say to be mindful of language in the second bullet under Wrap-up. Thanks!

Reply

Some results from the previous VoiceOver versions:

macOS 14.6.1 (Safari 17.6): Same support and behavior with <mark>. No support for <del> or <ins>, however role="deletion" and role="insertion" are supported, with the same reading behavior as <mark>. No support for <s>.

iOS 17.7: Same support and behavior with <mark>, <ins> and <del>. No support for <s>.

James Edwards; . Permalink
In response to James Edwards. Reply

Thanks, SiblingPastry! For those reading along, James had helped validate some of my tests and, while running vaguely older kit, was able to gather other results. I refused to include it and asked him to leave it as a comment instead. Something something dark side, something something ego.

Reply

Glad to see improvements from the testing Steve Faulkner did in 2023.

Adding more VoiceOver results, Safari 18.4 on macOS 14.7.4 (Sonoma) is the same as macOS 15.4 (Sequoia). Safari 18 is available for macOS 15, 14, and 13 (Ventura) which run on Mac hardware from 2017 and newer (or older, with unofficial patches).

I made a table of the test results.

Curtis Wilcox; . Permalink
In response to Curtis Wilcox. Reply

Indeed, and better than the two 2024 updates I wrote after including Steve’s 2023 update in my last post. All taken together, there has been slow but positive movement for a while and that trend is hopefully clear. I appreciate the additional older macOS support notes.

Reply

Are there any differences if you add the appropriate ARIA roles to the appropriate HTML text level elements, e.g. <del>?
Sometimes support for an ARIA role is better than for the HTML element that maps to it, aso there’s no harm in adding the role to the corresponding element, though it should be redundant.
Great pot though, as someone battling rustration around lack of screen reader support for text level elements since 2010 it is good to see significant progress.

Birkir Gunnarsson; . Permalink
In response to Birkir Gunnarsson. Reply

Are there any differences if you add the appropriate ARIA roles to the appropriate HTML text level elements […]?

Nope. Not in my tests with the kit I outlined. I would have noted it.

However, four comments above yours James gives results from his own Safari 17.x testing and includes how the roles impacted them.

Leave a Comment or Response

  • The form doesn’t support Markdown.
  • This form allows limited HTML.
  • Allowed HTML elements are <a href>, <blockquote>, <code>, <del>, <em>, <ins>, <q>, <strong>, and maybe some others. WordPress is fickle and randomly blocks or allows some.
  • If you want to include HTML examples in your comment, then HTML encode them. E.g. <code>&lt;div&gt;</code> (you can copy and paste that chunk).