Skip to content

Commit b9cc09c

Browse files
committed
feat(template-require-iframe-title): split strict whitespace-only check into opt-out option
Two changes, both addressing fallacies flagged in review-pr-bodies.md: 1. Document that SC 4.1.2 (Name, Role, Value) is the normative requirement; H64 is a sufficient technique illustrating one way to meet it. Earlier framing attributed the requirement to H64 itself — same pattern as template-heading-level's miscited G141. 2. Whitespace-only `title=" "` is technically spec-compliant: ACCNAME 1.2 step 2I (Tooltip) does not whitespace-trim like step 2D (aria-label) does. Keeping the strict flag as default (authoring hygiene — 3 spaces is useless as an accessible name in practice) but exposing `allowWhitespaceOnlyTitle: boolean` (default false) so teams that want spec-aligned behavior can opt out. Empty-string `title=""` and invalid mustache literals (null/undefined/number/boolean) remain always flagged: those are correctness, not style.
1 parent 6d4ed23 commit b9cc09c

3 files changed

Lines changed: 87 additions & 10 deletions

File tree

docs/rules/template-require-iframe-title.md

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
## `<iframe>`
88

9-
`<iframe>` elements must have a unique title property to indicate its content to the user.
10-
11-
This rule takes no arguments.
9+
`<iframe>` elements must have a unique title property so assistive
10+
technology can convey their content to the user. The normative
11+
requirement is [WCAG SC 4.1.2 (Name, Role, Value)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/ensure-compat-rsv.html);
12+
the `title` attribute is *one sufficient technique* for meeting it
13+
(sufficient technique [H64](https://www.w3.org/WAI/WCAG21/Techniques/html/H64)).
1214

1315
## Examples
1416

@@ -27,12 +29,43 @@ This rule **forbids** the following:
2729
<template>
2830
<iframe />
2931
<iframe title='' />
32+
<iframe title=' ' />
33+
<iframe title={{null}} />
34+
<iframe title={{undefined}} />
35+
<iframe title={{42}} />
3036
</template>
3137
```
3238

39+
Whitespace-only `title` (`" "`) is flagged by default as an
40+
authoring-hygiene check: HTML and ACCNAME technically permit it (step 2I
41+
doesn't trim), but a whitespace-only accessible name is useless in
42+
practice. Suppress this specific strictness via `allowWhitespaceOnlyTitle:
43+
true` if your codebase needs it.
44+
45+
## Configuration
46+
47+
- `allowWhitespaceOnlyTitle` (`boolean`, default `false`): when `true`,
48+
`<iframe title=" ">` is accepted. Empty-string `title=""` and
49+
non-string mustache literals (`{{null}}`, `{{undefined}}`, `{{42}}`) are
50+
still flagged.
51+
52+
```js
53+
{
54+
rules: {
55+
'ember/template-require-iframe-title': [
56+
'error',
57+
{ allowWhitespaceOnlyTitle: true },
58+
],
59+
},
60+
}
61+
```
62+
3363
## References
3464

35-
- [Deque University](https://dequeuniversity.com/rules/axe/1.1/frame-title)
36-
- [Technique H65: Using the title attribute of the frame and iframe elements](https://www.w3.org/TR/2014/NOTE-WCAG20-TECHS-20140408/H64)
37-
- [WCAG Success Criterion 2.4.1 - Bypass Blocks](https://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-skip.html)
38-
- [WCAG Success Criterion 4.1.2 - Name, Role, Value](https://www.w3.org/TR/UNDERSTANDING-WCAG20/ensure-compat-rsv.html)
65+
- [WCAG SC 4.1.2 — Name, Role, Value](https://www.w3.org/TR/UNDERSTANDING-WCAG20/ensure-compat-rsv.html)
66+
— the normative requirement.
67+
- [WCAG Technique H64 — Using the title attribute of the iframe element](https://www.w3.org/WAI/WCAG21/Techniques/html/H64)
68+
— a sufficient technique for SC 4.1.2, not itself normative.
69+
- [WCAG Success Criterion 2.4.1 — Bypass Blocks](https://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-skip.html)
70+
- [ACCNAME 1.2 — accessible-name computation](https://www.w3.org/TR/accname-1.2/)
71+
- [axe-core rule `frame-title`](https://dequeuniversity.com/rules/axe/4.10/frame-title)

lib/rules/template-require-iframe-title.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,17 @@ module.exports = {
2727
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-require-iframe-title.md',
2828
templateMode: 'both',
2929
},
30-
schema: [],
30+
schema: [
31+
{
32+
type: 'object',
33+
properties: {
34+
allowWhitespaceOnlyTitle: {
35+
type: 'boolean',
36+
},
37+
},
38+
additionalProperties: false,
39+
},
40+
],
3141
messages: {
3242
// Four messageIds (missingTitle, emptyTitle, dynamicFalseTitle,
3343
// duplicateTitle) for richer diagnostic detail.
@@ -46,6 +56,14 @@ module.exports = {
4656
},
4757
},
4858
create(context) {
59+
// Whitespace-only `title=" "` is technically spec-compliant: ACCNAME
60+
// 1.2 step 2I (Tooltip) does not whitespace-trim like step 2D
61+
// (aria-label) does, so a 3-space accessible name is assigned. That is
62+
// useless in practice but not a spec violation. Default behavior flags
63+
// it as authoring hygiene; set `allowWhitespaceOnlyTitle: true` to
64+
// align with spec/peer behavior.
65+
const allowWhitespaceOnlyTitle = Boolean(context.options[0]?.allowWhitespaceOnlyTitle);
66+
4967
// Each entry: { value, node, index }
5068
// - value: trimmed title string
5169
// - node: original element node for the first occurrence
@@ -76,9 +94,15 @@ module.exports = {
7694
if (titleAttr.value) {
7795
switch (titleAttr.value.type) {
7896
case 'GlimmerTextNode': {
79-
const value = titleAttr.value.chars.trim();
97+
const raw = titleAttr.value.chars;
98+
const value = raw.trim();
8099
if (value.length === 0) {
81-
context.report({ node, messageId: 'emptyTitle' });
100+
// Distinguish genuine empty-string (always flagged — not a
101+
// string) from whitespace-only (spec-compliant but hygiene
102+
// check). Whitespace-only case respects the schema option.
103+
if (raw.length === 0 || !allowWhitespaceOnlyTitle) {
104+
context.report({ node, messageId: 'emptyTitle' });
105+
}
82106
} else {
83107
// Check for duplicate titles. Reports BOTH the first and the
84108
// current occurrence on every collision, sharing a `#N` index

tests/lib/rules/template-require-iframe-title.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ ruleTester.run('template-require-iframe-title', rule, {
1818
'<template><iframe title="" aria-hidden /></template>',
1919
'<template><iframe title="" hidden /></template>',
2020
'<template><iframe title="foo" /><iframe title="bar" /></template>',
21+
// allowWhitespaceOnlyTitle: true — whitespace-only accepted.
22+
{
23+
code: '<template><iframe title=" " /></template>',
24+
options: [{ allowWhitespaceOnlyTitle: true }],
25+
},
2126
],
2227
invalid: [
2328
{
@@ -136,6 +141,21 @@ ruleTester.run('template-require-iframe-title', rule, {
136141
output: null,
137142
errors: [{ messageId: 'dynamicFalseTitle' }],
138143
},
144+
145+
// Whitespace-only title is flagged by default (authoring hygiene).
146+
{
147+
code: '<template><iframe title=" " /></template>',
148+
output: null,
149+
errors: [{ messageId: 'emptyTitle' }],
150+
},
151+
// Empty-string title is flagged even with allowWhitespaceOnlyTitle: true
152+
// (an empty string is not a whitespace string).
153+
{
154+
code: '<template><iframe title="" /></template>',
155+
options: [{ allowWhitespaceOnlyTitle: true }],
156+
output: null,
157+
errors: [{ messageId: 'emptyTitle' }],
158+
},
139159
],
140160
});
141161

0 commit comments

Comments
 (0)