Skip to content
EldanCSS

Docs

Components

Articles, details, navigation, dialogs, and popovers.

Most components are just elements. EldanCSS adds structure without requiring a wrapper class.

Articles

article gets a surface, a border, and padding. Use its header and footer for supporting metadata.

<article>
  <header>
    <h3>Field notes</h3>
    <p>Published August 12</p>
  </header>
  <p>A short piece of standalone content.</p>
  <footer>
    <small>3 minute read</small>
  </footer>
</article>

Field notes

Published August 12

A short piece of standalone content. The surface and border keep it separate from the prose around it.

3 minute read

Details and accordions

details and summary provide disclosure without JavaScript. The summary row is the control; the plus and minus marker shows the current state.

<details>
  <summary>What is a classless stylesheet?</summary>
  <p>A stylesheet that targets semantic elements instead of requiring utility classes.</p>
</details>
What is a classless stylesheet?

A stylesheet that targets semantic elements instead of requiring utility classes.

Does it need JavaScript?

No. The stylesheet is CSS only. This documentation uses a small script for the interactive examples.

Figures

<figure>
  <img src="/image.jpg" alt="Description">
  <figcaption>An optional caption.</figcaption>
</figure>

Images and SVGs are fluid. Figures remove the default inline margin, and captions use the muted text color.

An inline SVG figure with a caption.

Progress and meter

<progress value="68" max="100">68%</progress>
<meter value="0.62">62%</meter>

Both controls use the accent color and fill the available width.

68% 62%

A nav containing a list becomes a horizontal menu. Give each nav an accessible name, and mark the current item with aria-current="page".

<nav aria-label="Primary">
  <ul>
    <li><a href="/" aria-current="page">Home</a></li>
    <li><a href="/docs/">Docs</a></li>
    <li><a href="/demo/">Demo</a></li>
  </ul>
</nav>

Nested lists switch to a vertical layout for simple submenus. A full sidebar can override the top-level list with display: grid or display: block in your own CSS.

Loading

Set aria-busy="true" on the region that is working. The stylesheet changes the cursor and appends an animated ellipsis that uses the current text color and keeps the label in place.

<button type="button" aria-busy="true">Saving</button>

<p aria-busy="true">Fetching entries</p>

Fetching entries

Dialogs

A dialog is either modal or non-modal. Modal dialogs make the rest of the page inert and trap focus; non-modal dialogs leave the page interactive. The stylesheet styles both.

PatternModalJavaScript
<dialog> + showModal()YesYes
<dialog popover> + popovertargetNoNo
<dialog> + command="show-modal"YesNo (Baseline 2025)
<dialog aria-labelledby="dialog-title">
  <h2 id="dialog-title">Dialog title</h2>
  <p>Native dialog content.</p>
  <form method="dialog">
    <button>Close</button>
  </form>
</dialog>

showModal() opens the dialog modally: focus moves inside, the rest of the page becomes inert, and Esc closes it. The demo below uses a tiny script to call it.

Dialog title

Native modal dialog content, styled with the same tokens as the rest of the page.

For a JS-free modal, the Invoker Commands API offers a declarative equivalent:

<button type="button" commandfor="dialog-id" command="show-modal">Open dialog</button>

<dialog id="dialog-id" aria-labelledby="dialog-title">
  <h2 id="dialog-title">Dialog title</h2>
  <p>Native modal dialog content.</p>
  <button type="button" commandfor="dialog-id" command="close">Close</button>
</dialog>

commandfor and command are Baseline 2025 newly available. Browsers without them will not open the dialog, so the script above remains the portable path.

Non-modal dialog

Add popover to a dialog and open it with popovertarget. The browser handles opening, light dismissal, and the top layer without a script. The dialog keeps dialog semantics but is non-modal: the page stays interactive, focus is not trapped, and the backdrop is transparent by default.

<button type="button" popovertarget="dialog-id">Open dialog</button>

<dialog popover id="dialog-id" aria-labelledby="dialog-title">
  <h2 id="dialog-title">Dialog title</h2>
  <p>Non-modal dialog content.</p>
  <button type="button" popovertarget="dialog-id" popovertargetaction="hide">Close</button>
</dialog>

Dialog title

This dialog is non-modal: the page stays interactive and focus is not trapped.

Popovers share the dialog surface but intentionally keep ::backdrop transparent, so menus and other popovers do not dim the page. To give a specific non-modal dialog a modal-like backdrop, style its ::backdrop:

#dialog-id::backdrop {
  background: hsl(var(--hue) calc(20% * var(--saturation)) 5% / 0.65);
}

The same hook can blur the page with backdrop-filter instead of dimming it. A dimmed backdrop does not make the dialog modal; modality comes from showModal() or command="show-modal", not from the backdrop. The demo above opts in with docs-only CSS; EldanCSS does not apply a backdrop to every popover.

Popovers

A popover is a native, non-modal surface. Unlike a modal dialog, it does not trap focus or make the rest of the page inert. Add popover to an element and point a button at it with popovertarget. The browser opens it in the top layer and closes it on outside click or Esc; no script is required.

<button type="button" popovertarget="menu">Menu</button>

<nav popover id="menu" aria-label="Menu">
  <ul>
    <li><a href="/docs/">Docs</a></li>
    <li><a href="/demo/">Demo</a></li>
  </ul>
</nav>

Popovers share the dialog surface. A nav inside a popover becomes a vertical menu, so the same semantic markup works as a small navigation panel. Unlike modal dialogs, popovers have a transparent ::backdrop by default; see the non-modal dialog example above for an opt-in dimmed backdrop.

Popovers are centered by default. For a menu anchored to its trigger, add CSS anchor positioning in your own styles; the stylesheet does not depend on it.