✨ Create high-converting landing pages at scaleExplore the AI Landing Page Builder
Performance & UX
·9 min read

Modern CSS Alternatives to JavaScript: Sliders, Dialogs, Accordions & More!

CSS has improved a lot over the years. With new specifications and better browser support, it can now handle animations, interactions, and UI components that previously required JavaScript. This means we don’t always need JavaScript for certain effects, which helps make websites faster and easier to maintain.

In this article, we will look at some common UI components that used to require JavaScript and show how you can now build them using modern CSS.

Why Replace JavaScript with CSS?

Replacing JavaScript with CSS where possible has several benefits:

  • Better performance: CSS is handled by the browser’s rendering engine, which is highly optimized for visual changes. This means animations and transitions run smoothly, even on old phones and laptops.
  • Faster load times: Less JavaScript means there are fewer scripts for browsers to download, parse, and execute, which reduces page load time.
  • Improved maintainability: Styling and visual effects stay in the stylesheet, making code cleaner and easier to manage.
  • Fewer dependencies: You can avoid relying on third-party libraries or frameworks for effects that CSS can handle natively.
  • Progressive enhancement: You can gracefully degrade features in older browsers without breaking your site’s core functionality.

Having learned about the benefits of using CSS (where and when possible) instead of JavaScript, let’s explore scenarios where CSS can fully replace JavaScript, starting with animations and transitions.

Accordion

An accordion is one of those website components you’ve probably seen a hundred times, including places like FAQs, menus, and settings panels. It's a stack of sections where each one can expand or collapse to reveal more content. Instead of showing everything all at once and overwhelming the user, the accordion helps you hide content till the user opens it.

JavaScript version

The JavaScript version of an accordion works by using event listeners on each accordion header. When a user clicks a header, JavaScript checks its current state and toggles an "active" CSS class. This class controls whether the accordion expands to show its content or collapses to hide it. Each click updates the state, allowing users to open and close sections dynamically.

CSS version

The CSS-only accordion uses hidden checkboxes and the :has() selector to handle interactivity. The checkbox acts as the “state manager”: when it’s checked, the accordion knows to expand, and when it’s unchecked, it collapses.

The checkbox’s label acts as the clickable element, and the ::after pseudo-element creates the plus sign that rotates when the section is opened.

Tooltips and popovers

Tooltips are small “text bubbles” that appear when you hover over or focus on something, like when you hover over an icon and a short description appears.

Popovers are a bit bigger. They’re like tooltips but can hold more content and have buttons or links inside. Both are ways to give users extra context without crowding the screen.

JavaScript version

Instead of relying on pseudo-elements or :hover, the JS version uses scripts to dynamically create, attach, and control tooltip elements for each trigger on the page.

CSS version

The tooltip is powered by the ::before and ::after pseudo-elements. The ::before holds the text itself, pulled straight from a data-tooltip attribute, while the ::after draws the little arrow pointing back to the trigger.

By default, both are invisible. When the user hovers over any button, the hover state kicks in, making the tooltip text appear.

CSS also handles positioning: The .top, .bottom, .left, and .right classes shift where the tooltip appears relative to the trigger, and the arrow automatically adjusts with it.

Modals

A modal is like a pop-up window that appears on top of the main content, usually to capture your attention for something important. You’ll often see it when a website wants you to confirm an action, log in, or read some extra information before continuing.

JavaScript version

This JavaScript code includes functions to open and close modals by toggling an "active" class, while also disabling page scrolling when a modal is open. The modals can be dismissed by clicking the overlay background, pressing the Escape key, or calling the closeModal function directly.

CSS version

This CSS-only modal setup uses a hidden checkbox and the :checked pseudo-class to control when a modal is shown. When the checkbox is checked, the overlay becomes visible and the modal scales into view with an animation.

Dialogs

A dialog is similar to a modal in that it also pops up on top of a webpage or application to grab your attention and ask you to interact with it.

The main difference is that while a modal often blocks interaction with the rest of the page until it is closed, a dialog can be either modal (blocking) or non-modal (allowing you to still interact with the page behind it). Dialogs are commonly used for alerts, confirmations, or collecting input from users.

JavaScript version

This JavaScript dialog system uses the native <dialog> element, which provides built-in modal functionality in modern browsers. The code defines reusable openDialog and closeDialog functions, letting you show or hide dialogs by their ID.

CSS version

This CSS-only dialog relies on selectors like :checked and :target to control when a dialog is shown. A hidden checkbox triggers the dialog to appear. The .dialog-overlay darkens and blurs the background, while the .dialog container transitions into view.

Carousels

Carousels are rotating banner that cycles through slides of content, whether images, text, or both. They’re often used to highlight promotions, products, or featured content. They are commonly seen on hero sections and other parts of a website.

JavaScript version

This JavaScript carousel works by keeping track of the current slide and moving the slides container using transform: translateX(). It sets up buttons for moving forward and backward, clickable indicators for jumping to specific slides, and a counter to show the current slide number.

The nextSlide and prevSlide methods update the index, while updateCarousel handles moving the slides, updating indicators, and enabling/disabling buttons.

CSS version

The carousel uses CSS features like :has() and :target. Each slide is tied to a hidden anchor (#slide1, #slide2, etc.), and when one of these is targeted, the transform: translateX() shifts the slides to show the right one.

Dropdown menus are another common website element. They are typically found in dashboards, navigation bars, or settings menus. A dropdown hides multiple options under a single label, saving space and keeping the interface clean. When clicked or hovered over, the list expands, allowing users to choose an item.

JavaScript version

The JavaScript dropdown menu uses a class-based setup to manage all its behavior. A button acts as the toggle, and when clicked, it opens or closes the menu by adding or removing a CSS class.

CSS version

The CSS-only dropdown works by using either a hidden checkbox or the :hover state to control when the menu is shown.

  • In the checkbox version, the input acts as the toggle. When it’s checked, CSS selectors (:checked ~ .dropdown-menu) apply styles that make the menu visible. The arrow icon also rotates to show the open state.
  • In the hover version, the menu appears when the user hovers over the button, and the arrow rotates at the same time.

First time here? Discover what Prismic can do!

👋 Meet Prismic, your solution for creating performant websites! Developers, build with your preferred tech stack and deliver a visual page builder to marketers so they can quickly create on-brand pages independently!

Dark mode switcher

Theme switchers are an optional but nice control you can add to your website. They let users customize the color scheme of the interface by toggling between light mode and dark mode. This small feature can improve accessibility, reduce eye strain, and make the experience more personal. While it's not essential for functionality, it adds a thoughtful touch that users often appreciate.

JavaScript version

The JavaScript dark mode switcher first checks if a saved preference exists in localStorage. If not, it falls back to the system’s color scheme preference.

When the user clicks the main switch or the alternative toggle, it switches the theme, updates the page with a data-theme attribute, and saves the preference for future visits.

CSS version

The CSS dark mode switcher works by using CSS variables for colors and themes, and a hidden checkbox to control whether light or dark mode is active.

By default, the :root variables define the light theme, while a @media (prefers-color-scheme: dark) rule applies the dark theme if the system is set that way.

The hidden checkbox (.theme-switcher-checkbox) lets users override this preference manually. When unchecked, the light theme variables are applied, and when checked, the dark theme variables take over.

Custom form inputs

Default form inputs often look plain and may not match your brand’s style. That’s where creating custom inputs comes in. By styling inputs yourself, you can add a personal touch that aligns with your brand guidelines, whether it’s changing colors, shapes, or interactions. This way, your forms align with the rest of your website’s design.

JavaScript version

The state of the custom inputs (checkboxes, radios, and toggles) is stored in a Map, which makes it easy to update and keep track of them. When a user clicks one of these inputs, the JavaScript code updates both the stored state and the visual styles.

CSS version

This CSS version of custom form inputs works by hiding the browser’s default checkboxes and radio buttons (opacity: 0; position: absolute;) and replacing them with styled elements (.custom-checkbox and .custom-radio).

Animations and transitions

Text animations

Texts don’t have to be boring. You can “bring them to life” by applying various effects, like fade in, slide in, bounce, color changes, typewriter, etc.

JavaScript version

The JavaScript code starts by selecting the text elements and buttons from the DOM, then defines the string to animate. When the typewriter starts, it resets any previous animation, disables the start button, and applies a blinking cursor effect on the right side of the text.

CSS version

This CSS-only setup relies on keyframes and other selectors to create the typewriter text animation. The .typewriter-text element starts with a hidden width and gradually reveals the text using the typing animation, which expands the width step by step while showing a blue border on the right as a cursor. After typing finishes, the blink animation takes over to create a blinking cursor effect.

Scroll animations

Scroll animations activate as the user moves through the page. Instead of everything loading at once, elements animate in as they come into view. This makes the experience more dynamic and keeps people scrolling because it feels like the page is “responding” to them.

JavaScript version

This JavaScript setup uses the Intersection Observer API to watch content sections. It applies a visible class when elements enter the viewport so they can smoothly fade in, and removes the class when they exit.

There’s also a scroll event listener that calculates how far the user has scrolled inside the container, then updates a progress bar’s width and a text indicator to show the percentage of scroll progress.

CSS version

This design uses CSS scroll-linked animations to track how far the user has scrolled and trigger visual effects without JavaScript.

At the top, a fixed progress bar gradually fills as the user scrolls down, powered by a progressFill animation linked to the scroll timeline. In the corner, a circular progress indicator rotates its border to show progress dynamically.

Alongside it, a percentage indicator updates its number using a counter that increases with scroll, giving a precise percentage readout.

Note: CSS handles simple animations well, but if you need physics-based movement, timelines, or fine-grained animation control, JavaScript libraries like GSAP are better.

Best Practices for Using CSS Instead of JavaScript

If you’re choosing CSS over JavaScript, here are a few best practices to make sure you’re doing it the right way:

  • Use semantic HTML + CSS for the base UI: Build the core layout and interactions with semantic HTML and CSS before adding JavaScript. This way, even if scripts fail to load, your site remains usable and visually consistent.
  • Always check browser support: Not all CSS features are fully supported across browsers. Before using something new, like advanced selectors, scroll snapping, or container queries, check Can I Use. This saves you from breaking layouts in older browsers.
  • Think about accessibility from the start: Don’t just replace JavaScript with CSS for the sake of it. Make sure interactions are accessible with a keyboard, screen readers, or other assistive tech. For example, CSS-only dropdowns might not always be fully accessible without ARIA attributes or JS support.
  • Use progressive enhancement: Start with a working CSS solution, then layer on JavaScript if you need more complex functionality. For example, you can use CSS for hover effects or transitions, then add JS if you need event tracking or dynamic updates.
  • Keep it maintainable. Just because CSS can do something doesn’t always mean it should. Don’t write overly complicated CSS hacks just to avoid JavaScript. Keep your styles clean and easy to maintain for future developers.
  • Test across devices. A pure CSS solution might work great on desktop but behave unexpectedly on mobile (e.g., hover-only menus). Always test your implementation across screen sizes and devices.

CSS vs JavaScript: Choosing the Right Approach

At the end of the day, neither CSS nor JavaScript is “better” across the board. It really depends on what you’re trying to build. CSS is great when you want something lightweight, fast, and easy to maintain. You don’t need to load extra scripts, and the browser does a lot of the heavy lifting for you. CSS is the right option for simple animations, styling changes, or toggling states, especially when combined with the appropriate HTML elements.

But there are times when CSS just isn’t enough. Maybe you need logic, user interaction, or more complex animations that depend on conditions. That’s where JavaScript shines. It gives you flexibility and control that CSS alone can’t provide.

The real trick is knowing when to use which or mix both of them. Think of CSS as your first line of defense. If it can do the job, go for it. If it can’t, then JavaScript is there to pick up the slack. Using both in balance is how you end up with solutions that look good and actually work the way you need them to.

FAQs

You can use websites like Can I Use. It shows detailed, up-to-date information on browser support for CSS features across all major browsers.

Article written by

Alison Brunk

Alison is a technical content strategist at Prismic. She is passionate about design and web development and loves learning new tools and frameworks. In her free time, she loves playing golf and painting.

More posts
A headshot of Alison Brunk

Join the discussion

Hit your website goals

Websites success stories from the Prismic Community

How Arcadia is Telling a Consistent Brand Story

Read Case Study

How Evri Cut their Time to Ship

Read Case Study

How Pallyy Grew Daily Visitors from 500 to 10,000

Read Case Study

From Powder to Pixels - Perfectly Planned Ski Vacations, Now Perfectly Digital

Read Case Study