CSS-only* UI Components

UI components that once seemed to require JavaScript—often built with frameworks like React—are now possible with nothing more than HTML and CSS.

The web platform has grown dramatically, with modern CSS introducing features like scroll snapping, the <details> element, and the native <dialog> API. These tools let us build interactive patterns that used to demand JavaScript, while keeping our code lighter and easier to maintain.

This isn’t about replacing JavaScript entirely, but about recognizing where the platform already gives us what we need. Every time we lean on native HTML and CSS instead of shipping extra JS, we get better performance, simpler code, and improved accessibility for free.

Carousel

This carousel looks and feels like something you’d normally reach for JavaScript to build, but it’s powered entirely by modern CSS.

  • Scrolling & snapping — The cards sit in a horizontal strip you can swipe or scroll through. CSS scroll snapping keeps each card neatly centered as you stop.
  • Navigation buttons — Instead of custom JavaScript, the browser provides built-in scroll buttons you can style directly with CSS. They appear at the edges and let you step through smoothly.
  • Indicators (dots) — Markers show where you are in the carousel, also handled through CSS pseudo-elements. No extra markup or scripting needed.
  • Responsive by default — Because it’s flexbox + aspect ratio, the layout adapts gracefully to small or large screens.

In short: the browser’s layout and scrolling engine is doing all the work. We’re just layering on styles.

Note: If your browser doesn’t support newer CSS features like ::scroll-button and ::scroll-marker, you may not see the carousel controls or indicators. That’s okay — the content is still fully accessible as a scrollable strip. This is an example of progressive enhancement, where modern browsers get the upgraded experience, while older ones still get a usable fallback.

Accordion

This accordion is JS-free, built with native <details> and a shared name to ensure only one section is open at a time.

  • Semantic HTML: <details> / <summary> communicates “disclosure” to browsers, screen readers, and keyboards.
  • Built-in UX: Space/Enter toggles; Arrow keys move focus between summaries (browser-dependent).
  • Mutual exclusivity: Giving all <details> the same name attribute turns them into a group—opening one closes the others.
What is NostalgiaPHP?

A tiny flat-file CMS: Markdown in, HTML out—no DB, no build step.

How do I add a page?

Create content/pages/your-page/index.md with front-matter and content.

How do collections work?

Put items in content/collections/{name} as .md files (one per item).

Can I use templates and partials?

Yes—pick template: main (or your own), and reuse partials like header/footer.

Where can I deploy?

Anywhere PHP runs—shared hosting, Render, Netlify PHP adapters, etc.

I am a modal

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Repellat nulla ad nemo.

CSS Dialogs

The <dialog> element gives you a built-in way to create modals without JavaScript. Opening and closing is handled natively with .showModal() and .close() (so, a little JS, but let's move on), and the browser automatically adds a dimmed backdrop behind the dialog.

This means you don’t need to wire up ARIA attributes, focus trapping, or overlay click handling yourself — it’s all provided by the platform. You can still style the dialog and its backdrop to fit your design.

Note: Backdrop styling (via ::backdrop) is still inconsistent across browsers. Some support blur and custom colors, while others are limited.

Before/After Comparison

Another classic UI pattern that used to almost always rely on heavy JavaScript libraries is the before–after slider. In this version, CSS handles the actual reveal effect using clip-path and a custom property. JavaScript plays only a minimal role: wiring up the range input so its value updates the CSS variable.

It’s a much lighter approach than older libraries — most of the work is done natively in CSS, while JS just provides the bridge between user input and styling.

Before After

Tabs

Tabs let you organize related content into panels where only one is visible at a time. Traditionally, this required JavaScript to toggle states and hide/show panels. But with plain HTML and CSS, we can achieve the same behavior using radio inputs and labels.

Each tab is backed by a hidden radio input (so only one can be active at a time), and the labels act as the clickable tab headers. CSS then uses the :checked state to display the correct panel.

This approach is accessible by default (since radios are part of the native form controls), keyboard-friendly, and requires no scripting. It’s also easy to style so the tabs look like the traditional UI pattern users expect.

Overview

This is the overview panel.

Features

This is the features panel.

Pricing

This is the pricing panel.