Skip to content

a11y: logos without alt text are handled inconsistently across formats #14660

Description

@cwickham

I have:

  • searched the issue tracker for similar issues
  • installed the latest version of Quarto CLI
  • formatted my issue following the Bug Reports guide

Bug description

Logo alt text is settable across formats — websites via logo-alt, typst and revealjs via logo: {path, alt}.
This issue is about the default when no alt is provided: the format pipelines disagree, and two of them produce inaccessible output rather than a safe fallback.

When a logo (navbar/sidebar logo, revealjs slide logo, or typst logo) is rendered without alt text, they emit:

Path Emitted markup for a logo with no alt Source
HTML website navbar <img … alt="" …> navbrand.ejs#L6-L9
HTML website sidebar <img … alt="" …> sidebar.ejs#L40-L43
revealjs slide logo <img …>alt attribute omitted format-reveal.ts#L409-L416
Typst title-page logo image(...)alt parameter omitted page.typ#L14

HTML defaults a missing logo alt to an empty alt=""; revealjs and Typst leave the attribute off entirely.

This matters because empty vs. absent alt are not equivalent for accessibility:

  • <img alt=""> is a valid decorative-image signal — assistive tech skips it, and axe-core's image-alt rule passes.
  • <img> with no alt attribute fails axe-core image-alt (tagged wcag2a/wcag111 → WCAG 2.2 SC 1.1.1 Non-text Content, Level A), and screen readers commonly announce the image filename instead.

So on the same input (a logo with no alt), a Quarto website is conformant while a reveal deck produces a Level A failure — and the Typst PDF's logo has no text alternative.

Investigation was AI-assisted, grounded in a local clone of this repo (source pinned to 50f3d27) and reproduced with Quarto 1.10.12, per CONTRIBUTING.md's note on using AI tools.

The design question

Fixing the inconsistency forces a decision about the intended default, which is really the point of this issue.
Our options are:

  1. Assume logos are decorative → emit alt="" everywhere. Make revealjs/Typst match HTML. Simplest; no broken output; passes axe. Downside: it silently passes — an author who forgot alt on a genuinely meaningful logo is never told, because alt="" conforms.
  2. Emit alt="" everywhere and warn at render time when a logo resolves with no alt, plus an explicit opt-out to mark a logo decorative and suppress the warning. Alerts every author at build time while keeping output safe. Downside: more work; needs a "decorative" flag and could be noisy for logos that legitimately sit next to visible title text.
  3. Something else?

I think omit alt as a default isn't an option. This leads to an outright 1.1.1 failure that will only get picked up if an author runs a check.

So at minimum revealjs and typst should stop omitting the attribute. The open question is whether the default should stay a silent alt="" (option 1) or gain a warning (option 2).

For context, logos are often decorative but not always. For example, a navbar logo that includes the site/organization name (that isn't otherwise in the navbar).

Steps to reproduce

---
title: "Logo alt test"
format: revealjs
logo: logo.svg
---

## Slide one

Content.

Render, then inspect the emitted logo:

quarto render deck.qmd
grep -oE '<img[^>]*slide-logo[^>]*>' deck.html

Actual behavior

Same input, no alt specified — divergent output:

revealjs:  <img src="logo.svg" class="slide-logo">                       # no alt
website:   <img src="./logo.svg" alt="" class="navbar-logo light-content">  # alt=""

The revealjs <img> has no alt attribute → axe-core image-alt violation (WCAG 1.1.1, Level A); screen readers may read logo.svg. The Typst pipeline likewise omits the alt: parameter on the title-page logo.

Expected behavior

Consistent handling across formats.

Your environment

  • IDE: Positron (CLI render; IDE-independent)
  • OS: macOS 26.5.1 (25F80)

Quarto check output

Quarto 1.10.12
[✓] Checking environment information...
      Quarto cache location: /Users/charlottewickham/Library/Caches/quarto
[✓] Checking versions of quarto binary dependencies...
      Pandoc version 3.8.3: OK
      Dart Sass version 1.87.0: OK
      Deno version 2.7.14: OK
      Typst version 0.14.2: OK
[✓] Checking versions of quarto dependencies......OK
[✓] Checking Quarto installation......OK
      Version: 1.10.12
      Path: /Applications/quarto/bin

[✓] Checking tools....................OK
      TinyTeX: v2026.04
      VeraPDF: 1.28.2
      Chrome Headless Shell: (not installed)

[✓] Checking LaTeX....................OK
      Using: TinyTex
      Path: /Users/charlottewickham/Library/TinyTeX/bin/universal-darwin
      Version: 2026

[✓] Checking Chrome Headless....................OK
      Using: Chrome found on system
      Path: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
      Source: MacOS known location

[✓] Checking basic markdown render....OK

[✓] Checking R installation...........OK
      Version: 4.6.0
      Path: /Library/Frameworks/R.framework/Versions/4.6/Resources
      LibPaths:
        - /Users/charlottewickham/Library/R/arm64/4.6/library
        - /Library/Frameworks/R.framework/Versions/4.6/Resources/library
      knitr: 1.51
      rmarkdown: 2.31

[✓] Checking Knitr engine render......OK

[✓] Checking Python 3 installation....OK
      Version: 3.12.2
      Path: /Users/charlottewickham/.pyenv/versions/3.12.2/bin/python3
      Jupyter: 5.9.1
      Kernels: python3

[✓] Checking Jupyter engine render....OK

[✓] Checking Julia installation...

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions