Accessible UI Component Library for Vanilla JS & React – Monochrome
It currently includes 4 prebuilt UI components (accordion, collapsible, menu, and tabs) and works with any server-rendered stack, static site generator, or React application.
The library uses the DOM as its source of truth. It reads and writes ARIA attributes directly, with no per-component state and no framework dependency at runtime.
It’s ideal for teams building content-heavy or server-rendered pages who need real accessibility compliance at a near-zero runtime cost.
monochrome/react package provides React components that generate the correct HTML structure and IDs.hidden="until-found" attribute keeps collapsed content discoverable via Cmd+F in the browser.hidden="until-found". Supports all current major browsers.1. Install Monochrome with the package manager you prefer.
# Using npm npm install monochrome # Using pnpm pnpm add monochrome # Using bun bun add monochrome # Using yarn yarn add monochrome
2. Import the runtime once at your application’s entry point.
// Entry point: index.js, main.js, or app.js // This registers the global event listeners that power every component import "monochrome";
3. For projects that have no build step, use the CDN script tag:
<!-- Add this to your <head> or before </body> --> <!-- Works in PHP, Rails, Django, WordPress, or plain HTML files --> <script defer src="https://unpkg.com/monochrome"></script>
4. Create your own UI components. Monochrome identifies which elements to coordinate using a structured ID prefix system. You do not call any initialization function. The library reads these IDs on interaction and responds accordingly.
| Prefix | Role | Example ID |
|---|---|---|
mct: | Trigger (the button or clickable element) | mct:collapsible:faq-1 |
mcc: | Content (the panel that shows or hides) | mcc:collapsible:faq-1 |
mcr: | Root container (for grouped components) | mcr:accordion:product-faq |
The third segment of the ID (e.g., faq-1) is your unique identifier. It links the trigger to its content panel. You choose it; just keep it consistent within a component pair.
<!-- Accordion Component -->
<div id="mcr:accordion:faq" data-mode="single">
<div>
<h3>
<button id="mct:accordion:q1" aria-expanded="false" aria-controls="mcc:accordion:q1">
What is monochrome?
</button>
</h3>
<div id="mcc:accordion:q1" role="region" aria-labelledby="mct:accordion:q1" aria-hidden="true" hidden="until-found">
A minimal component library...
</div>
</div>
<div>
<h3>
<button id="mct:accordion:q2" aria-expanded="false" aria-controls="mcc:accordion:q2">
How does it work?
</button>
</h3>
<div id="mcc:accordion:q2" role="region" aria-labelledby="mct:accordion:q2" aria-hidden="true" hidden="until-found">
Components are server-rendered...
</div>
</div>
</div> <!-- Collapsible Component --> <button id="mct:collapsible:details" aria-expanded="false" aria-controls="mcc:collapsible:details"> Show more details </button> <div id="mcc:collapsible:details" aria-labelledby="mct:collapsible:details" aria-hidden="true" hidden="until-found"> This content is revealed when you click the trigger. </div>
<!-- Menu Component -->
<div id="mcr:menu:account">
<button type="button" id="mct:menu:account" aria-controls="mcc:menu:account" aria-expanded="false" aria-haspopup="menu">
Account
</button>
<ul role="menu" id="mcc:menu:account" aria-labelledby="mct:menu:account" aria-hidden="true" popover="manual">
<li role="presentation">Settings</li>
<li role="none"><button role="menuitem" tabindex="-1">Profile</button></li>
<li role="none"><button role="menuitem" tabindex="-1">Preferences</button></li>
<li role="separator"></li>
<li role="none"><button role="menuitem" tabindex="-1">Sign Out</button></li>
</ul>
</div> <!-- Tabs Component -->
<div id="mcr:tabs:demo" data-orientation="horizontal">
<div role="tablist" aria-orientation="horizontal">
<button
role="tab"
id="mct:tabs:t1"
aria-selected="true"
aria-controls="mcc:tabs:t1"
tabindex="0"
>
Overview
</button>
<button
role="tab"
id="mct:tabs:t2"
aria-selected="false"
aria-controls="mcc:tabs:t2"
tabindex="-1"
>
Features
</button>
</div>
<div
role="tabpanel"
id="mcc:tabs:t1"
aria-labelledby="mct:tabs:t1"
aria-hidden="false"
tabindex="0"
>
Overview content...
</div>
<div
role="tabpanel"
id="mcc:tabs:t2"
aria-labelledby="mct:tabs:t2"
aria-hidden="true"
hidden="until-found"
tabindex="-1"
>
Features content...
</div>
</div> 5. If you use React, you can import the optional wrapper components. These components generate the correct HTML structure automatically.
// Accordion
import { Accordion } from "monochrome/react"
<Accordion.Root>
<Accordion.Item>
<Accordion.Header>
<Accordion.Trigger>What is monochrome?</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel>A minimal component library...</Accordion.Panel>
</Accordion.Item>
<Accordion.Item>
<Accordion.Header>
<Accordion.Trigger>How does it work?</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel>Components are server-rendered...</Accordion.Panel>
</Accordion.Item>
</Accordion.Root> // Collapsible
import { Collapsible } from "monochrome/react"
<Collapsible.Root>
<Collapsible.Trigger>Show more details</Collapsible.Trigger>
<Collapsible.Panel>
This content is revealed when you click the trigger.
</Collapsible.Panel>
</Collapsible.Root> // Menu
import { Menu } from "monochrome/react"
<Menu.Root>
<Menu.Trigger>Account</Menu.Trigger>
<Menu.Popover>
<Menu.Label>Settings</Menu.Label>
<Menu.Item>Profile</Menu.Item>
<Menu.Item>Preferences</Menu.Item>
<Menu.Separator />
<Menu.Item>Sign Out</Menu.Item>
</Menu.Popover>
</Menu.Root> // Tabs
import { Tabs } from "monochrome/react"
<Tabs.Root defaultValue="overview">
<Tabs.List>
<Tabs.Tab value="overview">Overview</Tabs.Tab>
<Tabs.Tab value="features">Features</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="overview">Overview content...</Tabs.Panel>
<Tabs.Panel value="features">Features content...</Tabs.Panel>
</Tabs.Root> The post Accessible UI Component Library for Vanilla JS & React – Monochrome appeared first on CSS Script.
THE HAGUE, Netherlands (AP) — As U.S. and Israeli forces pounded Iran, and Tehran and its…
Americans don’t trust President Donald Trump when it comes to foreign policy, a Reuters/Ipsos poll…
If you own an old car without Bluetooth and you're looking for a cheap and…
2026 has already seen surges in the cost of RAM and GPUs. Unfortunately, this also…
A gas pump is seen in a vehicle on Nov. 26, 2025, in Austin, Texas.…
A gas pump is seen in a vehicle on Nov. 26, 2025, in Austin, Texas.…
This website uses cookies.