Skip to content

Commit

Permalink
Merge pull request #659 from electricbookworks/epub-accessibility
Browse files Browse the repository at this point in the history
Improve epub accessibility
  • Loading branch information
arthurattwell authored Apr 17, 2023
2 parents 82c5e80 + 8d0622f commit 3590514
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 21 deletions.
20 changes: 19 additions & 1 deletion _docs/editing/figures.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Here is a full example:
{% raw %}
```
{% include figure
images="mydog.jpg, yourdog.jpg"
images="mydog.jpg"
html="<table></table>"
markdown="A *bad* example."
reference="Figure 1.2a"
Expand All @@ -72,6 +72,24 @@ caption='Blake's illustration for "The Tyger".'
caption="Blake's illustration for “The Tyger”."
```

### Multiple images and alt text

You can have multiple images in the same figure. Include each image's filename in a comma-separated list.

Each image needs its own alt text. To do this, include each image's alt text in order, separated by `|`.

{% raw %}
```
{% include figure
images="mydog.jpg, yourdog.jpg"
caption="Our dogs."
alt-text="A chocolate-coloured labrador | A grey husky."
%}
```
{% endraw %}

Each image matches each piece of alt text in order. If you include multiple images in a figure, make sure you provide separate alt text for each image. If you don't, some images will have no alt text.

### Rotating figures

If you need to rotate a large figure on the page, add the `rotate` class. E.g.
Expand Down
6 changes: 6 additions & 0 deletions _docs/output/epub-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ defaults:
contains-svg: true
```

## HTML transformations

You may want to make universal changes to your epubs' HTML when epubs are generated. These are called transformations. The template already includes one simple transformation for accessibility purposes: the `epubAriaSidenotes` transformation gives all elements with the class `sidenote` the ARIA attribute `role="note"`.

To add a transformation, add a file containing a single function to `_tools/gulp/transformations/epub`. For the structure of the function, copy one of the existing files there. For syntax, these functions use [Cheerio](https://cheerio.js.org/docs/api/classes/Cheerio#manipulation-methods).

## Troubleshooting

Epubs are notoriously hard to make, largely because validation is so strict. Here are some tips that may be useful when troubleshooting.
Expand Down
25 changes: 20 additions & 5 deletions _includes/figure
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ Then get the total number of images in the array.
{% assign figure-images = image-list | split: "," %}
{% assign number-of-images = figure-images | size %}

{% comment %} Do the same for alt text,
since each image has its own. They should be
separated with |s. {% endcomment %}
{% assign alt-text-array-raw = include.alt-text | split: "|" %}
{% assign alt-text-array = "" | split: "" %}
{% for alt-text in alt-text-array-raw %}
{% capture alt-text-array-entry %}{{ alt-text | strip }}{% endcapture %}
{% assign alt-text-array = alt-text-array | push: alt-text-array-entry %}
{% endfor %}

{% comment %} Allow tag to override default images location,
where 'path=""' can specify a path relative to the site's
root directory. {% endcomment %}
Expand All @@ -41,13 +51,15 @@ We have to do funky remove: '<p>' | remove: '</p>' | strip_newlines
because https://github.com/jekyll/jekyll/issues/2248.
{% endcomment %}

<div class="figure{% if include.class %} {{ include.class }}{% endif %}"{% if figure-reference %} id="{{ figure-reference | slugify }}"{% endif %}>
<div class="figure{% if include.class %} {{ include.class }}{% endif %}"{% if figure-reference %} id="{{ figure-reference | slugify }}"{% endif %} role="figure" {% if include.caption %}aria-labelledby="{{ figure-reference | slugify }}-caption"{% endif %}>

<div class="figure-body">

{% if include.image or include.images %}
<div class="figure-images contains-{{ number-of-images }}">

{% assign alt-text-counter = 0 %}

{% for image in figure-images %}

{% comment %} Get the file extension and the filename without it,
Expand All @@ -72,7 +84,7 @@ because https://github.com/jekyll/jekyll/issues/2248.
sizes="(min-width: 600px) 1300px, 100vw"
{% endunless %}

alt="{% if include.title %}{{ include.title | markdownify | strip_html }}: {% endif %}{% if include.alt-text %}{{ include.alt-text | markdownify | strip_html }}{% else %}{{ include.caption | markdownify | strip_html }}{% endif %}"
alt="{% if include.title %}{{ include.title | markdownify | strip_html }}: {% endif %}{% if alt-text-array.size > 0 %}{{ alt-text-array[alt-text-counter] | markdownify | strip_html }}{% else %}{{ include.caption | markdownify | strip_html }}{% endif %}"
{% if include.image-height != nil %} class="height-{{ include.image-height }}"{% endif %}
/>
</noscript>
Expand All @@ -87,19 +99,22 @@ because https://github.com/jekyll/jekyll/issues/2248.
sizes="auto"
{% endunless %}

alt="{% if include.title %}{{ include.title | markdownify | strip_html }}: {% endif %}{% if include.alt-text %}{{ include.alt-text | markdownify | strip_html }}{% else %}{{ include.caption | markdownify | strip_html }}{% endif %}"
alt="{% if include.title %}{{ include.title | markdownify | strip_html }}: {% endif %}{% if alt-text-array.size > 0 %}{{ alt-text-array[alt-text-counter] | markdownify | strip_html }}{% else %}{{ include.caption | markdownify | strip_html }}{% endif %}"
class="{% if image contains '.svg' %}inject-svg{% endif %}{% if include.image-height != nil %} height-{{ include.image-height }}{% endif %}"
/>

{% else %}

<img src="{{ images }}/{{ image }}"
alt="{% capture figure-image-alt-text %}{% if include.title %}{{ include.title }}: {% endif %}{% if include.alt-text %}{{ include.alt-text }}{% else %}{{ include.caption }}{% endif %}{% endcapture %}{{ figure-image-alt-text | replace: "&nbsp;", " " | replace: "&shy;", "" | markdownify | strip_html }}"
alt="{% capture figure-image-alt-text %}{% if include.title %}{{ include.title }}: {% endif %}{% if alt-text-array.size > 0 %}{{ alt-text-array[alt-text-counter] }}{% else %}{{ include.caption }}{% endif %}{% endcapture %}{{ figure-image-alt-text | replace: "&nbsp;", " " | replace: "&shy;", "" | markdownify | strip_html }}"
class="{% if image contains '.svg' %}inject-svg{% endif %}{% if include.image-height != nil %} height-{{ include.image-height }}{% endif %}" />

{% endif %}

{% if include.link %}</a>{% endif %}

{% assign alt-text-counter = alt-text-counter | plus: 1 %}

{% endfor %}
</div>
{% endif %}
Expand Down Expand Up @@ -131,7 +146,7 @@ because https://github.com/jekyll/jekyll/issues/2248.
{% if include.slide-caption == nil %}

{% if figure-reference-hidden == false or include.caption and include.caption != "" %}
<p class="caption" markdown="1">
<p class="caption" markdown="1" id="{{ figure-reference | slugify }}-caption">
{% if figure-reference and figure-reference-hidden == false %}<span class="figure-reference">{{ figure-reference }} </span>{% endif %}{{ include.caption | markdownify | remove: '<p>' | remove: '</p>' | strip_newlines }}
</p>

Expand Down
2 changes: 0 additions & 2 deletions _sass/template/partials/_epub-code.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ $epub-code: true !default;
font-size: 90%;
line-height: $line-height-default;
white-space: pre-wrap;
background-color: $color-light;
padding: 0.1em 0.3em;
border-radius: 0.2em;
}
pre {
background-color: $color-light;
border-radius: 0.2em;
margin: $line-height-default 0;
padding: $line-height-default / 2;
Expand Down
25 changes: 19 additions & 6 deletions _sass/template/partials/_epub-helpers.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@ $epub-helpers: true !default;
@if $epub-helpers {

.visuallyhidden {
// We don't use the best-practice technique in web partials
// because that requires moving the element out of the flow
// with position absolute or fixed, but epubcheck throws
// warnings if we use those.
display: none;
}

// See https://webaim.org/techniques/css/invisiblecontent/
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;

// This is the traditional approach, most likely
// to work reliably on older epub readers.
left: -10000px;
top: auto;

// This additional approach works on Kindle,
// which otherwise leaves big visual gaps where
// this element appears in the text.
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
margin: -1px;
padding: 0;
}
}
2 changes: 0 additions & 2 deletions _sass/template/partials/_print-code.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ $print-code: true !default;
font-size: 90%;
line-height: $line-height-default;
white-space: pre-wrap;
background-color: $color-light;
padding: 0.1em 0.3em;
border-radius: 0.2em;
}
pre {
background-color: $color-light;
border-radius: 0.2em;
margin: $line-height-default 0;
padding: $line-height-default / 2;
Expand Down
2 changes: 0 additions & 2 deletions _sass/template/partials/_web-code.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ $web-code: true !default;
font-size: 90%;
line-height: $line-height-default;
white-space: pre-wrap;
background-color: $color-light;
padding: 0.1em 0.3em;
border-radius: 0.2em;
}
pre {
background-color: $color-light;
border-radius: 0.2em;
margin: $line-height-default 0;
padding: $line-height-default / 2;
Expand Down
23 changes: 23 additions & 0 deletions _tools/gulp/processors/epub.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,32 @@ const del = require('del')
const gulp = require('gulp')
const iconv = require('iconv-lite')
const rename = require('gulp-rename')
const fsPath = require('path')

// Local helpers
const { book, language } = require('../helpers/args.js')
const { paths } = require('../helpers/paths.js')
const epubTransformations = require('require-all')(fsPath.join(__dirname, '/../transformations/epub'))

// Run epub transformations from scripts in transformations/epub
function runEpubTransformations (done) {
'use strict'

gulp.src([paths.epub.src], { base: './' })
.pipe(cheerio({
run: function ($) {
Object.keys(epubTransformations).forEach(async function (transformation) {
epubTransformations[transformation][transformation]($)
})
},
parserOptions: {
xmlMode: true
}
}))
.pipe(debug({ title: 'Performing epub HTML transformations ...' }))
.pipe(gulp.dest('./'))
done()
}

// Convert all file names in internal links from .html to .xhtml.
// This is required for epub output to avoid EPUBCheck warnings.
Expand Down Expand Up @@ -98,3 +120,4 @@ function epubCleanHtmlFiles () {
exports.epubXhtmlLinks = epubXhtmlLinks
exports.epubXhtmlFiles = epubXhtmlFiles
exports.epubCleanHtmlFiles = epubCleanHtmlFiles
exports.runEpubTransformations = runEpubTransformations
5 changes: 5 additions & 0 deletions _tools/gulp/transformations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Adding transformation functions

The functions folder lets you define custom functions that transform HTML during pre-processing. For example, the template includes a function that adds ARIA `role="note"` to `.sidenote` elements.

Currently, these functions only affect epub output.
8 changes: 8 additions & 0 deletions _tools/gulp/transformations/epub/epubAriaSidenotes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function epubAriaSidenotes ($) {
// Add note role to sidenotes
$('[class^="sidenote"]').each(function () {
$(this).attr('role', 'note')
})
}

exports.epubAriaSidenotes = epubAriaSidenotes
10 changes: 10 additions & 0 deletions _tools/gulp/transformations/epub/exampleTransformation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// For methods you can use to manipulate $(this), see
// https://cheerio.js.org/docs/api/classes/Cheerio#manipulation-methods

function exampleTransformation ($) {
$('[class^="figure"]').each(function () {
// console.log($(this) + ' is a figure.')
})
}

exports.exampleTransformation = exampleTransformation
29 changes: 28 additions & 1 deletion _tools/run/helpers/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,33 @@ async function convertXHTMLLinks (argv) {
}
}

// Run HTML transformations on elements in epubs
async function epubHTMLTransformations (argv) {
'use strict'
console.log('Adding ARIA roles ...')

try {
let epubHTMLTransformationsProcess
if (argv.language) {
epubHTMLTransformationsProcess = spawn(
'gulp',
['runEpubTransformations',
'--book', argv.book,
'--language', argv.language]
)
} else {
epubHTMLTransformationsProcess = spawn(
'gulp',
['runEpubTransformations', '--book', argv.book]
)
}
await logProcess(epubHTMLTransformationsProcess, 'Sidenotes ARIA role')
return true
} catch (error) {
console.log(error)
}
}

// Converts .html files to .xhtml, e.g. for epub output
async function convertXHTMLFiles (argv) {
'use strict'
Expand Down Expand Up @@ -640,7 +667,6 @@ function fileList (argv) {
// 'cover-page.html' or 'cover-versions-of-songs.html'.
let coverFile = false
files.forEach(function (filename) {
console.log(filename);
// Remove all non-alphabetical-characters
const filenameWordsOnly = filename.replace(/[^a-zA-Z]/g, '')

Expand Down Expand Up @@ -1470,6 +1496,7 @@ function newBook (argv) {
}

module.exports = {
epubHTMLTransformations,
addToEpub,
assembleApp,
bookAssetPaths,
Expand Down
4 changes: 3 additions & 1 deletion _tools/run/helpers/output/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ const {
renderIndexComments,
renderIndexLinks,
renderMathjax,
runPrince
runPrince,
epubHTMLTransformations
} = require('../helpers.js')
const merge = require('../merge')

Expand Down Expand Up @@ -64,6 +65,7 @@ async function epub (argv) {
try {
await fs.emptyDir(process.cwd() + '/_site')
await jekyll(argv)
await epubHTMLTransformations(argv)
await renderIndexComments(argv)
await renderIndexLinks(argv)
await convertXHTMLLinks(argv)
Expand Down
3 changes: 2 additions & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { parallel } = require('gulp')

// Import tasks
const {
epubXhtmlLinks, epubXhtmlFiles, epubCleanHtmlFiles
epubXhtmlLinks, epubXhtmlFiles, epubCleanHtmlFiles, runEpubTransformations
} = require('./_tools/gulp/processors/epub.js')

const {
Expand Down Expand Up @@ -56,6 +56,7 @@ exports.images = parallel(
exports.epubXhtmlLinks = epubXhtmlLinks
exports.epubXhtmlFiles = epubXhtmlFiles
exports.epubCleanHtmlFiles = epubCleanHtmlFiles
exports.runEpubTransformations = runEpubTransformations

exports.renderIndexCommentsAsTargets = renderIndexCommentsAsTargets
exports.renderIndexListReferences = renderIndexListReferences
Expand Down

0 comments on commit 3590514

Please sign in to comment.