Skip to content

Commit 0260f76

Browse files
committed
Change footnote rendering to match Pandoc
* Use `a` around `sup`, not the inverse * Use counters instead of the actual identifier/label * Add ARIA roles to footer, calls, notes, and backlinks * Change backlink class from `footnote-backref` to `footnote-back` * Change foot tag name from `div` to `section` * Change hashes from `fn-1` to `#fn1` * Remove unneeded wrapping paragraph around backlink Closes remarkjs/remark#672.
1 parent c5f8266 commit 0260f76

File tree

7 files changed

+358
-241
lines changed

7 files changed

+358
-241
lines changed

lib/footer.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ export function footer(h) {
2424
var def
2525
/** @type {Link} */
2626
var backReference
27-
/** @type {Paragraph} */
28-
var tail
2927
/** @type {Array.<BlockContent>} */
3028
var content
29+
/** @type {string} */
30+
var marker
3131

3232
while (++index < footnoteOrder.length) {
3333
def = footnoteById[footnoteOrder[index].toUpperCase()]
@@ -36,30 +36,30 @@ export function footer(h) {
3636
continue
3737
}
3838

39+
marker = String(index + 1)
40+
3941
content = [...def.children]
4042
backReference = {
4143
type: 'link',
42-
url: '#fnref-' + def.identifier,
43-
data: {hProperties: {className: ['footnote-backref']}},
44+
url: '#fnref' + marker,
45+
data: {hProperties: {className: ['footnote-back'], role: 'doc-backlink'}},
4446
children: [{type: 'text', value: '↩'}]
4547
}
4648

4749
if (
4850
content[content.length - 1] &&
4951
content[content.length - 1].type === 'paragraph'
5052
) {
51-
// @ts-ignore it’s a paragraph, TypeScript...
52-
tail = content[content.length - 1]
53+
// @ts-ignore it’s a paragraph, TypeScript
54+
content[content.length - 1].children.push(backReference)
5355
} else {
54-
tail = {type: 'paragraph', children: []}
55-
content.push(tail)
56+
// @ts-ignore Indeed, link directly added in block content.
57+
content.push(backReference)
5658
}
5759

58-
tail.children.push(backReference)
59-
6060
listItems.push({
6161
type: 'listItem',
62-
data: {hProperties: {id: 'fn-' + def.identifier}},
62+
data: {hProperties: {id: 'fn' + marker, role: 'doc-endnote'}},
6363
children: content,
6464
position: def.position
6565
})
@@ -71,8 +71,8 @@ export function footer(h) {
7171

7272
return h(
7373
null,
74-
'div',
75-
{className: ['footnotes']},
74+
'section',
75+
{className: ['footnotes'], role: 'doc-endnotes'},
7676
wrap(
7777
[].concat(
7878
thematicBreak(h),

lib/handlers/footnote-reference.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,18 @@ import {u} from 'unist-builder'
1212
export function footnoteReference(h, node) {
1313
var footnoteOrder = h.footnoteOrder
1414
var identifier = String(node.identifier)
15+
var index = footnoteOrder.indexOf(identifier)
16+
var marker = String(index === -1 ? footnoteOrder.push(identifier) : index + 1)
1517

16-
if (!footnoteOrder.includes(identifier)) {
17-
footnoteOrder.push(identifier)
18-
}
19-
20-
return h(node.position, 'sup', {id: 'fnref-' + identifier}, [
21-
h(node, 'a', {href: '#fn-' + identifier, className: ['footnote-ref']}, [
22-
u('text', node.label || identifier)
23-
])
24-
])
18+
return h(
19+
node,
20+
'a',
21+
{
22+
href: '#fn' + marker,
23+
className: ['footnote-ref'],
24+
id: 'fnref' + marker,
25+
role: 'doc-noteref'
26+
},
27+
[h(node.position, 'sup', [u('text', marker)])]
28+
)
2529
}

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ The following [mdast][]:
154154
type: 'image',
155155
src: 'circle.svg',
156156
alt: 'Big red circle on a black background',
157-
title: null
157+
title: null,
158158
data: {hProperties: {className: ['responsive']}}
159159
}
160160
```

test/footnote-definition.js

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,41 +28,56 @@ test('FootnoteDefinition', function (t) {
2828
])
2929
),
3030
u('root', [
31-
u('element', {tagName: 'sup', properties: {id: 'fnref-zulu'}}, [
32-
u(
33-
'element',
34-
{
35-
tagName: 'a',
36-
properties: {href: '#fn-zulu', className: ['footnote-ref']}
37-
},
38-
[u('text', 'zulu')]
39-
)
40-
]),
31+
u(
32+
'element',
33+
{
34+
tagName: 'a',
35+
properties: {
36+
href: '#fn1',
37+
className: ['footnote-ref'],
38+
id: 'fnref1',
39+
role: 'doc-noteref'
40+
}
41+
},
42+
[u('element', {tagName: 'sup', properties: {}}, [u('text', '1')])]
43+
),
4144
u('text', '\n'),
42-
u('element', {tagName: 'div', properties: {className: ['footnotes']}}, [
43-
u('text', '\n'),
44-
u('element', {tagName: 'hr', properties: {}}, []),
45-
u('text', '\n'),
46-
u('element', {tagName: 'ol', properties: {}}, [
45+
u(
46+
'element',
47+
{
48+
tagName: 'section',
49+
properties: {className: ['footnotes'], role: 'doc-endnotes'}
50+
},
51+
[
4752
u('text', '\n'),
48-
u('element', {tagName: 'li', properties: {id: 'fn-zulu'}}, [
49-
u('text', 'alpha'),
53+
u('element', {tagName: 'hr', properties: {}}, []),
54+
u('text', '\n'),
55+
u('element', {tagName: 'ol', properties: {}}, [
56+
u('text', '\n'),
5057
u(
5158
'element',
52-
{
53-
tagName: 'a',
54-
properties: {
55-
href: '#fnref-zulu',
56-
className: ['footnote-backref']
57-
}
58-
},
59-
[u('text', '↩')]
60-
)
59+
{tagName: 'li', properties: {id: 'fn1', role: 'doc-endnote'}},
60+
[
61+
u('text', 'alpha'),
62+
u(
63+
'element',
64+
{
65+
tagName: 'a',
66+
properties: {
67+
href: '#fnref1',
68+
className: ['footnote-back'],
69+
role: 'doc-backlink'
70+
}
71+
},
72+
[u('text', '↩')]
73+
)
74+
]
75+
),
76+
u('text', '\n')
6177
]),
6278
u('text', '\n')
63-
]),
64-
u('text', '\n')
65-
])
79+
]
80+
)
6681
]),
6782
'should use the first `footnoteDefinition` if multiple exist'
6883
)

test/footnote-mixed.js

Lines changed: 76 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import test from 'tape'
22
import {u} from 'unist-builder'
33
import {toHast} from '../index.js'
44

5-
test('Footnote', function (t) {
5+
test('Footnote (mixed)', function (t) {
66
var mdast = toHast(
77
u('root', [
88
u('paragraph', [
@@ -22,65 +22,94 @@ test('Footnote', function (t) {
2222
var hast = u('root', [
2323
u('element', {tagName: 'p', properties: {}}, [
2424
u('text', 'Alpha'),
25-
u('element', {tagName: 'sup', properties: {id: 'fnref-1'}}, [
26-
u(
27-
'element',
28-
{
29-
tagName: 'a',
30-
properties: {href: '#fn-1', className: ['footnote-ref']}
31-
},
32-
[u('text', '1')]
33-
)
34-
])
25+
u(
26+
'element',
27+
{
28+
tagName: 'a',
29+
properties: {
30+
href: '#fn1',
31+
className: ['footnote-ref'],
32+
id: 'fnref1',
33+
role: 'doc-noteref'
34+
}
35+
},
36+
[u('element', {tagName: 'sup', properties: {}}, [u('text', '1')])]
37+
)
3538
]),
3639
u('text', '\n'),
3740
u('element', {tagName: 'p', properties: {}}, [
3841
u('text', 'Bravo'),
39-
u('element', {tagName: 'sup', properties: {id: 'fnref-x'}}, [
40-
u(
41-
'element',
42-
{
43-
tagName: 'a',
44-
properties: {href: '#fn-x', className: ['footnote-ref']}
45-
},
46-
[u('text', 'x')]
47-
)
48-
])
42+
u(
43+
'element',
44+
{
45+
tagName: 'a',
46+
properties: {
47+
href: '#fn2',
48+
className: ['footnote-ref'],
49+
id: 'fnref2',
50+
role: 'doc-noteref'
51+
}
52+
},
53+
[u('element', {tagName: 'sup', properties: {}}, [u('text', '2')])]
54+
)
4955
]),
5056
u('text', '\n'),
51-
u('element', {tagName: 'div', properties: {className: ['footnotes']}}, [
52-
u('text', '\n'),
53-
u('element', {tagName: 'hr', properties: {}}, []),
54-
u('text', '\n'),
55-
u('element', {tagName: 'ol', properties: {}}, [
57+
u(
58+
'element',
59+
{
60+
tagName: 'section',
61+
properties: {className: ['footnotes'], role: 'doc-endnotes'}
62+
},
63+
[
5664
u('text', '\n'),
57-
u('element', {tagName: 'li', properties: {id: 'fn-1'}}, [
58-
u('text', 'Charlie'),
65+
u('element', {tagName: 'hr', properties: {}}, []),
66+
u('text', '\n'),
67+
u('element', {tagName: 'ol', properties: {}}, [
68+
u('text', '\n'),
5969
u(
6070
'element',
61-
{
62-
tagName: 'a',
63-
properties: {href: '#fnref-1', className: ['footnote-backref']}
64-
},
65-
[u('text', '↩')]
66-
)
67-
]),
68-
u('text', '\n'),
69-
u('element', {tagName: 'li', properties: {id: 'fn-x'}}, [
70-
u('text', 'Delta'),
71+
{tagName: 'li', properties: {id: 'fn1', role: 'doc-endnote'}},
72+
[
73+
u('text', 'Charlie'),
74+
u(
75+
'element',
76+
{
77+
tagName: 'a',
78+
properties: {
79+
href: '#fnref1',
80+
className: ['footnote-back'],
81+
role: 'doc-backlink'
82+
}
83+
},
84+
[u('text', '↩')]
85+
)
86+
]
87+
),
88+
u('text', '\n'),
7189
u(
7290
'element',
73-
{
74-
tagName: 'a',
75-
properties: {href: '#fnref-x', className: ['footnote-backref']}
76-
},
77-
[u('text', '↩')]
78-
)
91+
{tagName: 'li', properties: {id: 'fn2', role: 'doc-endnote'}},
92+
[
93+
u('text', 'Delta'),
94+
u(
95+
'element',
96+
{
97+
tagName: 'a',
98+
properties: {
99+
href: '#fnref2',
100+
className: ['footnote-back'],
101+
role: 'doc-backlink'
102+
}
103+
},
104+
[u('text', '↩')]
105+
)
106+
]
107+
),
108+
u('text', '\n')
79109
]),
80110
u('text', '\n')
81-
]),
82-
u('text', '\n')
83-
])
111+
]
112+
)
84113
])
85114

86115
t.deepEqual(mdast, hast, 'should order the footnote section by usage')

0 commit comments

Comments
 (0)