audrey.feldroy.com

The experimental notebooks of Audrey M. Roy Greenfeld. This website and all its notebooks are open-source at github.com/audreyfeldroy/audrey.feldroy.com


# Creating an Accessible Inline Nav FastTag

by Audrey M. Roy Greenfeld | Thu, Feb 6, 2025

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>



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>



nav((ul((li((a(('audrey.feldroy.com',),{'href': 'https://audrey.feldroy.com/'}),),{}), li((a(('Source',),{'href': 'https://github.com/audreyfeldroy/audrey.feldroy.com'}),),{})),{}),),{})

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'})]

Improve Accessibility

To follow accessibility best practices, including making this useful to screen readers:

  1. Descriptive aria-label on Nav
  2. Visual space between inline list items
  3. 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)

© 2024-2025 Audrey M. Roy Greenfeld