Usually.

The text “inaccessible” with “CSS-only” jammed into the middle to spell “inacCSS-onlyible”.
I originally titled this InacCSS-onlyible. I even made this typographically, er, distinct image. Then I realized it was silly and will instead use the neologism in a talk so I can hear the groans IRL.

Interactive widgets powered with only CSS are relatively common as people are playing with all the ways CSS can respond to, or create, interactions. CodePen contests are a great venue to see these experiments (which hopefully are never moved to a live human-facing project).

The problem with many of these examples is they need to convey their state (such as expanded or not), properties (think of relationships), or values, and (sometimes) dynamic names. While CSS is ideal to show these visually, they need to be passed to accessibility APIs so they can be conveyed to users of assistive technologies.

Not everything that relies on CSS for interaction is inaccessible. There are always going to be exceptions, but those are (and should be) rare.

Examples

I know folks do better with examples, so I have grabbed common ones.

Disclosures

It is very common to see a checkbox used as a trigger to hide or show content. Its use in “hamburger” navigation is sadly common, but I often see it with arbitrary expando patterns, such as this fork of an image gallery I made more accessible than its source:

See the Pen Forked: A more accessible NOT CSS-only expanding gallery by Adrian Roselli (@aardrian) on CodePen.

Originally this was coded with a checkbox. A screen reader user would have no idea why there is a checkbox. Since the images are displayed even in the collapsed state (as black boxes), a sighted screen reader user could be confused why their alternative text is announced. A keyboard only user may wonder why it does not fire on Enter.

By changing the trigger to a <button> with aria-expanded, a screen reader user will know the trigger is for a disclosure and it fires on Enter. By adding aria-hidden="true" when collapsed, those images will not be exposed to that same user. This particular design means simply toggling a hidden HTML attribute or the CSS-only display: none will not do the job.

In short, script is needed to manage the expanded or collapsed state as well as the programmatic exposure of the images when reduced to tiny icons. It is a simple disclosure widget with an extra requirement.

Please do not use this example for a real project. It has other issues; I created it simply to demonstrate how easily a CSS-only widget can be tweaked for a baseline of WCAG conformance and a modicum of accessibility affordances.

Update 31 March 2023: A popular tweet demonstrating CSS trigonometry functions uses a non-ideal expando widget. I forked it and made it into an accessible disclosure widget (notes in the code). Scott O’Hara went further and rebuilt it as a popover using the Chrome-only OpenUI proposal. A missed opportunity for OP to demonstrate a pattern which OP has championed.

Toggles

This example is a control to toggle dark or light mode (from a site offering copy-pasta-ready code) that is also quite inaccessible:

See the Pen Inaccessible toggle by Adrian Roselli (@aardrian) on CodePen.

This fake toggle uses a hidden checkbox, so it cannot be activated with the keyboard. This also means a sighted screen reader user cannot activate it without grabbing a mouse. Bear in mind that the code in this sample is scraped from the demo, not from the downloadable package(s).

It is fine to use a checkbox as a toggle, incidentally. Though maybe save it for when you plan to progressively enhance the control and/or flipping the toggle will only take effect when the user submits it. In other words, plan to add a submit button when using a checkbox.

The site also offers a button version of the control, but that button does not convey its state. For that to happen, it would need to use aria-pressed and alternate its value between true and false. Then you hang styles off it, visually reinforcing its programmatic state.

I tend to prefer a button as a toggle in this scenario if you can count on JavaScript being available and flipping the toggle has an immediate effect. For most theme toggles on sites, this is the preferred approach.

As an aside, as of this writing my site uses buttons for the theme toggle. If JavaScript is not available, it uses links with a query string which spares the user having to also press a submit button.

Update 18 May 2024: While not a toggle, using :has() with select menus brings the same risk as the checkbox hack. While articles like Combining CSS :has() And HTML <select> For Greater Conditional Styling are nice demonstrations of CSS, they should not be used in production without caution. I left a comment saying as much.

Sorting

I have seen a few efforts using CSS to sort content. They generally follow variations on re-ordering the visual layout using the order property or repositioning items in a grid track.

The following video captures an experiment from 2018 that visually re-orders rows in a table. It demonstrates it as experienced in JAWS using table navigation.

Visually the table rows are sorted, but their row position announcement and navigation order never changes.

From an interface standpoint, an accessible sortable table would use aria-sort on a column header to convey its current sort state. The header cell would also include a control that can be operated by keyboard (a <button>, for those keeping score).

Programmatically, the sorting operation would restructure the DOM so that the new first row would become the first row in the DOM, the new second row moves into the second row spot, and so on. Otherwise you have both meaningful sequence and focus order WCAG violations.

Marquee

Added 2 December 2023. It is relatively simple to use CSS to create all sorts of animations. A marquee, recreating the classic and terrible <marquee> element, is probably the first place most people start as they teach themselves the CSS syntax. While you can use a prefers-reduced-motion feature query to prevent any animation for users with that system-level preference, it is useless for those who still want animations but want to pause or stop this specific case on a page.

It is also definitely not a carousel (or slider or whatever the kids call it). And adding aria-hidden will not make it accessible, despite what the lead design engineer at Vercel (and Twitter blue-check) casually asserts:

0 javascript infinite carousel

– the trick is to duplicate the items
– for a11y, add `aria-hidden` to all the duplicated items

codepen 👇 pic.twitter.com/CongykLVex

Video description

A 5 second video showing a row of boxes, each made up of an illustrated head, a bold role name (designer or developer) under it, below that the text “Hire pre-vetted developers directly from your dashboard,” and below that is a large bold dollar value alongside a smaller “/hr”. These cards slowly slide left, revealing one more on the right as the one on the left disappears.

He does not provide a CodePen for his example, but he did link someone else’s 2017 demo that makes no accessibility claims (and so should not be shamed). However, the video he made clearly shows structured data, no ability to pause or stop animation, and (if those avatars are or ever become links) a problematic experience for screen reader users. While my expectations for Vercel code quality are incredibly low, I was disappointed to see CodePen boost this.

Sadly, it is not uncommon for accessibility claims to be wrong. I just wish they weren’t promoted when they are so easily disproven.

Looper

Added 3 May 2024. Google Chrome has a video about radio button groups that its author is calling The Looper Mini Web Machine. A core feature of groups of radio buttons is that they are operable with arrow keys by default. Another core feature is that they are form controls, meant to gather information from users and then submit to a process. This video is promoting them strictly as a UI widget.

See the Pen Radiento – Bento Radio Group Carousel thing by Adrian Roselli (@aardrian) on CodePen.

That is only one of the examples, but it demonstrates the risk of taking the video’s advice at face value. When I forked it, I removed the script that handles the view transitions since I am focusing on the CSS and HTML (the script only animates the change via view transitions).

The hit area not matching the visual box is a usability problem. The missing group label (the absent <legend>) is a 1.3.1 Info and Relationships violation and the shifting position of the currently selected radio button can be argued as a 3.2.2 On Input failure (depending on broader context of use). That sudden shift can be a problem for magnifier users as well. Sadly, this author has a history of WCAG-violating advice on behalf of Google, and Google continues to provide a platform, so I expect more problematic content in this “web machine” series by Google.

In short, if you want arrow key support for a widget then use script. See ARIA grids, tab lists, listboxes for scripting examples, even if the roles aren’t a fit. Taking a form control and using CSS to turn it into a contextually different widget only risks WCAG violations and confused users.

Tabs

Added 19 June 2024. This example is from 2020, but it came my way today when someone asked about this “accessible” pure CSS set of tabs. It uses radio buttons to hide and show content. Two immediate clues this may not satisfy its “accessible” claim:

I am ignoring the unnecessary tabindex on the heading and paragraph as well as its value of 1.

See the Pen Pure CSS Accessible Tabs by Adrian Roselli (@aardrian) on CodePen.

Animated <details>

Added 7 March 2026. An author has promised smoothly animated <details> using no JavaScript. This is a common request, and I know tiny animations can be compelling. But until interpolate-size is supported by more than Chromium, script may be the best way for all users. I argue it’s far better than making the disclosed content a peer to the <details> instead of nested within it. Definitely better than using aria-describedby to associate them. And I have no idea why you’d want term and definition roles folded into the mix. But here we are, offered by someone who makes this claim: Screen readers? Fully supported. Which, sure, if by “supported” you mean the arrow glyph gets announced along with the content that’s visually hidden.

See the Pen Untitled by Adrian Roselli (@aardrian) on CodePen.

Wrap-up

Any person, post, article, tweet, expert, fortune cookie, company, etc., that claims its CSS-only interactive widget is accessible is probably wrong.

While it may not be intentional, it is incumbent on you to confirm the accessibility because the person making the claim often has not.

Talk Reference (Added 7 July 2024)

This post was mentioned at CSS Day 2023.

YouTube: That’s not how I wrote CSS 3 years ago | Manuel Matuzović | CSS Day 2023.

2 September 2025

Chris Ferdinandi comes to a similar conclusion in CSS-only solutions are not accessible. His example is site navigation that operates on hover (I have a similar example above with the gallery disclosure). However, I recommend against his approach of using <details> and <summary> since a find-in-page (Ctrl+F) will expand them with a match. Use a link and disclosure widget pattern instead.