Skip to content

Commit

Permalink
feat(rule): Inline text spacing must be adjustable with custom styles…
Browse files Browse the repository at this point in the history
…heets (#1446)

Ensure text spacing is not affected by inline spacing styles that affects CSS specificity

Closes issue: 
- #1301

**Note: to make sure to make it clear in the failure message that this is due to limited support for custom stylesheets.**

## Reviewer checks

**Required fields, to be filled out by PR reviewer(s)**
- [x] Follows the commit message policy, appropriate for next version
- [x] Has documentation updated, a DU ticket, or requires no documentation change
- [x] Includes new tests, or was unnecessary
- [x] Code is reviewed for security by: Steve
  • Loading branch information
jeeyyy authored and WilcoFiers committed Apr 17, 2019
1 parent 6cf8b2f commit 430b07f
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 2 deletions.
1 change: 1 addition & 0 deletions doc/rule-descriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
| aria-valid-attr | Ensures attributes that begin with aria- are valid ARIA attributes | Critical | cat.aria, wcag2a, wcag412 | true |
| audio-caption | Ensures <audio> elements have captions | Critical | cat.time-and-media, wcag2a, wcag121, section508, section508.22.a | false |
| autocomplete-valid | Ensure the autocomplete attribute is correct and suitable for the form field | Serious | cat.forms, wcag21aa, wcag135 | true |
| avoid-inline-spacing | Ensure that text spacing set through style attributes can be adjusted with custom stylesheets | Serious | wcag21, wcag1412 | true |
| blink | Ensures <blink> elements are not used | Serious | cat.time-and-media, wcag2a, wcag222, section508, section508.22.j | true |
| button-name | Ensures buttons have discernible text | Serious, Critical | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a | true |
| bypass | Ensures each page has at least one mechanism for a user to bypass navigation and jump straight to the content | Serious | cat.keyboard, wcag2a, wcag241, section508, section508.22.o | true |
Expand Down
18 changes: 18 additions & 0 deletions lib/checks/shared/avoid-inline-spacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const inlineSpacingCssProperties = [
'line-height',
'letter-spacing',
'word-spacing'
];

const overriddenProperties = inlineSpacingCssProperties.filter(property => {
if (node.style.getPropertyPriority(property) === `important`) {
return property;
}
});

if (overriddenProperties.length > 0) {
this.data(overriddenProperties);
return false;
}

return true;
11 changes: 11 additions & 0 deletions lib/checks/shared/avoid-inline-spacing.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "avoid-inline-spacing",
"evaluate": "avoid-inline-spacing.js",
"metadata": {
"impact": "serious",
"messages": {
"pass": "No inline styles with '!important' that affect text spacing has been specified",
"fail": "Remove '!important' from inline style{{=it.data && it.data.length > 1 ? 's' : ''}} {{=it.data.join(', ')}}, as overriding this is not supported by most browsers"
}
}
}
12 changes: 12 additions & 0 deletions lib/rules/avoid-inline-spacing.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": "avoid-inline-spacing",
"selector": "[style]",
"tags": ["wcag21", "wcag1412"],
"metadata": {
"description": "Ensure that text spacing set through style attributes can be adjusted with custom stylesheets",
"help": "Inline text spacing must be adjustable with custom stylesheets"
},
"all": ["avoid-inline-spacing"],
"any": [],
"none": []
}
121 changes: 121 additions & 0 deletions test/checks/shared/avoid-inline-spacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
describe('avoid-inline-spacing tests', function() {
'use strict';

var fixture = document.getElementById('fixture');
var queryFixture = axe.testUtils.queryFixture;
var check = checks['avoid-inline-spacing'];
var checkContext = axe.testUtils.MockCheckContext();

afterEach(function() {
fixture.innerHTML = '';
checkContext.reset();
});

it('returns true when no inline spacing styles are specified', function() {
var vNode = queryFixture(
'<p id="target" style="font-size: 200%;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isTrue(actual);
assert.isNull(checkContext._data);
});

it('returns true when inline spacing styles has invalid value', function() {
var vNode = queryFixture(
'<p id="target" style="line-height: 5invalid;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isTrue(actual);
assert.isNull(checkContext._data);
});

it('returns true when inline spacing styles has invalid value and `!important` priority', function() {
var vNode = queryFixture(
'<p id="target" style="line-height: invalid !important;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isTrue(actual);
assert.isNull(checkContext._data);
});

it('returns true when `line-height` style specified has no `!important` priority', function() {
var vNode = queryFixture(
'<p id="target" style="line-height: 1.5;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isTrue(actual);
assert.isNull(checkContext._data);
});

it('returns true when `letter-spacing` style specified has no `!important` priority', function() {
var vNode = queryFixture(
'<p id="target" style="letter-spacing: 50px;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isTrue(actual);
assert.isNull(checkContext._data);
});

it('returns true when `word-spacing` style specified has no `!important` priority', function() {
var vNode = queryFixture(
'<p id="target" style="word-spacing: 10px;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isTrue(actual);
assert.isNull(checkContext._data);
});

it('returns true when none of the multiple inline spacing styles specified have priority of `!important`', function() {
var vNode = queryFixture(
'<p id="target" style="word-spacing: 20ch; letter-spacing: 50rem; line-height: 3;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isTrue(actual);
assert.isNull(checkContext._data);
});

it('returns false when `line-height` style specified has `!important` priority', function() {
var vNode = queryFixture(
'<p id="target" style="line-height: 1.5 !important;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isFalse(actual);
assert.deepEqual(checkContext._data, ['line-height']);
});

it('returns false when `letter-spacing` style specified has `!important` priority', function() {
var vNode = queryFixture(
'<p id="target" style="letter-spacing: 100em !important;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isFalse(actual);
assert.deepEqual(checkContext._data, ['letter-spacing']);
});

it('returns false when `word-spacing` style specified has `!important` priority', function() {
var vNode = queryFixture(
'<p id="target" style="word-spacing: -.4ch !important;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isFalse(actual);
assert.deepEqual(checkContext._data, ['word-spacing']);
});

it('returns false when any of the multiple inline spacing styles specifies priority of `!important`', function() {
var vNode = queryFixture(
'<p id="target" style="word-spacing: 200%; letter-spacing: 50rem !important; line-height: 3;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isFalse(actual);
assert.deepEqual(checkContext._data, ['letter-spacing']);
});

it('returns false when multiple inline spacing styles specifies priority of `!important`', function() {
var vNode = queryFixture(
'<p id="target" style="line-height: 3 !important; letter-spacing: 50rem !important;">The quick brown fox jumped over the lazy dog</p>'
);
var actual = check.evaluate.call(checkContext, vNode.actualNode);
assert.isFalse(actual);
assert.deepEqual(checkContext._data, ['line-height', 'letter-spacing']);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!-- Inapplicable -->
<p id="inapplicable1">I am so blue I'm greener than purple.</p>

<!-- Pass -->
<p id="pass1" style="font-size: 200%" >I stepped on a Corn Flake, now I'm a Cereal Killer</p>
<p id="pass2" style="line-height: 1.5;" >On a scale from one to ten what is your favourite colour of the alphabet.</p>
<p id="pass3" style="letter-spacing: 50px;">The quick brown fox jumped over the lazy dog</p>
<p id="pass4" style="word-spacing: 10px;">A group of 24 Caterpillars have 694 legs</p>
<p id="pass5" style="word-spacing: 20ch; letter-spacing: 50rem; line-height: 3;">Look, a distraction!</p>

<!-- Fail -->
<p id="fail1" style="line-height: 1.5 !important;">Banana error</p>
<p id="fail2" style="letter-spacing: 100em !important;">We need more cheeeeeeessseeeee!!!</p>
<p id="fail3" style="word-spacing: -.4ch !important;">The cheese grater is in the way!</p>
<p id="fail4" style="word-spacing: 200%; letter-spacing: 50rem !important; line-height: 3;">Yo Darth Vader</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "avoid-inline-spacing tests",
"rule": "avoid-inline-spacing",
"violations": [["#fail1"], ["#fail2"], ["#fail3"], ["#fail4"]],
"passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"], ["#pass5"]]
}
9 changes: 7 additions & 2 deletions test/playground.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@
<html lang="en">
<title>O hai</title>

<div class="foo" id="foo">foo</div>
<p id="target" style="word-spacing: 200% !important; letter-spacing: 50rem !important; line-height: 3 !important;">The quick brown fox jumped over the lazy dog</p>

<script src="/axe.js"></script>
<script>
window.addEventListener('load' , function () {
axe.run(document, function (err, res) {
axe.run({
runOnly: {
type: 'rule',
values: ['avoid-inline-spacing']
}
}, function (err, res) {
console.log(res.violations);
res.incomplete.forEach(issue => console.log(issue))
});
Expand Down

0 comments on commit 430b07f

Please sign in to comment.