Creating an Accessible Inline Nav FastTag
I make a lightweight vanilla InlineNav FT with FastHTML, using the HTML nav element and as minimal styling as I can get away with.
References
Setup
from fastcore.utils import *
from fasthtml.common import *
from fasthtml.jupyter import *
Basic HTML Elements
This generates an HTML nav element:
Nav()
<nav></nav>
Within a nav element, screen readers handle lists best.
nv = Nav(
Ul(
Li(A("audrey.feldroy.com", href="https://audrey.feldroy.com/")),
Li(A("Source", href="https://github.com/audreyfeldroy/audrey.feldroy.com"))
))
nv
<nav>
<ul>
<li>
<a href="https://audrey.feldroy.com/">audrey.feldroy.com</a> </li>
<li>
<a href="https://github.com/audreyfeldroy/audrey.feldroy.com">Source</a> </li>
</ul>
</nav>
show(nv)
Custom FastTags to Add Minimal CSS Styles
I often see Jeremy and Isaac simply overriding the existing FTs. I tried that at first, but it didn't feel right to me. Maybe in the future I'll switch to that pattern. Here I'll name them differently.
Mainly I didn't want to have style="display:inline"
twice.
def InLi(*c): return Li(*c, style="display:inline")
Then it felt natural to do one for the parent Ul, but not really necessary. Rather, it felt awkward not to give it a partner element.
def InUl(*c): return Ul(*c, style="list-style:none")
But now it feels awkward to not have an inline version of the main nav element. Hmm.
nv = Nav(
InUl(
InLi(A("audrey.feldroy.com", href="https://audrey.feldroy.com/")),
InLi(A("Source", href="https://github.com/audreyfeldroy/audrey.feldroy.com"))
)
)
show(nv)
I guess I could do it this way:
nv = Nav(
Ul(
InLi(A("audrey.feldroy.com", href="https://audrey.feldroy.com/")),
InLi(A("Source", href="https://github.com/audreyfeldroy/audrey.feldroy.com")),
style="list-style:none"
)
)
show(nv)
Or maybe this way:
def InlineNav():
return Ul(
InLi(A("audrey.feldroy.com", href="https://audrey.feldroy.com/")),
InLi(A("Source", href="https://github.com/audreyfeldroy/audrey.feldroy.com")),
style="list-style:none"
)
nv = InlineNav()
show(nv)
Let's refactor to pull out the parts that matter:
navlinks = L(
("audrey.feldroy.com", "https://audrey.feldroy.com/"),
("GitHub repo for this site", "https://github.com/audreyfeldroy/audrey.feldroy.com")
)
def InLi(linktuple):
txt, href = linktuple
return Li(A(txt, href=href), style="display:inline")
navlinks.map(InLi)
(#2) [li((a(('audrey.feldroy.com',),{'href': 'https://audrey.feldroy.com/'}),),{'style': 'display:inline'}),li((a(('GitHub repo for this site',),{'href': 'https://github.com/audreyfeldroy/audrey.feldroy.com'}),),{'style': 'display:inline'})]
def InlineNav(navlinks):
return Nav(Ul(
*navlinks.map(InLi),
style="list-style:none"
))
nv = InlineNav(navlinks)
show(nv)
Improve Accessibility
To follow accessibility best practices, including making this useful to screen readers:
- Descriptive
aria-label
onNav
- Visual space between inline list items
role="navigation"
(redundant with<nav>
but helps old assistive tech)
def InLi(linktuple):
txt, href = linktuple
return Li(A(txt, href=href), style="display:inline;margin-right:1em")
def InlineNav(navlinks):
return Nav(
Ul(
*navlinks.map(InLi),
style="list-style:none;padding-left:0"
),
aria_label="Main navigation",
role="navigation"
)
nv = InlineNav(navlinks)
show(nv)