
\documentclass[12pt]{article}
\usepackage[T2A,OT1]{fontenc}
\usepackage[a4paper, top=20mm, bottom=20mm, left=20mm, right=20mm]{geometry}
\usepackage[utf8]{inputenc}
\usepackage[russian, english]{babel}
\usepackage{tabu}
\usepackage{hyperref}
\usepackage{parskip}
\usepackage{graphicx}
\usepackage{tabularx}
\usepackage[normalem]{ulem}
\usepackage{float}
\floatstyle{boxed}
\restylefloat{figure}
\usepackage{setspace}
\onehalfspacing
\author{Artyom Bologov \href{mailto:new-menu@aartaka.me}{(email)}}
\date{\today}
\title{New Declarative Website Menu with Invoker Commands and noscript Hacks!}
\makeatletter
\def\endenv{\expandafter\end\expandafter{\@currenvir}}
\makeatother
\begin{document}
\maketitle

\includegraphics[width=\textwidth,height=\textheight,keepaspectratio]{./assets/new-menu.png}

So I read
\href{https://dbushell.com/2026/02/12/declarative-dialog-menu-invoker-commands}{David Bushell's post on Invoker Commands}
and it got me inspired!
(In short: Invoker Commands API is a way to send events and open dialogs without JS.)
I wanted to make a menu like that for my own site.
However, I had
\href{run:principles}{important principal requirements} for it:

\begin{itemize}\item It has to display an alternative full-length menu when JS is off or Invoker Commands are unsupported.
\item It should work on a multitude of screens.
\item It should be simple and HTML+CSS only.
\end{itemize}

This post is a description of a technique I settled on.

\section*{noscript detection in CSS} \label{noscript}

So a question I asked David when I planned for this post was quite hard.
How do I detect if JS or Invoker Commands are on, using pure CSS?

\paragraph{Why ``JS or Invoker Commands''} \begin{quote}
This sure has some minor corner-cases.
But the heuristic is: if it supports JS, then it's one of the major browsers.
And if it's one of the major browsers, it does support Invoker Commands too.
\end{quote}

David did not have a solution, and I don't blame him.
It's a non-goal for him to make the website fully accessible to noJS people.
But my goal is!

\href{https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/noscript#usage_notes}{So noscript tag reference is quite quirky}:
it interprets its content as HTML when JS is off.
And when JS is on, it interprets it as… plaintext.
Which is really weird, but it actually opens a way for detection of noscript.
In short, you have to add an easily identifiable element inside \verb|noscript|.

\begin{figure}[h!]\begin{verbatim}
<noscript>
<span id=noscript></span>
</noscript>
\end{verbatim}\caption{An element that only exists when JS is off}\end{figure}

And then check its presence in CSS with a \verb|:root:has( #noscript)| pseudo-class.
With that, the sky is the limit.
We can, say, define a CSS class that only shows when JS is on:

\begin{figure}[h!]\begin{verbatim}
:root:has( #noscript) > .script {
    display: none;
}
\end{verbatim}\caption{The logic is inverted, but .script displays by default, so no need to spell the positive case out}\end{figure}

\section*{Making HTML for the Menu and the Dialog} \label{html}

Now that we have a way to detect noJS browsers, we can get to writing our HTML and CSS. First, the easy part: creating the long menu:

\begin{figure}[h!]\begin{verbatim}
<nav>
  <a href="/">Artyom's Chaotic Blog</a>
  <span id=nav-links>
    <a href="/rss.xml">(RSS)</a>
    <a href="/about.html">About & Contacts</a>
    <!-- ... -->
  </span>
</nav>
\end{verbatim}\caption{Fill in yours!}\end{figure}

(I leave the root link out of it, because it should be displayed at all times.)

Then we can create the \verb|dialog| that will be displayed.
It has to be keyboard-accessible and easy enough to use.
Thus the structure:

\begin{figure}[h!]\begin{verbatim}
<dialog id=menu-dialog closedby=any>
<form formmethod="dialog">
  <button>Close menu</button>
</form>
<ul>
  <li> <a href="/">Artyom's Chaotic Blog</a>
  <li> <a href="/rss.xml">(RSS)</a>
  <-- ... -->
</ul>
</dialog>
\end{verbatim}\caption{Note the form with close action---there has to be a way to close the dialog}\end{figure}

The ID of the \verb|dialog| is useful in creating the menu button:

\begin{figure}[h!]\begin{verbatim}
<nav>
  <!-- Accessibility essential skiplink, include yours too! -->
  <a class=skiplink href=#maincontent>Skip to content</a>
  <a href="/">Artyom's Chaotic Blog</a>
  <!-- ... -->
  <button id=menu-button commandfor=menu-dialog command=show-modal>Menu</button>
</nav>
\end{verbatim}\caption{Menu button using Invoker Commands}\end{figure}

This button toggles the \verb|dialog| on whenever pressed.

\section*{Supporting CSS} \label{css}

Now the tasty (and slightly frightening) stuff: CSS!
First, we only decide to show the menu button on smaller screens:

\begin{figure}[h!]\begin{verbatim}
/* Adjust the width at your convenience */
@media (min-width: 500px) {
    #menu-button {
        display: none;
    }
}
\end{verbatim}\caption{We do not need the menu on larger screens}\end{figure}

Then, we have to deal with the fact that some browsers display the dialog unconditionally:

\begin{figure}[h!]\begin{verbatim}
dialog {
    /* ... */
    display: none;
}
dialog:modal {
    display: block;
}
\end{verbatim}\caption{iOS, I swear I'll…}\end{figure}

And, lastly, we have to hide the long list of links when on smaller screens and when Invoker Commands are likely on:

\begin{figure}[h!]\begin{verbatim}
/* Adjust the width at your convenience */
@media (max-width: 500px) {
    :root:not(:has( #noscript)) nav #nav-links {
        display: none;
    }
}
\end{verbatim}\caption{Do not show nav when JS is on and the screen is narrow}\end{figure}

This has a scary selector, true.
But it's actually
\href{https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement}{progressively enhanced}!
Whenever these pseudo-classes are unsupported, links simply stay displayed.
So noJS browsers and older browsers get this list of links.
That's basically it!

\section*{Corner-cases and Problems} \label{corner-cases}

There are three main problems with this approach:

\begin{itemize}\item The menu button stays visible for all small screens at all times.
  Given that there's no way to detect Invoker Commands support in CSS, I cannot hide it when unsupported.
  This is, however, only a problem for browsers that don't have Invoker Commands support.
\item Invoker Commands API is not supported in iOS 18 (which I use,) only in iOS 26.
  So my own phone lacks navigation when JS is on (it usually isn't.)
  This should not be a problem, as most mobile browsers nowadays have support for Invoker Commands.
  And browsers that don't---they likely don't support JavaScript, which means fallback link list is displayed there.
\item Again, this approach relies on the heuristic ``if JS is on, Invoker Commands are likely supported.''
  This is imperfect, but practical enough for me.
\end{itemize}

\section*{Show Me Your Menus!} \label{show}

You can always send me an email via the feedback form below or
\href{run:about}{contact me elsewhere (section contacts)}.
Care about your noJS and mobile users and be kind to people 💙


\par\noindent\rule{\textwidth}{0.4pt}
\href{https://creativecommons.org/licenses/by/4.0}{CC-BY 4.0} 2022-2026 by Artyom Bologov (aartaka,)
\href{https://codeberg.org/aartaka/pages/commit/a91befa}{with one commit remixing Claude-generated code}.
Any and all opinions listed here are my own and not representative of my employers; future, past and present.
\end{document}
