[{"data":1,"prerenderedAt":984},["ShallowReactive",2],{"post-/posts/getting-footnotes-right-in-the-feeds":3},{"id":4,"title":5,"body":6,"created":975,"description":976,"extension":977,"meta":978,"navigation":386,"path":979,"seo":980,"stem":981,"updated":982,"__hash__":983},"posts/2.posts/20250207.Getting Footnotes Right in the Feeds.md","Getting Footnotes Right in the Feeds",{"type":7,"value":8,"toc":967},"minimark",[9,33,53,58,73,103,107,110,113,140,143,207,219,223,232,239,242,263,270,273,584,603,614,622,625,647,656,884,890,894,909,912,931,935,938,941,963],[10,11,12,13,20,21,32],"p",{},"While upgrading my blog from Nuxt Content 2 to ",[14,15,19],"a",{"href":16,"rel":17},"https://content.nuxt.com/blog/v3",[18],"nofollow","Nuxt Content 3",", I made some improvements to my web feed.\nPreviously, the footnotes",[22,23,24],"sup",{},[14,25,31],{"href":26,"ariaDescribedBy":27,"dataFootnoteRef":29,"id":30},"#user-content-fn-1",[28],"footnote-label","","user-content-fnref-1","1"," that I added to my posts were not working properly in feed readers, and I wanted to fix that.",[10,34,35,36,41,42,46,47,52],{},"Specifically, I noticed how footnotes in John Siracusa's ",[14,37,40],{"href":38,"rel":39},"https://hypercritical.co/",[18],"Hypercritical"," feed ",[43,44,45],"em",{},"did"," work nicely in my feed reader of choice (",[14,48,51],{"href":49,"rel":50},"https://netnewswire.com",[18],"NetNewsWire","), and it has been a great help in fixing my own feed.",[54,55,57],"h2",{"id":56},"feeds-and-feed-readers","Feeds and feed readers",[10,59,60,61,66,67,72],{},"For the small number of people who read my blog, it's also possible to do that using a feed reader.\nI publish an ",[14,62,65],{"href":63,"rel":64},"https://en.wikipedia.org/wiki/Atom_(web_standard)",[18],"Atom feed",", which is an XML format for web feeds, similar to the ",[14,68,71],{"href":69,"rel":70},"https://en.wikipedia.org/wiki/RSS",[18],"RSS feed",".\nLike RSS, Atom is widely supported, but it is newer and has some additional features.",[10,74,75,76,79,80,85,86,85,91,96,97,102],{},"Personally, I use ",[14,77,51],{"href":49,"rel":78},[18]," as a feed reader.\nThere's also others such as ",[14,81,84],{"href":82,"rel":83},"https://reederapp.com",[18],"Reeder",", ",[14,87,90],{"href":88,"rel":89},"https://readwise.io/read",[18],"Readwise Reader",[14,92,95],{"href":93,"rel":94},"https://feedly.com",[18],"Feedly",", or ",[14,98,101],{"href":99,"rel":100},"https://feedbin.com",[18],"Feedbin",", each with their own set of features (and not all of them support footnotes).",[54,104,106],{"id":105},"footnotes-in-feed-readers","Footnotes in feed readers",[10,108,109],{},"So, what do I mean with footnotes working nicely in a feed reader?",[10,111,112],{},"There are two features that I require:",[114,115,116,127],"ul",{},[117,118,119,122,123,126],"li",{},[43,120,121],{},"Footnote pop-ups",": When tapping a footnote link, the footnote should appear as a small pop-up at the position of the footnote link in the text, and ",[43,124,125],{},"not scroll down to the footnote list",".",[117,128,129,132,133,136,137,126],{},[43,130,131],{},"Backlinks",": At the bottom of the article, all footnotes should be listed and they should link ",[43,134,135],{},"back"," to where the footnote links are in the text, and ",[43,138,139],{},"not open the link in a web view",[10,141,142],{},"This table gives an overview of the extent of support for these features in several feed readers:",[144,145,146,159],"table",{},[147,148,149],"thead",{},[150,151,152,155,157],"tr",{},[153,154],"th",{},[153,156,121],{},[153,158,131],{},[160,161,162,172,181,189,199],"tbody",{},[150,163,164,167,170],{},[165,166,51],"td",{},[165,168,169],{},"✅",[165,171,169],{},[150,173,174,176,179],{},[165,175,84],{},[165,177,178],{},"❌",[165,180,178],{},[150,182,183,185,187],{},[165,184,101],{},[165,186,169],{},[165,188,178],{},[150,190,191,193,196],{},[165,192,90],{},[165,194,195],{},"⏳",[165,197,198],{},"⏳?",[150,200,201,203,205],{},[165,202,95],{},[165,204,178],{},[165,206,178],{},[10,208,209,210,213,214,216,217],{},"✅: Supported",[211,212],"br",{},"\n❌: Not supported",[211,215],{},"\n⏳: On their backlog",[211,218],{},[54,220,222],{"id":221},"generating-the-feed","Generating the feed",[10,224,225,226,231],{},"I write my articles in Markdown, and ",[14,227,230],{"href":228,"rel":229},"https://content.nuxt.com",[18],"Nuxt Content"," generates HTML for each of my articles.\nFor the website, that HTML is simply rendered in a web app.",[10,233,234,235,238],{},"However, the web feed requires ",[43,236,237],{},"all"," of the articles to be captured in a single XML file, along with some metadata.",[10,240,241],{},"So what's in the feed?",[114,243,244,252],{},[117,245,246,247],{},"Some metadata about the feed itself:\n",[114,248,249],{},[117,250,251],{},"Author, language, favicon, copyright, last updated date, etc.",[117,253,254,255],{},"For each article (called \"entry\" in Atom):\n",[114,256,257,260],{},[117,258,259],{},"Entry title, publish date, author, description, link to web page, etc.",[117,261,262],{},"And most importantly, the content of the entry: The 'raw' HTML that is presented in the feed reader app.",[10,264,265,266,269],{},"Unfortunately, it is currently not trivial to generate an Atom feed using Nuxt Content.\nNuxt Content ",[43,267,268],{},"does"," provide the HTML abstract syntax tree for the entries, which I can process to generate the HTML that goes in the entry's content.",[10,271,272],{},"However, the HTML that Nuxt Content provides does not make my footnotes work properly.\nThis is how the HTML is provided (I added some whitespace & comments to make it more readable):",[274,275,279],"pre",{"className":276,"code":277,"language":278,"meta":29,"style":29},"language-html shiki shiki-themes one-dark-pro","\u003C!-- a footnote link that appears in\n     the article's main content -->\n\u003Csup>\n    \u003Ca\n        href=\"#user-content-fn-1\"\n        aria-describedby=\"footnote-label\"\n        data-footnote-ref\n        id=\"user-content-fnref-1\"\n    >1\u003C/a>\n\u003C/sup>\n\n\u003C!-- other content -->\n\n\u003Csection class=\"footnotes\" data-footnotes>\n    \u003Ch2 class=\"sr-only\" id=\"footnote-label\">\n        Footnotes\n    \u003C/h2>\n    \u003Col>\n        \u003Cli id=\"user-content-fn-1\">\n            This is the footnote.\n            \u003Ca\n                href=\"#user-content-fnref-1\"\n                aria-label=\"Back to reference 1\"\n                class=\"data-footnote-backref\"\n                data-footnote-backref\n            >↩\u003C/a>\n        \u003C/li>\n        \u003C!-- other footnotes -->\n    \u003C/ol>\n\u003C/section>\n","html",[280,281,282,291,297,310,319,333,344,350,361,371,381,388,394,399,420,444,450,460,470,487,493,501,512,523,534,540,550,560,566,575],"code",{"__ignoreMap":29},[283,284,287],"span",{"class":285,"line":286},"line",1,[283,288,290],{"class":289},"sV9Aq","\u003C!-- a footnote link that appears in\n",[283,292,294],{"class":285,"line":293},2,[283,295,296],{"class":289},"     the article's main content -->\n",[283,298,300,304,307],{"class":285,"line":299},3,[283,301,303],{"class":302},"sn6KH","\u003C",[283,305,22],{"class":306},"sVyAn",[283,308,309],{"class":302},">\n",[283,311,313,316],{"class":285,"line":312},4,[283,314,315],{"class":302},"    \u003C",[283,317,318],{"class":306},"a\n",[283,320,322,326,329],{"class":285,"line":321},5,[283,323,325],{"class":324},"sVC51","        href",[283,327,328],{"class":302},"=",[283,330,332],{"class":331},"subq3","\"#user-content-fn-1\"\n",[283,334,336,339,341],{"class":285,"line":335},6,[283,337,338],{"class":324},"        aria-describedby",[283,340,328],{"class":302},[283,342,343],{"class":331},"\"footnote-label\"\n",[283,345,347],{"class":285,"line":346},7,[283,348,349],{"class":324},"        data-footnote-ref\n",[283,351,353,356,358],{"class":285,"line":352},8,[283,354,355],{"class":324},"        id",[283,357,328],{"class":302},[283,359,360],{"class":331},"\"user-content-fnref-1\"\n",[283,362,364,367,369],{"class":285,"line":363},9,[283,365,366],{"class":302},"    >1\u003C/",[283,368,14],{"class":306},[283,370,309],{"class":302},[283,372,374,377,379],{"class":285,"line":373},10,[283,375,376],{"class":302},"\u003C/",[283,378,22],{"class":306},[283,380,309],{"class":302},[283,382,384],{"class":285,"line":383},11,[283,385,387],{"emptyLinePlaceholder":386},true,"\n",[283,389,391],{"class":285,"line":390},12,[283,392,393],{"class":289},"\u003C!-- other content -->\n",[283,395,397],{"class":285,"line":396},13,[283,398,387],{"emptyLinePlaceholder":386},[283,400,402,404,407,410,412,415,418],{"class":285,"line":401},14,[283,403,303],{"class":302},[283,405,406],{"class":306},"section",[283,408,409],{"class":324}," class",[283,411,328],{"class":302},[283,413,414],{"class":331},"\"footnotes\"",[283,416,417],{"class":324}," data-footnotes",[283,419,309],{"class":302},[283,421,423,425,427,429,431,434,437,439,442],{"class":285,"line":422},15,[283,424,315],{"class":302},[283,426,54],{"class":306},[283,428,409],{"class":324},[283,430,328],{"class":302},[283,432,433],{"class":331},"\"sr-only\"",[283,435,436],{"class":324}," id",[283,438,328],{"class":302},[283,440,441],{"class":331},"\"footnote-label\"",[283,443,309],{"class":302},[283,445,447],{"class":285,"line":446},16,[283,448,449],{"class":302},"        Footnotes\n",[283,451,453,456,458],{"class":285,"line":452},17,[283,454,455],{"class":302},"    \u003C/",[283,457,54],{"class":306},[283,459,309],{"class":302},[283,461,463,465,468],{"class":285,"line":462},18,[283,464,315],{"class":302},[283,466,467],{"class":306},"ol",[283,469,309],{"class":302},[283,471,473,476,478,480,482,485],{"class":285,"line":472},19,[283,474,475],{"class":302},"        \u003C",[283,477,117],{"class":306},[283,479,436],{"class":324},[283,481,328],{"class":302},[283,483,484],{"class":331},"\"user-content-fn-1\"",[283,486,309],{"class":302},[283,488,490],{"class":285,"line":489},20,[283,491,492],{"class":302},"            This is the footnote.\n",[283,494,496,499],{"class":285,"line":495},21,[283,497,498],{"class":302},"            \u003C",[283,500,318],{"class":306},[283,502,504,507,509],{"class":285,"line":503},22,[283,505,506],{"class":324},"                href",[283,508,328],{"class":302},[283,510,511],{"class":331},"\"#user-content-fnref-1\"\n",[283,513,515,518,520],{"class":285,"line":514},23,[283,516,517],{"class":324},"                aria-label",[283,519,328],{"class":302},[283,521,522],{"class":331},"\"Back to reference 1\"\n",[283,524,526,529,531],{"class":285,"line":525},24,[283,527,528],{"class":324},"                class",[283,530,328],{"class":302},[283,532,533],{"class":331},"\"data-footnote-backref\"\n",[283,535,537],{"class":285,"line":536},25,[283,538,539],{"class":324},"                data-footnote-backref\n",[283,541,543,546,548],{"class":285,"line":542},26,[283,544,545],{"class":302},"            >↩\u003C/",[283,547,14],{"class":306},[283,549,309],{"class":302},[283,551,553,556,558],{"class":285,"line":552},27,[283,554,555],{"class":302},"        \u003C/",[283,557,117],{"class":306},[283,559,309],{"class":302},[283,561,563],{"class":285,"line":562},28,[283,564,565],{"class":289},"        \u003C!-- other footnotes -->\n",[283,567,569,571,573],{"class":285,"line":568},29,[283,570,455],{"class":302},[283,572,467],{"class":306},[283,574,309],{"class":302},[283,576,578,580,582],{"class":285,"line":577},30,[283,579,376],{"class":302},[283,581,406],{"class":306},[283,583,309],{"class":302},[10,585,586,587,590,591,594,595,598,599,602],{},"Now, observing the ",[280,588,589],{},"href","s, ",[280,592,593],{},"aria-*"," attributes, and ",[280,596,597],{},"data-*"," attributes, it is clear that ",[43,600,601],{},"some"," effort has been put into footnotes by Nuxt Content's Markdown processor.",[10,604,605,606,609,610,613],{},"However, when I load this into ",[14,607,51],{"href":49,"rel":608},[18],", clicking the link to the footnote ",[43,611,612],{},"takes me to a web browser"," where the web page is loaded!",[10,615,616,617,621],{},"And that made me wonder: How do I make it work like ",[14,618,40],{"href":619,"rel":620},"https://hypercritical.co",[18],"'s feed does?",[10,623,624],{},"It turned out that I needed to change 2 things:",[114,626,627,638],{},[117,628,629,630,633,634,637],{},"Link to the footnotes with ",[280,631,632],{},"#fn:1"," (and ",[280,635,636],{},"#fn:2"," and so on).",[117,639,640,641,633,644,637],{},"Backlink from the footnotes with ",[280,642,643],{},"#fnref:1",[280,645,646],{},"#fnref:2",[10,648,649,650,655],{},"Doing this transformation while generating the feed (see ",[14,651,654],{"href":652,"rel":653},"https://github.com/HanKruiger/hankruiger.com/blob/main/server/routes/atom.xml.get.ts",[18],"my implementation",") results in the following HTML:",[274,657,659],{"className":276,"code":658,"language":278,"meta":29,"style":29},"\u003C!-- a footnote link that appears in\n     the article's main content -->\n\u003Csup>\n    \u003Ca\n        href=\"#fn:1\"\n        aria-described-by=\"footnote-label\"\n        data-footnote-ref\n        id=\"fnref:1\"\n    >1\u003C/a>\n\u003C/sup>\n\n\u003C!-- other content -->\n\n\u003Csection class=\"footnotes\" data-footnotes>\n    \u003Ch2 class=\"sr-only\" id=\"footnote-label\">\n        Footnotes\n    \u003C/h2>\n    \u003Col>\n        \u003Cli id=\"fn:1\">\n            This is the footnote.\n            \u003Ca\n                href=\"#fnref:1\"\n                aria-label=\"Back to reference 1\"\n                class=\"data-footnote-backref\"\n                data-footnote-backref\n            >↩\u003C/a>\n        \u003C/li>\n    \u003C/ol>\n\u003C/section>\n",[280,660,661,665,669,677,683,692,701,705,714,722,730,734,738,742,758,778,782,790,798,813,817,823,832,840,848,852,860,868,876],{"__ignoreMap":29},[283,662,663],{"class":285,"line":286},[283,664,290],{"class":289},[283,666,667],{"class":285,"line":293},[283,668,296],{"class":289},[283,670,671,673,675],{"class":285,"line":299},[283,672,303],{"class":302},[283,674,22],{"class":306},[283,676,309],{"class":302},[283,678,679,681],{"class":285,"line":312},[283,680,315],{"class":302},[283,682,318],{"class":306},[283,684,685,687,689],{"class":285,"line":321},[283,686,325],{"class":324},[283,688,328],{"class":302},[283,690,691],{"class":331},"\"#fn:1\"\n",[283,693,694,697,699],{"class":285,"line":335},[283,695,696],{"class":324},"        aria-described-by",[283,698,328],{"class":302},[283,700,343],{"class":331},[283,702,703],{"class":285,"line":346},[283,704,349],{"class":324},[283,706,707,709,711],{"class":285,"line":352},[283,708,355],{"class":324},[283,710,328],{"class":302},[283,712,713],{"class":331},"\"fnref:1\"\n",[283,715,716,718,720],{"class":285,"line":363},[283,717,366],{"class":302},[283,719,14],{"class":306},[283,721,309],{"class":302},[283,723,724,726,728],{"class":285,"line":373},[283,725,376],{"class":302},[283,727,22],{"class":306},[283,729,309],{"class":302},[283,731,732],{"class":285,"line":383},[283,733,387],{"emptyLinePlaceholder":386},[283,735,736],{"class":285,"line":390},[283,737,393],{"class":289},[283,739,740],{"class":285,"line":396},[283,741,387],{"emptyLinePlaceholder":386},[283,743,744,746,748,750,752,754,756],{"class":285,"line":401},[283,745,303],{"class":302},[283,747,406],{"class":306},[283,749,409],{"class":324},[283,751,328],{"class":302},[283,753,414],{"class":331},[283,755,417],{"class":324},[283,757,309],{"class":302},[283,759,760,762,764,766,768,770,772,774,776],{"class":285,"line":422},[283,761,315],{"class":302},[283,763,54],{"class":306},[283,765,409],{"class":324},[283,767,328],{"class":302},[283,769,433],{"class":331},[283,771,436],{"class":324},[283,773,328],{"class":302},[283,775,441],{"class":331},[283,777,309],{"class":302},[283,779,780],{"class":285,"line":446},[283,781,449],{"class":302},[283,783,784,786,788],{"class":285,"line":452},[283,785,455],{"class":302},[283,787,54],{"class":306},[283,789,309],{"class":302},[283,791,792,794,796],{"class":285,"line":462},[283,793,315],{"class":302},[283,795,467],{"class":306},[283,797,309],{"class":302},[283,799,800,802,804,806,808,811],{"class":285,"line":472},[283,801,475],{"class":302},[283,803,117],{"class":306},[283,805,436],{"class":324},[283,807,328],{"class":302},[283,809,810],{"class":331},"\"fn:1\"",[283,812,309],{"class":302},[283,814,815],{"class":285,"line":489},[283,816,492],{"class":302},[283,818,819,821],{"class":285,"line":495},[283,820,498],{"class":302},[283,822,318],{"class":306},[283,824,825,827,829],{"class":285,"line":503},[283,826,506],{"class":324},[283,828,328],{"class":302},[283,830,831],{"class":331},"\"#fnref:1\"\n",[283,833,834,836,838],{"class":285,"line":514},[283,835,517],{"class":324},[283,837,328],{"class":302},[283,839,522],{"class":331},[283,841,842,844,846],{"class":285,"line":525},[283,843,528],{"class":324},[283,845,328],{"class":302},[283,847,533],{"class":331},[283,849,850],{"class":285,"line":536},[283,851,539],{"class":324},[283,853,854,856,858],{"class":285,"line":542},[283,855,545],{"class":302},[283,857,14],{"class":306},[283,859,309],{"class":302},[283,861,862,864,866],{"class":285,"line":552},[283,863,555],{"class":302},[283,865,117],{"class":306},[283,867,309],{"class":302},[283,869,870,872,874],{"class":285,"line":562},[283,871,455],{"class":302},[283,873,467],{"class":306},[283,875,309],{"class":302},[283,877,878,880,882],{"class":285,"line":568},[283,879,376],{"class":302},[283,881,406],{"class":306},[283,883,309],{"class":302},[10,885,886,887,126],{},"And, lo and behold, this makes the footnote behave perfectly in ",[14,888,51],{"href":49,"rel":889},[18],[54,891,893],{"id":892},"how-do-feed-readers-process-this","How do feed readers process this?",[10,895,896,897,902,903,908],{},"Just to find out how my feed reader of choice actually processes this, I found the ",[14,898,901],{"href":899,"rel":900},"https://github.com/Ranchero-Software/NetNewsWire/blob/05c27b188c17194e92d412ebea9d49791e9ad49d/Shared/ArticleRendering/main.js#L147",[18],"footnote linking logic"," and the ",[14,904,907],{"href":905,"rel":906},"https://github.com/Ranchero-Software/NetNewsWire/blob/05c27b188c17194e92d412ebea9d49791e9ad49d/Shared/ArticleRendering/newsfoot.js#L160",[18],"backlink logic"," in their source code.",[10,910,911],{},"It appears like there's multiple conventions, so it's kind of a mess.\nIt would be nice if there'd by a standardised way of doing footnotes.",[10,913,914,915,920,921,926,927,930],{},"There are W3C-recommended ARIA roles called ",[14,916,919],{"href":917,"rel":918},"https://www.w3.org/TR/dpub-aria-1.0/#doc-footnote",[18],"doc-footnote"," and ",[14,922,925],{"href":923,"rel":924},"https://www.w3.org/TR/dpub-aria-1.0/#doc-backlink",[18],"doc-backlink"," that appear to be intended for this purpose, but they don't seem to be supported in ",[43,928,929],{},"any"," feed reader app to my knowledge.",[54,932,934],{"id":933},"conclusion","Conclusion",[10,936,937],{},"This was a bit of a pain to properly set up, but now I'm glad that it works.",[10,939,940],{},"Let me know if you have any other insights into this, I would be happy to hear it.",[406,942,945,950],{"className":943,"dataFootnotes":29},[944],"footnotes",[54,946,949],{"className":947,"id":28},[948],"sr-only","Footnotes",[467,951,952],{},[117,953,955,956],{"id":954},"user-content-fn-1","This is an example of such a footnote. It allows the author to add additional information to their content, without cluttering the main text. Notice how the footnote (when viewing them at the bottom of the article) also links back to the position in the main text where it is linked. ",[14,957,962],{"href":958,"ariaLabel":959,"className":960,"dataFootnoteBackref":29},"#user-content-fnref-1","Back to reference 1",[961],"data-footnote-backref","↩",[964,965,966],"style",{},"html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":29,"searchDepth":293,"depth":293,"links":968},[969,970,971,972,973,974],{"id":56,"depth":293,"text":57},{"id":105,"depth":293,"text":106},{"id":221,"depth":293,"text":222},{"id":892,"depth":293,"text":893},{"id":933,"depth":293,"text":934},{"id":28,"depth":293,"text":949},"2025-02-07","How to generate HTML to make footnotes compatible with RSS and Atom feed readers.","md",{},"/posts/getting-footnotes-right-in-the-feeds",{"title":5,"description":976},"2.posts/20250207.Getting Footnotes Right in the Feeds",null,"pbpI6cUw9OHbCEHO3fxHv4ohHb5AmlAIMbyVuB40rXk",1775234851922]