Skip to content

## a copy-paste ui kit for the 2026 web platform

Every component ships 0 bytes of JavaScript.

Popovers, dialogs, tabs, toasts and carousels built from HTML + CSS only. The browser does the modality, the focus, the keyboard — nothing to download, parse, or break.

browse components → $ npx nojsui add

01 — proof

Open DevTools.
Count the scripts.

This page — live demos included — loads zero. Every demo runs on native HTML invoked the 2026 way: Popover API, anchor positioning, invoker commands, scroll-driven animations.

02 — the kit

28 components, running right now.

view all →

No JavaScript opened this. Click outside or press Esc to dismiss.

popover popovertarget

The invoker is the anchor — light dismiss, Esc and focus return are native.

Delete this project?

The page behind is inert, focus is confined here, and Esc returns focus to the button. The browser does all of it — this page loads no script.

modal dialog commandfor

Invoker Commands: modality, focus containment and return come from <dialog> itself.

Why zero JS?

Less to download, parse and break. The browser already knows how to do this.

Is it accessible?

Native semantics mean the keyboard and screen-reader behaviour come from the browser, not a reimplementation.

accordion details[name]

The browser enforces that opening one closes the others.

Filters

Pinned with margin-inline-start: auto. A top-layer dialog's containing block is the viewport, so that is the whole trick.

Share

No swipe-to-dismiss: CSS cannot close a dialog, and a gesture that only hid it would leave the page inert behind a sheet you think is gone.

drawer / sheet dialog

The same <dialog>, pinned to a viewport edge with logical margins.

Saves without leaving the page Hidden from the list, kept for 30 days

tooltip aria-describedby

Announced by screen readers whether or not the bubble is visible.

select appearance

A real <select> with a styleable picker. Arrow keys and form data are native.

Saved to your library

toast @starting-style

Appears, announced politely, removes itself on a timer — no script.

Install method

npx nojsui add tabs

Two files, straight out of the repository.

tabs radio group

One tab stop and arrow-key selection come from the browser.

this card rose into place
on a view() timeline

scroll reveal animation-timeline

Content rises into place on a view() timeline — visible without it.

carousel ::scroll-marker

Scroll-snap everywhere; arrows and dots generated by CSS.

Type something that is not an address, then click away.

text field :placeholder-shown

The label floats when the field has content, driven by the input's own state.

Notifications

checkbox / switch / radio accent-color

Three native controls, one look — the switch is a checkbox, so forms and keyboards work.

range <datalist>

A native slider with tick marks; a live value readout needs script, so it ships without one.

Depth, with no script

The backdrop lags the page on a view() timeline.

parallax band animation-timeline

Depth from a scroll() timeline, off entirely under reduced motion.

the bar across the top of this page
is this component, reading scroll()

progress <progress>

Styled native <progress> — the value comes from markup, never from script.

navigate anywhere on this site —
the header holds its place across the load

page transitions @view-transition

Cross-document view transitions: real navigations that animate, with no router.

Narrow column

Container queries

The card asks how much room it has.

Wide column — same markup

Container queries

Past its threshold the media takes a column and the body takes the rest. Nothing here knows which zone it is in.

responsive card @container

Lays itself out from the space it is given, not the width of the window.

button inline-grid

One button on a native <button> or <a>; the fill can be any color or image.

Enter an email address we can reply to.

alert @starting-style

A message that stays until you remove it — the counterpart to toast.

Studio

app bar scroll()

Site chrome that condenses as the page moves, on a scroll timeline.

section max-inline-size

Page rhythm, alternating surfaces and a centred measure — the page shell.

What we do

Interfaces that get out of the way

The one-sentence version, set larger and quieter.

prose max-inline-size

Eyebrow, heading, lede and body copy at a reading measure.

Zero JavaScript

Arrives on load

entrance animation-fill-mode

Content arrives on load — the one moment a scroll timeline cannot reach.

texture color-mix()

A fixed decorative backdrop that inverts with the theme on its own.

03 — why zero

/01

Less to break

Nothing to download, parse, or crash. The browser already knows how to open a dialog and trap focus.

/02

Accessible by default

Native semantics mean keyboard and screen-reader behaviour come from the browser, not a reimplementation.

/03

Degrades gracefully

Every component documents its fallback tier — usable all the way down to plain HTML.

Ship the platform, not a framework.

browse the components →