Skip to content

Commit 7e99cf3

Browse files
committed
Adds aria-hidden to default defaultAttributes option. Adds support for presentational SVG icons (content inside <i>) mapped to svg titles. https://docs.fontawesome.com/web/dig-deeper/accessibility#making-icons-accessible-manually
1 parent 2b411c9 commit 7e99cf3

File tree

4 files changed

+77
-15
lines changed

4 files changed

+77
-15
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@fortawesome/free-solid-svg-icons": "^6.7.2",
4040
"debug": "^4.4.0",
4141
"entities": "^6.0.0",
42+
"nanoid": "^5.1.0",
4243
"posthtml-match-helper": "^2.0.3"
4344
}
4445
}

plugin.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { library } from "@fortawesome/fontawesome-svg-core";
2+
import { nanoid } from "nanoid";
23
import { Transform } from "./src/transform.js";
34

45
import { mergeAttrs, attrsToHtml, faIconToHtml } from "./src/icon-to-html.js";
@@ -12,8 +13,11 @@ export default function(eleventyConfig, pluginOptions = {}) {
1213
bundle: "fontawesome",
1314
transform: "i[class]", // Selector for icons, falsy to disable
1415
shortcode: false, // Optional shortcode name
15-
defaultAttributes: {},
16+
defaultAttributes: {
17+
"aria-hidden": "true",
18+
},
1619
ignoredClasses: [],
20+
generateId: () => `fa11-text-${nanoid()}`,
1721
}, pluginOptions);
1822

1923
if(!options.bundle || typeof options.bundle !== "string") {

src/transform.js

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,18 +139,39 @@ function Transform(eleventyConfig, options = {}) {
139139
if(pageUrl && managers[bundleName] && html) {
140140
managers[bundleName].addToPage(pageUrl, [ html ]);
141141

142+
let attrs = mergeAttrs(node.attrs, options.defaultAttributes);
143+
let content = [];
144+
145+
// set generateId to falsy to disable this feature.
146+
if(typeof options.generateId === "function" && Array.isArray(node?.content) && node.content.length > 0) {
147+
// See https://docs.fontawesome.com/web/dig-deeper/accessibility#making-icons-accessible-manually
148+
let id = options.generateId();
149+
attrs["aria-labelledby"] = id;
150+
attrs.role = "img";
151+
152+
delete attrs["aria-hidden"];
153+
154+
content.push({
155+
tag: "title",
156+
attrs: {
157+
id,
158+
},
159+
content: [...node?.content],
160+
});
161+
}
162+
163+
content.push({
164+
tag: "use",
165+
attrs: {
166+
href: `#${ref}`,
167+
"xlink:href": `#${ref}`,
168+
}
169+
});
170+
142171
return {
143172
tag: "svg",
144-
attrs: filterAttrs(mergeAttrs(node.attrs, options.defaultAttributes)),
145-
content: [
146-
{
147-
tag: "use",
148-
attrs: {
149-
href: `#${ref}`,
150-
"xlink:href": `#${ref}`,
151-
}
152-
}
153-
]
173+
attrs: filterAttrs(attrs),
174+
content,
154175
};
155176
}
156177
} catch(e) {

test/test.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ test("Transform", async t => {
1616
});
1717

1818
let [result] = await elev.toJSON();
19-
t.is(result.content, `<svg><use href="#far-fa-user" xlink:href="#far-fa-user"></use></svg>
19+
t.is(result.content, `<svg aria-hidden="true"><use href="#far-fa-user" xlink:href="#far-fa-user"></use></svg>
2020
<svg style="display: none;"><symbol aria-hidden="true" focusable="false" data-prefix="far" data-icon="user" class="svg-inline--fa fa-user" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" id="far-fa-user"><path fill="currentColor" d="M304 128a80 80 0 1 0 -160 0 80 80 0 1 0 160 0zM96 128a128 128 0 1 1 256 0A128 128 0 1 1 96 128zM49.3 464l349.5 0c-8.9-63.3-63.3-112-129-112l-91.4 0c-65.7 0-120.1 48.7-129 112zM0 482.3C0 383.8 79.8 304 178.3 304l91.4 0C368.2 304 448 383.8 448 482.3c0 16.4-13.3 29.7-29.7 29.7L29.7 512C13.3 512 0 498.7 0 482.3z"></path></symbol></svg>`);
2121
});
2222

@@ -65,7 +65,7 @@ test("Transform is working in layout", async t => {
6565
});
6666

6767
let [result] = await elev.toJSON();
68-
t.is(result.content, `<svg><use href="#far-fa-user" xlink:href="#far-fa-user"></use></svg>
68+
t.is(result.content, `<svg aria-hidden="true"><use href="#far-fa-user" xlink:href="#far-fa-user"></use></svg>
6969
<svg style="display: none;"><symbol aria-hidden="true" focusable="false" data-prefix="far" data-icon="user" class="svg-inline--fa fa-user" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" id="far-fa-user"><path fill="currentColor" d="M304 128a80 80 0 1 0 -160 0 80 80 0 1 0 160 0zM96 128a128 128 0 1 1 256 0A128 128 0 1 1 96 128zM49.3 464l349.5 0c-8.9-63.3-63.3-112-129-112l-91.4 0c-65.7 0-120.1 48.7-129 112zM0 482.3C0 383.8 79.8 304 178.3 304l91.4 0C368.2 304 448 383.8 448 482.3c0 16.4-13.3 29.7-29.7 29.7L29.7 512C13.3 512 0 498.7 0 482.3z"></path></symbol></svg>`);
7070
});
7171

@@ -123,7 +123,7 @@ test("Transform with ignoredClasses", async t => {
123123
});
124124

125125
let [result] = await elev.toJSON();
126-
t.is(result.content, `<i class="fak fa-dot"></i><svg><use href="#far-fa-font-awesome" xlink:href="#far-fa-font-awesome"></use></svg>
126+
t.is(result.content, `<i class="fak fa-dot"></i><svg aria-hidden="true"><use href="#far-fa-font-awesome" xlink:href="#far-fa-font-awesome"></use></svg>
127127
<svg style="display: none;"><symbol aria-hidden="true" focusable="false" data-prefix="far" data-icon="font-awesome" class="svg-inline--fa fa-font-awesome" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" id="far-fa-font-awesome"><path fill="currentColor" d="M91.7 96C106.3 86.8 116 70.5 116 52C116 23.3 92.7 0 64 0S12 23.3 12 52c0 16.7 7.8 31.5 20 41l0 3 0 48 0 256 0 48 0 64 48 0 0-64 389.6 0c14.6 0 26.4-11.8 26.4-26.4c0-3.7-.8-7.3-2.3-10.7L432 272l61.7-138.9c1.5-3.4 2.3-7 2.3-10.7c0-14.6-11.8-26.4-26.4-26.4L91.7 96zM80 400l0-256 356.4 0L388.1 252.5c-5.5 12.4-5.5 26.6 0 39L436.4 400 80 400z"></path></symbol></svg>`);
128128
});
129129

@@ -162,6 +162,42 @@ test("Old tshirt syntax (html)", async t => {
162162
});
163163

164164
let [result] = await elev.toJSON();
165-
t.is(result.content, `<svg><use href="#fas-fa-shirt" xlink:href="#fas-fa-shirt"></use></svg>
165+
t.is(result.content, `<svg aria-hidden="true"><use href="#fas-fa-shirt" xlink:href="#fas-fa-shirt"></use></svg>
166+
<svg style="display: none;"><symbol aria-hidden="true" focusable="false" data-prefix="fas" data-icon="shirt" class="svg-inline--fa fa-shirt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" id="fas-fa-shirt"><path fill="currentColor" d="M211.8 0c7.8 0 14.3 5.7 16.7 13.2C240.8 51.9 277.1 80 320 80s79.2-28.1 91.5-66.8C413.9 5.7 420.4 0 428.2 0l12.6 0c22.5 0 44.2 7.9 61.5 22.3L628.5 127.4c6.6 5.5 10.7 13.5 11.4 22.1s-2.1 17.1-7.8 23.6l-56 64c-11.4 13.1-31.2 14.6-44.6 3.5L480 197.7 480 448c0 35.3-28.7 64-64 64l-192 0c-35.3 0-64-28.7-64-64l0-250.3-51.5 42.9c-13.3 11.1-33.1 9.6-44.6-3.5l-56-64c-5.7-6.5-8.5-15-7.8-23.6s4.8-16.6 11.4-22.1L137.7 22.3C155 7.9 176.7 0 199.2 0l12.6 0z"></path></symbol></svg>`);
167+
});
168+
169+
test("Accessible text", async t => {
170+
let elev = new Eleventy("./test/virtual/", "./_site", {
171+
config: function(eleventyConfig) {
172+
eleventyConfig.addPlugin(fontAwesomePlugin, {
173+
failOnError: true,
174+
generateId: () => `demo-static-id`, // override for test
175+
});
176+
177+
eleventyConfig.addTemplate("index.njk", `<i class="fas fa-tshirt">shirt</i>
178+
{% getBundle "fontawesome" %}`);
179+
}
180+
});
181+
182+
let [result] = await elev.toJSON();
183+
t.is(result.content, `<svg aria-labelledby="demo-static-id" role="img"><title id="demo-static-id">shirt</title><use href="#fas-fa-shirt" xlink:href="#fas-fa-shirt"></use></svg>
184+
<svg style="display: none;"><symbol aria-hidden="true" focusable="false" data-prefix="fas" data-icon="shirt" class="svg-inline--fa fa-shirt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" id="fas-fa-shirt"><path fill="currentColor" d="M211.8 0c7.8 0 14.3 5.7 16.7 13.2C240.8 51.9 277.1 80 320 80s79.2-28.1 91.5-66.8C413.9 5.7 420.4 0 428.2 0l12.6 0c22.5 0 44.2 7.9 61.5 22.3L628.5 127.4c6.6 5.5 10.7 13.5 11.4 22.1s-2.1 17.1-7.8 23.6l-56 64c-11.4 13.1-31.2 14.6-44.6 3.5L480 197.7 480 448c0 35.3-28.7 64-64 64l-192 0c-35.3 0-64-28.7-64-64l0-250.3-51.5 42.9c-13.3 11.1-33.1 9.6-44.6-3.5l-56-64c-5.7-6.5-8.5-15-7.8-23.6s4.8-16.6 11.4-22.1L137.7 22.3C155 7.9 176.7 0 199.2 0l12.6 0z"></path></symbol></svg>`);
185+
});
186+
187+
test("Disable accessible text", async t => {
188+
let elev = new Eleventy("./test/virtual/", "./_site", {
189+
config: function(eleventyConfig) {
190+
eleventyConfig.addPlugin(fontAwesomePlugin, {
191+
failOnError: true,
192+
generateId: false,
193+
});
194+
195+
eleventyConfig.addTemplate("index.njk", `<i class="fas fa-tshirt">shirt</i>
196+
{% getBundle "fontawesome" %}`);
197+
}
198+
});
199+
200+
let [result] = await elev.toJSON();
201+
t.is(result.content, `<svg aria-hidden="true"><use href="#fas-fa-shirt" xlink:href="#fas-fa-shirt"></use></svg>
166202
<svg style="display: none;"><symbol aria-hidden="true" focusable="false" data-prefix="fas" data-icon="shirt" class="svg-inline--fa fa-shirt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" id="fas-fa-shirt"><path fill="currentColor" d="M211.8 0c7.8 0 14.3 5.7 16.7 13.2C240.8 51.9 277.1 80 320 80s79.2-28.1 91.5-66.8C413.9 5.7 420.4 0 428.2 0l12.6 0c22.5 0 44.2 7.9 61.5 22.3L628.5 127.4c6.6 5.5 10.7 13.5 11.4 22.1s-2.1 17.1-7.8 23.6l-56 64c-11.4 13.1-31.2 14.6-44.6 3.5L480 197.7 480 448c0 35.3-28.7 64-64 64l-192 0c-35.3 0-64-28.7-64-64l0-250.3-51.5 42.9c-13.3 11.1-33.1 9.6-44.6-3.5l-56-64c-5.7-6.5-8.5-15-7.8-23.6s4.8-16.6 11.4-22.1L137.7 22.3C155 7.9 176.7 0 199.2 0l12.6 0z"></path></symbol></svg>`);
167203
});

0 commit comments

Comments
 (0)