diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts
index f7f3cc55bc9e..93997ae66ece 100644
--- a/src/compiler/compile/nodes/Element.ts
+++ b/src/compiler/compile/nodes/Element.ts
@@ -272,6 +272,7 @@ export default class Element extends Node {
}
this.validate_attributes();
+ this.validate_special_cases();
this.validate_bindings();
this.validate_content();
this.validate_event_handlers();
@@ -420,8 +421,16 @@ export default class Element extends Node {
attribute_map.set(attribute.name, attribute);
});
+ }
+
+ validate_special_cases() {
+ const { component,attributes } = this;
+ const attribute_map = new Map();
+
+ attributes.forEach(attribute => (
+ attribute_map.set(attribute.name, attribute)
+ ));
- // handle special cases
if (this.name === 'a') {
const href_attribute = attribute_map.get('href') || attribute_map.get('xlink:href');
const id_attribute = attribute_map.get('id');
@@ -447,9 +456,7 @@ export default class Element extends Node {
});
}
}
- }
-
- else {
+ } else {
const required_attributes = a11y_required_attributes[this.name];
if (required_attributes) {
const has_attribute = required_attributes.some(name => attribute_map.has(name));
@@ -458,16 +465,34 @@ export default class Element extends Node {
should_have_attribute(this, required_attributes);
}
}
+ }
- if (this.name === 'input') {
- const type = attribute_map.get('type');
- if (type && type.get_static_value() === 'image') {
- const required_attributes = ['alt', 'aria-label', 'aria-labelledby'];
- const has_attribute = required_attributes.some(name => attribute_map.has(name));
+ if (this.name === 'input') {
+ const type = attribute_map.get('type');
+ if (type && type.get_static_value() === 'image') {
+ const required_attributes = ['alt', 'aria-label', 'aria-labelledby'];
+ const has_attribute = required_attributes.some(name => attribute_map.has(name));
- if (!has_attribute) {
- should_have_attribute(this, required_attributes, 'input type="image"');
- }
+ if (!has_attribute) {
+ should_have_attribute(this, required_attributes, 'input type="image"');
+ }
+ }
+ }
+
+ if (this.name === 'img') {
+ const alt_attribute = attribute_map.get('alt');
+ const aria_hidden_attribute = attribute_map.get('aria-hidden');
+
+ const aria_hidden_exist = aria_hidden_attribute && aria_hidden_attribute.get_static_value();
+
+ if (alt_attribute && !aria_hidden_exist) {
+ const alt_value = alt_attribute.get_static_value();
+
+ if (alt_value.match(/\b(image|picture|photo)\b/i)) {
+ component.warn(this, {
+ code: `a11y-img-redundant-alt`,
+ message: `A11y: Screenreaders already announce
elements as an image.`
+ });
}
}
}
diff --git a/test/validator/samples/a11y-figcaption-in-non-element-block/input.svelte b/test/validator/samples/a11y-figcaption-in-non-element-block/input.svelte
index 9d4b6ded4d9d..33e7a891f009 100644
--- a/test/validator/samples/a11y-figcaption-in-non-element-block/input.svelte
+++ b/test/validator/samples/a11y-figcaption-in-non-element-block/input.svelte
@@ -3,7 +3,7 @@
-
+
{#if caption}
{caption}
{/if}
diff --git a/test/validator/samples/a11y-figcaption-right-place/input.svelte b/test/validator/samples/a11y-figcaption-right-place/input.svelte
index 808dbee941d6..783219c8f818 100644
--- a/test/validator/samples/a11y-figcaption-right-place/input.svelte
+++ b/test/validator/samples/a11y-figcaption-right-place/input.svelte
@@ -1,5 +1,5 @@
-
+
a foo in its natural habitat
diff --git a/test/validator/samples/a11y-figcaption-wrong-place/input.svelte b/test/validator/samples/a11y-figcaption-wrong-place/input.svelte
index ffa7dde65db1..e99d1cc86cfb 100644
--- a/test/validator/samples/a11y-figcaption-wrong-place/input.svelte
+++ b/test/validator/samples/a11y-figcaption-wrong-place/input.svelte
@@ -1,5 +1,5 @@
-
+
a foo in its natural habitat
@@ -9,7 +9,7 @@
-
+
diff --git a/test/validator/samples/a11y-figcaption-wrong-place/warnings.json b/test/validator/samples/a11y-figcaption-wrong-place/warnings.json
index ee5a89d3ff45..eba5b6f31eb4 100644
--- a/test/validator/samples/a11y-figcaption-wrong-place/warnings.json
+++ b/test/validator/samples/a11y-figcaption-wrong-place/warnings.json
@@ -5,14 +5,14 @@
"start": {
"line": 4,
"column": 1,
- "character": 57
+ "character": 44
},
"end": {
"line": 6,
"column": 14,
- "character": 115
+ "character": 102
},
- "pos": 57
+ "pos": 44
},
{
"code": "a11y-structure",
@@ -20,13 +20,13 @@
"start": {
"line": 15,
"column": 2,
- "character": 252
+ "character": 226
},
"end": {
"line": 17,
"column": 15,
- "character": 328
+ "character": 302
},
- "pos": 252
+ "pos": 226
}
]
diff --git a/test/validator/samples/a11y-img-redundant-alt/input.svelte b/test/validator/samples/a11y-img-redundant-alt/input.svelte
new file mode 100644
index 000000000000..3ba029844da0
--- /dev/null
+++ b/test/validator/samples/a11y-img-redundant-alt/input.svelte
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/validator/samples/a11y-img-redundant-alt/warnings.json b/test/validator/samples/a11y-img-redundant-alt/warnings.json
new file mode 100644
index 000000000000..44106c23d003
--- /dev/null
+++ b/test/validator/samples/a11y-img-redundant-alt/warnings.json
@@ -0,0 +1,47 @@
+[
+ {
+ "code": "a11y-img-redundant-alt",
+ "message": "A11y: Screenreaders already announce
elements as an image.",
+ "end": {
+ "character": 173,
+ "column": 49,
+ "line": 3
+ },
+ "start": {
+ "character": 124,
+ "column": 0,
+ "line": 3
+ },
+ "pos": 124
+ },
+ {
+ "code": "a11y-img-redundant-alt",
+ "message": "A11y: Screenreaders already announce
elements as an image.",
+ "end": {
+ "character": 219,
+ "column": 45,
+ "line": 4
+ },
+ "start": {
+ "character": 174,
+ "column": 0,
+ "line": 4
+ },
+ "pos": 174
+ },
+ {
+ "code": "a11y-img-redundant-alt",
+ "message": "A11y: Screenreaders already announce
elements as an image.",
+ "end": {
+ "character": 272,
+ "column": 52,
+ "line": 5
+ },
+ "start": {
+ "character": 220,
+ "column": 0,
+ "line": 5
+ },
+ "pos": 220
+ }
+]
\ No newline at end of file