npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rintran720/cyberpunk-ui

v0.1.1

Published

A neon-soaked, SSR-friendly, Tailwind-compatible cyberpunk CSS library. Class-based, zero-runtime, copy-paste friendly.

Readme

Cyberpunk UI

A neon-soaked, SSR-friendly, Tailwind-compatible CSS library — class-based, zero-runtime, copy-paste friendly. Drop into any framework. No JS shipped.

NIGHT  CITY  OS · v0.1
//  ALL  PACKETS  NOMINAL  //  LATENCY 12MS  //  CYBERSPACE  OPEN

Why

Most cyberpunk UI kits are heavy React widgets. This library is just CSS:

  • SSR-safe — pure styles, no hydration boundary, works in React Server Components, Next.js app router, Remix, Astro, Hugo, plain HTML.
  • Class-based, not utility-soup — each component is a class (cp-btn, cp-card) with BEM-style modifiers (cp-btn--neon cp-btn--cut), so JSX stays readable.
  • Tailwind v4 native — color/font/shadow tokens are exposed via @theme, so bg-cp-cyan-500 and shadow-cp-glow-magenta just work.
  • Tailwind v3 preset — single require() and you get the same tokens.
  • Shadcn-ish ownership — files are small, well-commented, and copy-paste-friendly. You can either install the package or vendor the CSS files into your repo.

Install

npm / pnpm / bun

npm  i @rintran720/cyberpunk-ui
pnpm add @rintran720/cyberpunk-ui
bun  add @rintran720/cyberpunk-ui

Or vendor it: copy src/ into your project and import the files you need.

Fonts (optional but recommended)

Cyberpunk UI references three font families and falls back to system fonts. For the proper neon look load them once (via Google Fonts or self-host):

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&family=Orbitron:wght@500;700;900&family=Rajdhani:wght@500;700&display=swap" rel="stylesheet">

Quick start

Tailwind v4 (recommended)

app/globals.css:

@import "tailwindcss";
@import "@rintran720/cyberpunk-ui";

That's it. The @theme block in @rintran720/cyberpunk-ui registers all tokens with Tailwind v4, so utilities like text-cp-magenta-500, bg-cp-bg-soft, shadow-cp-glow-cyan, font-cp-display work out of the box — alongside the prebuilt component classes.

app/layout.tsx (Next.js):

export default function RootLayout({ children }) {
  return (
    <html lang="en" data-cp-theme="dark">
      <body className="cp-root cp-grid-bg">{children}</body>
    </html>
  );
}

Tailwind v3

tailwind.config.js:

const cyberpunk = require('@rintran720/cyberpunk-ui/tailwind.preset');
module.exports = {
  presets: [cyberpunk],
  content: ['./app/**/*.{js,ts,jsx,tsx,mdx}'],
};

globals.css:

@tailwind base;
@tailwind components;
@tailwind utilities;
@import "@rintran720/cyberpunk-ui";

Vanilla / no Tailwind

<link rel="stylesheet" href="node_modules/@rintran720/cyberpunk-ui/src/index.css">
<body class="cp-root">…</body>

Cherry-pick

You don't need the whole bundle:

@import "@rintran720/cyberpunk-ui/src/theme.css";
@import "@rintran720/cyberpunk-ui/src/base.css";
@import "@rintran720/cyberpunk-ui/src/components/button.css";
@import "@rintran720/cyberpunk-ui/src/components/card.css";

Anatomy

cyberpunk-ui/
├── src/
│   ├── theme.css        ← Tailwind @theme + raw CSS vars (--cp-*)
│   ├── base.css         ← Reset, scoped under .cp-root
│   ├── animations.css   ← @keyframes (flicker, glitch, scan, …)
│   ├── utilities.css    ← .cp-glow-*, .cp-scanlines, .cp-glitch, .cp-grid-bg, …
│   ├── components/
│   │   ├── accordion.css   ├── alert.css       ├── avatar.css
│   │   ├── badge.css       ├── banner.css      ├── breadcrumb.css
│   │   ├── button.css      ├── calendar.css    ├── card.css
│   │   ├── checkbox.css    ├── chip.css        ├── code.css
│   │   ├── command.css     ├── container.css   ├── diff.css
│   │   ├── divider.css     ├── drawer.css      ├── dropzone.css
│   │   ├── empty.css       ├── equalizer.css   ├── gauge.css
│   │   ├── heading.css     ├── hud.css         ├── input.css
│   │   ├── kbd.css         ├── marquee.css     ├── menu.css
│   │   ├── modal.css       ├── notify.css      ├── otp.css
│   │   ├── pagination.css  ├── popover.css     ├── progress.css
│   │   ├── radar.css       ├── radio.css       ├── rating.css
│   │   ├── reticle.css     ├── segmented.css   ├── select.css
│   │   ├── sidebar.css     ├── skeleton.css    ├── slider.css
│   │   ├── spinner.css     ├── stat.css        ├── status.css
│   │   ├── stepper.css     ├── switch.css      ├── table.css
│   │   ├── tabs.css        ├── terminal.css    ├── textarea.css
│   │   ├── timeline.css    ├── toast.css       ├── toolbar.css
│   │   ├── tooltip.css     └── tree.css
│   └── index.css        ← imports everything above
└── tailwind.preset.cjs  ← Tailwind v3 preset

All component classes follow cp-{name} for the block and cp-{name}--{modifier} for variants — same convention as BEM.

Theme

Color scales

Each accent has a 50–900 scale plus a default 500:

| Hue | Token | Hex | | ------- | ----------------- | --------- | | Cyan | cp-cyan-500 | #00f0ff | | Magenta | cp-magenta-500 | #ff00ea | | Yellow | cp-yellow-500 | #fcee0a | | Green | cp-green-500 | #39ff14 | | Purple | cp-purple-500 | #bd00ff | | Red | cp-red | #ff003c |

Surface / FG

cp-bg, cp-bg-soft, cp-bg-raised, cp-bg-sunken, cp-fg, cp-fg-muted, cp-fg-dim, cp-border, cp-border-soft.

Glow shadows

shadow-cp-glow-cyan / …-magenta / …-yellow / …-green / …-purple / …-red.

Override / re-skin

Drop your own values in a CSS layer:

:root {
  --cp-primary: #f97316;          /* re-skin to orange */
  --cp-glow-cyan: 0 0 18px #f97316;
  --cp-corner-cut: 16px;          /* sharper geometry */
}

Light mode

<html data-cp-theme="light">

Reduce glow (a11y / motion sensitivity)

<html data-cp-glow="off">

prefers-reduced-motion: reduce automatically disables all keyframe-driven flicker / scan / shimmer / glitch animations.

Components — reference

Every component lives in its own file under src/components/<name>.css and follows the same conventions:

  • Block class is cp-{name} (e.g. .cp-btn, .cp-card).
  • Modifiers are cp-{name}--{modifier}. They are additive — combine freely (e.g. cp-btn--neon cp-btn--cut cp-btn--magenta cp-btn--lg).
  • Slots are cp-{name}__{slot} (e.g. .cp-card__header).
  • State is read from native DOM (:checked, :focus, [aria-disabled], [data-state="active"], [data-loading="true"]). No JS shipped — wire your framework, the styles handle the rest.

Modifier axes are usually one of:

| Axis | Values | | ------- | ----------------------------------------------------------- | | Color | cyan (default), magenta, pink, yellow, green, purple, danger | | Size | sm, md (default), lg | | Shape | cut, octa, round, tag, pill (component-dependent) | | Style | neon, ghost, solid, link, outline, hud |

A component listed below as having "color · size · shape" supports all three axes — consult the source file for the exact list. The full kitchen-sink demo is in examples/preview.html; one story per component (with all variants) is in examples/storybook/.


Inputs & forms

Button (cp-btn)

Primary interaction trigger. Works on <button>, <a>, <input type=submit>.

<button class="cp-btn cp-btn--neon cp-btn--cut">Engage</button>
<button class="cp-btn cp-btn--ghost cp-btn--magenta">Hack</button>
<a class="cp-btn cp-btn--lg cp-btn--block cp-btn--green cp-btn--cut" href="#">Deploy</a>

| Axis | Modifiers | | ---- | --------- | | Style | --neon filled with glow · --ghost translucent · --solid filled · --link inline w/ underline | | Color | --cyan (default) · --magenta · --yellow · --green · --purple · --danger | | Size | --sm · --md · --lg · --block (full width) · --icon (square 1:1) | | Shape | --cut (corner-cut) · --tag (left arrow notch) · --octa (HUD octagonal w/ thick ring) · --round (circular bezel) | | State | disabled / aria-disabled="true" (greyed) · data-loading="true" (8-dot spinner overlay, preserves width) |

Use <div class="cp-btn-group"> to merge buttons into a segmented strip.

Input / Field (cp-input, cp-field)

Text input with optional cp-field wrapper that adds label + hint slots.

<label class="cp-field">
  <span class="cp-field__label">Codename</span>
  <input class="cp-input cp-input--cut cp-input--magenta" placeholder="root@nightcity">
  <span class="cp-field__hint">No spaces, letters and digits only.</span>
</label>

<input class="cp-input cp-input--hud cp-input--cyan" placeholder="// CHANNEL">

| Axis | Modifiers | | ---- | --------- | | Color | --cyan (default) · --magenta · --yellow · --green · --purple | | Size | --sm · --md · --lg | | Shape | --cut (corner-cut) · --hud (octagonal HUD frame) | | State | aria-invalid="true" recolors red · disabled |

.cp-field__hint--error colors the hint red. .cp-input-group + .cp-input-group__addon add prefix/suffix slots.

Textarea (cp-input w/ cp-textarea modifier, or cp-textarea-x)

Two flavors:

  • cp-textarea — a cp-input modifier, simple multi-line.
  • cp-textarea-x — richer, ships with --code (mono + line-numbers feel), autosize, and a counter slot via cp-textarea-field__counter.
<textarea class="cp-input cp-textarea cp-input--cut" rows="4"></textarea>

<label class="cp-textarea-field">
  <span class="cp-textarea-field__label">Bio</span>
  <textarea class="cp-textarea-x" maxlength="160"></textarea>
  <span class="cp-textarea-field__counter">0 / 160</span>
</label>

<textarea class="cp-textarea-x cp-textarea-x--code cp-textarea-x--green">
function jackIn(node) { … }
</textarea>

Select (cp-select)

Native <select> styled with the cyberpunk chevron. The dropdown panel itself is rendered by the OS — for a fully themed dropdown panel use <details> + cp-menu (see "Custom dropdowns" below).

<select class="cp-select cp-select--magenta cp-select--cut">
  <option>Sector 7</option>
  <option>Watson</option>
</select>

Same color/size/shape axes as cp-input.

Checkbox (cp-check)

Square box with neon check + glow, paired with a real <input type=checkbox>. The same component also handles radio inputs via cp-check--radio.

<label class="cp-check">
  <input type="checkbox" class="cp-check__input">
  <span class="cp-check__box"></span>
  <span class="cp-check__label">I accept the risk</span>
</label>

<label class="cp-check cp-check--radio cp-check--magenta">
  <input type="radio" name="role" class="cp-check__input">
  <span class="cp-check__box"></span>
  <span class="cp-check__label">Netrunner</span>
</label>

Color axis (--cyan default · --magenta · --yellow · --green · --purple) · --radio (round box, dot fill). State: :checked, :disabled.

Radio (cp-radio)

Same pattern as checkbox.

<label class="cp-radio">
  <input type="radio" name="r1" class="cp-radio__input">
  <span class="cp-radio__mark"></span> Option A
</label>

Switch (cp-switch)

Toggle with sliding thumb + glow; bound to a real <input type=checkbox>.

<label class="cp-switch">
  <input type="checkbox" class="cp-switch__input">
  <span class="cp-switch__track"><span class="cp-switch__thumb"></span></span>
  <span class="cp-switch__label">NETRUNNER MODE</span>
</label>

Color axis · --sm / --lg.

Slider (cp-slider)

Native <input type=range> with custom thumb/track + glow.

<input type="range" class="cp-slider cp-slider--magenta" min="0" max="100" value="64">

Color axis. Use --cp-slider-progress (e.g. via oninput) for a colored fill.

OTP / PIN (cp-otp)

Segmented numeric code input. One real <input> per cell, inputmode="numeric".

<div class="cp-otp cp-otp--cyan" role="group" aria-label="Auth code">
  <input class="cp-otp__cell" type="text" inputmode="numeric" maxlength="1" autocomplete="one-time-code">
  <input class="cp-otp__cell" type="text" inputmode="numeric" maxlength="1">
  <span class="cp-otp__sep">·</span>
  <input class="cp-otp__cell" type="text" inputmode="numeric" maxlength="1">
</div>

Color axis · --sm / --lg. Auto-advance/back-navigation requires a few lines of framework code.

Rating (cp-rating)

5-star rating with zero-JS interaction via radio inputs (reverse DOM order + sibling selector). Read-only displays render via __star--filled / __star--half modifier classes.

<fieldset class="cp-rating cp-rating--magenta" role="radiogroup">
  <input class="cp-rating__input" type="radio" name="r" id="r5" value="5">
  <label class="cp-rating__star" for="r5"></label>
  <input class="cp-rating__input" type="radio" name="r" id="r4" value="4" checked>
  <label class="cp-rating__star" for="r4"></label>
  <!-- … -->
</fieldset>

<span class="cp-rating cp-rating--readonly" data-value="3.5">
  <span class="cp-rating__star cp-rating__star--filled"></span>
  <span class="cp-rating__star cp-rating__star--filled"></span>
  <span class="cp-rating__star cp-rating__star--filled"></span>
  <span class="cp-rating__star cp-rating__star--half"></span>
  <span class="cp-rating__star"></span>
</span>

Color · size axes.

Dropzone (cp-dropzone)

Wraps a real <input type=file> and styles the surrounding label.

<label class="cp-dropzone cp-dropzone--cyan">
  <input class="cp-dropzone__input" type="file" multiple>
  <div class="cp-dropzone__icon">⇧</div>
  <div class="cp-dropzone__title">Upload payload</div>
  <div class="cp-dropzone__hint">Drop files or <span class="cp-dropzone__browse">browse</span></div>
  <div class="cp-dropzone__meta">PNG · JPG · ZIP · 25 MB</div>
</label>

Color axis. Drag-over state read via :focus-within and (optional) data-cp-drag="true" if you wire the events.


Display

Card (cp-card)

Surface container with header / body / footer slots.

<article class="cp-card cp-card--cut cp-card--magenta">
  <div class="cp-card__header">
    <h3 class="cp-card__title">Codename</h3>
    <p class="cp-card__subtitle">v2077</p>
  </div>
  <div class="cp-card__body">…</div>
  <div class="cp-card__footer">
    <button class="cp-btn cp-btn--sm cp-btn--neon">OK</button>
  </div>
</article>

<article class="cp-card cp-card--neon">
  <div class="cp-card__header"><h3 class="cp-card__title">Live</h3></div>
  <div class="cp-card__body">…</div>
</article>

<article class="cp-card cp-card--hud cp-card--magenta">
  <div class="cp-card__strip"></div>
  <div class="cp-card__header"><h3 class="cp-card__title">SYSTEM // STATUS</h3></div>
  <div class="cp-card__body">…</div>
</article>

| Axis | Modifiers | | ---- | --------- | | Style | --cut corner-cut polygon · --neon rotating conic-gradient border · --hud notched HUD frame w/ cp-card__strip glow stripe · --raised brighter surface | | Color | --cyan (default) · --magenta · --yellow · --green · --purple | | Slots | __header, __title, __subtitle, __body, __footer, __strip (HUD only) |

Hover paints the per-card accent glow. --neon requires <div> children for header/body/footer because the border lives on ::before.

Badge / Chip / Pill (cp-badge)

Inline tag for status labels, version strings, counts.

<span class="cp-badge cp-badge--green cp-badge--dot">LIVE</span>
<span class="cp-badge cp-badge--solid cp-badge--magenta">v2.0</span>
<span class="cp-badge cp-badge--cut cp-badge--yellow">PRIORITY</span>
<span class="cp-badge cp-badge--octa cp-badge--cyan">7F</span>
<span class="cp-badge cp-badge--pill cp-badge--purple">PRO</span>

| Axis | Modifiers | | ---- | --------- | | Style | --solid (filled w/ glow) · --cut left/right slash · --octa 8-sided ring · --pill rounded · --dot pulsing leading dot | | Color | --cyan (default) · --magenta · --yellow · --green · --purple · --danger · --muted |

Chip (cp-chip)

Larger interactive variant: removable, choice-style, optional icon.

<span class="cp-chip cp-chip--cyan">netrunner</span>
<span class="cp-chip cp-chip--magenta cp-chip--filled">ICE-7</span>
<span class="cp-chip cp-chip--magenta cp-chip--removable">
  ICE-7 <button class="cp-chip__close" aria-label="Remove">×</button>
</span>

Color · --filled · --removable · --sm / --lg.

Avatar (cp-avatar)

Image, initials, or icon, with optional ring + status dot.

<span class="cp-avatar cp-avatar--lg cp-avatar--ring cp-avatar--magenta">
  <img src="…" alt="V">
  <span class="cp-avatar__status cp-avatar__status--online"></span>
</span>

<span class="cp-avatar-group">
  <span class="cp-avatar">RT</span>
  <span class="cp-avatar cp-avatar--cyan">JN</span>
  <span class="cp-avatar cp-avatar--purple">VK</span>
</span>

Color · size · --ring (neon halo) · --square. Status: --online / --away / --busy / --offline.

Stat (cp-stat)

Single-metric tile with label / value / delta.

<div class="cp-stat cp-stat--cyan">
  <p class="cp-stat__label">PING</p>
  <p class="cp-stat__value">12<span class="cp-stat__unit">ms</span></p>
  <p class="cp-stat__delta cp-stat__delta--up">▲ 3%</p>
</div>

Color axis. Delta direction: --up (green) / --down (red).

Heading (cp-heading)

Display heading with optional cyberpunk effects.

<h1 class="cp-heading cp-heading--xl">NIGHT CITY</h1>
<h1 class="cp-heading cp-heading--xl cp-heading--neon cp-heading--magenta">2077</h1>
<h1 class="cp-heading cp-heading--xl cp-heading--glitch" data-text="ERROR">ERROR</h1>
<h1 class="cp-heading cp-heading--xl cp-heading--holo">HOLOGRAM</h1>

| Axis | Modifiers | | ---- | --------- | | Size | --xs · --sm · --md · --lg · --xl | | Style | --neon (color + glow + uppercase) · --glitch (RGB-split, requires data-text="…") · --holo (animated rainbow gradient) | | Color | accent variants for --neon |

cp-heading-block adds __eyebrow + __subtitle slots for hero blocks.

Code (cp-code) and Kbd (cp-kbd)

Inline code and keyboard-shortcut display.

<code class="cp-code">npm install</code>
<pre class="cp-code cp-code--block">…</pre>

<span class="cp-kbd-group">
  <kbd class="cp-kbd cp-kbd--cyan">⌘</kbd>
  <span class="cp-kbd-group__sep">+</span>
  <kbd class="cp-kbd cp-kbd--cyan">K</kbd>
</span>
<kbd class="cp-kbd cp-kbd--solid cp-kbd--magenta">ESC</kbd>

Color · --sm · --solid (filled). cp-kbd-group joins multiple keys with separators.

Status (cp-status)

Tiny semantic badge: pulsing dot + label, used in tables / lists.

<span class="cp-status cp-status--online cp-status--pill">
  <span class="cp-status__dot"></span>
  <span class="cp-status__label">ONLINE</span>
</span>

States: --online · --away · --busy · --offline · --building. --pill rounds, --solid fills.

Spinner (cp-spinner)

Loading indicator — three glyph styles.

<span class="cp-spinner" role="status" aria-label="Loading"></span>
<span class="cp-spinner cp-spinner--lg cp-spinner--magenta"></span>
<span class="cp-spinner cp-spinner--bars cp-spinner--green"><i></i><i></i><i></i></span>
<span class="cp-spinner cp-spinner--dots cp-spinner--yellow"><i></i><i></i><i></i></span>

| Axis | Modifiers | | ---- | --------- | | Style | default ring · --bars audio-bars · --dots orbiting dots | | Color | --cyan (default) · --magenta · --yellow · --green · --purple | | Size | --sm · --lg |

Skeleton (cp-skeleton)

Shimmer placeholder for loading states.

<div class="cp-skeleton" style="height: 2rem; width: 80%;"></div>
<div class="cp-skeleton cp-skeleton--circle" style="width: 3rem; height: 3rem;"></div>

--circle for avatars, --block for rectangles. Shimmer respects prefers-reduced-motion.

Divider (cp-divider)

Horizontal rule, optional inline label.

<hr class="cp-divider">
<hr class="cp-divider cp-divider--neon cp-divider--magenta">
<div class="cp-divider cp-divider--label"><span>// SECTION</span></div>

Color · --neon (glowing) · --label (text in middle).


Feedback

Alert (cp-alert)

Inline notice with colored accent + icon slot.

<div class="cp-alert cp-alert--warning" role="alert">
  <div class="cp-alert__icon">!</div>
  <div class="cp-alert__content">
    <p class="cp-alert__title">ICE DETECTED</p>
    <p class="cp-alert__body">Black ICE on perimeter — disconnect now.</p>
  </div>
  <button class="cp-alert__close" aria-label="Dismiss">×</button>
</div>

Tones: --info · --success · --warning · --danger · --magenta · --purple. Use role="alert" for warnings/errors, role="status" otherwise.

Banner (cp-banner)

Page-wide announcement strip; spans full width with left accent bar.

<div class="cp-banner cp-banner--warning">
  <span class="cp-banner__icon">△</span>
  <div class="cp-banner__body"><strong>Heat advisory</strong> — limit overclocks.</div>
  <button class="cp-btn cp-btn--ghost cp-btn--sm cp-banner__action">Details</button>
  <button class="cp-banner__close" aria-label="Dismiss">×</button>
</div>

<div class="cp-banner cp-banner--hud cp-banner--magenta">
  <span class="cp-banner__body">SIGNAL LOCKED // SECTOR 07</span>
</div>

| Axis | Modifiers | | ---- | --------- | | Tone | --info · --success · --warning · --danger · --magenta · --pink · --purple | | Style | default left-bar · --solid (full fill) · --hud (notched frame + center glow stripe) | | Behavior | --sticky (position: sticky; top: 0) · --animated (subtle scanline shimmer) |

Toast (cp-toast)

Floating notification (you stack them in a portal of your choice).

<div class="cp-toast cp-toast--success" role="status">
  <div class="cp-toast__icon">✓</div>
  <div class="cp-toast__body">Connection established</div>
  <button class="cp-toast__close" aria-label="Dismiss">×</button>
</div>

Tone axis matches cp-alert. Often used together with cp-stack and a data-cp-auto-dismiss="6" attribute (you bind the JS — see Alert / Interactive · Auto-dismiss countdown story for an example).

Notify (cp-notify)

Notification feed list (read/unread, error/info subtypes).

<ul class="cp-notify cp-notify--cyan cp-notify--frame" role="feed">
  <li class="cp-notify__item cp-notify__item--error cp-notify__item--unread">
    <span class="cp-notify__icon">⚠</span>
    <div class="cp-notify__body">
      <div class="cp-notify__head">
        <span class="cp-notify__title">Trace detected</span>
        <span class="cp-notify__time">now</span>
      </div>
      <p class="cp-notify__text">Active route from arasaka-edge-1.</p>
    </div>
    <button class="cp-notify__close" aria-label="Dismiss">×</button>
  </li>
</ul>

Color · --frame (boxed) · item modifiers --unread, --error, --success, --warning.

Modal / Dialog (cp-modal)

Use native <dialog> for accessibility (focus trap + ESC close come free).

<dialog class="cp-modal cp-modal--magenta">
  <form method="dialog" class="cp-modal__panel">
    <header class="cp-modal__header">
      <h3 class="cp-modal__title">Confirm jack-in</h3>
      <button class="cp-modal__close" aria-label="Close">×</button>
    </header>
    <div class="cp-modal__body">Disconnect from local net?</div>
    <footer class="cp-modal__footer">
      <button class="cp-btn">Cancel</button>
      <button class="cp-btn cp-btn--neon cp-btn--magenta">Engage</button>
    </footer>
  </form>
</dialog>

Or <div class="cp-modal cp-modal--open"> for non-<dialog> backends.

| Axis | Modifiers | | ---- | --------- | | Color | --cyan (default) · --magenta · --yellow · --green · --purple · --danger | | Size | --sm · --md · --lg · --full | | Shape | --cut corner-cut panel · --hud ornate notched HUD frame |

Drawer (cp-drawer)

Slide-in panel (any edge).

<div class="cp-drawer cp-drawer--right cp-drawer--magenta cp-drawer--open">
  <div class="cp-drawer__overlay" data-close></div>
  <aside class="cp-drawer__panel" role="dialog" aria-modal="true">
    <header class="cp-drawer__header">
      <h3 class="cp-drawer__title">Sector detail</h3>
      <button class="cp-drawer__close" aria-label="Close">×</button>
    </header>
    <div class="cp-drawer__body">…</div>
    <footer class="cp-drawer__footer">…</footer>
  </aside>
</div>

| Axis | Modifiers | | ---- | --------- | | Edge | --right · --left · --top · --bottom | | Color | accent variants | | Size | --sm · --md · --lg | | State | --open (visible) — toggle via JS or set [hidden] for closed |

Popover (cp-popover)

Floating contextual panel with arrow.

<div class="cp-popover cp-popover--bottom cp-popover--cyan cp-popover--open">
  <div class="cp-popover__arrow"></div>
  <div class="cp-popover__header"><h4 class="cp-popover__title">Node 7</h4></div>
  <div class="cp-popover__body">High-traffic uplink…</div>
</div>

Position: --top · --bottom · --left · --right. Color axis. Pair with a positioning lib (Floating UI) for production.

Tooltip (cp-tooltip via attribute)

Pure-CSS tooltip via data-cp-tooltip attribute — no JS, no markup.

<button class="cp-btn"
        data-cp-tooltip="Hold ⌘ to bypass"
        data-cp-tooltip-pos="top">
  Engage
</button>

data-cp-tooltip-pos: top (default) · right · bottom · left. Show on :hover + :focus-visible.

Empty (cp-empty)

Illustration + message + actions for empty datasets.

<div class="cp-empty cp-empty--boxed cp-empty--purple">
  <div class="cp-empty__icon">∅</div>
  <h3 class="cp-empty__title">No transmissions</h3>
  <p class="cp-empty__desc">Inbox is clear.</p>
  <div class="cp-empty__actions">
    <button class="cp-btn cp-btn--neon cp-btn--purple cp-btn--sm">Refresh</button>
  </div>
</div>

Color · --boxed (bordered).


Navigation

Tabs (cp-tabs)

Two render modes — see CSS-only tabs section below.

<div class="cp-tabs">
  <div class="cp-tabs__list" role="tablist">
    <button class="cp-tabs__tab" data-state="active">Overview</button>
    <button class="cp-tabs__tab">Logs</button>
  </div>
  <div class="cp-tabs__panels">
    <div class="cp-tabs__panel" data-state="active">…</div>
    <div class="cp-tabs__panel">…</div>
  </div>
</div>

Color axis · --vertical · --cut corner-cut tabs.

Menu (cp-menu)

Dropdown / context list.

<ul class="cp-menu cp-menu--cyan" role="menu">
  <li class="cp-menu__label">Account</li>
  <li role="none">
    <a class="cp-menu__item" role="menuitem" href="#">
      <span class="cp-menu__icon">▶</span>Profile
      <span class="cp-menu__shortcut">⌘P</span>
    </a>
  </li>
  <li role="none">
    <a class="cp-menu__item cp-menu__item--active" role="menuitem" href="#">
      Settings
    </a>
  </li>
  <li class="cp-menu__sep" role="separator"></li>
  <li role="none">
    <a class="cp-menu__item cp-menu__item--danger" role="menuitem" href="#">Sign out</a>
  </li>
</ul>

| Slot | What | | ---- | ---- | | __label | Group heading | | __item | Menu row · --active · --danger | | __icon | Leading glyph | | __shortcut | Trailing keyboard hint | | __sep | <li role="separator"> divider |

Color axis. Use with <details> for zero-JS dropdowns (see Custom dropdowns).

Sidebar (cp-sidebar)

Vertical nav rail.

<aside class="cp-sidebar cp-sidebar--cyan" aria-label="Main">
  <div class="cp-sidebar__brand">
    <span class="cp-sidebar__logo">▣</span>
    <span class="cp-sidebar__brandname">NETRUNNER</span>
  </div>
  <nav class="cp-sidebar__nav">
    <div class="cp-sidebar__group">
      <div class="cp-sidebar__heading">Operations</div>
      <a class="cp-sidebar__item cp-sidebar__item--active" href="#">
        <span class="cp-sidebar__icon">◈</span>
        <span class="cp-sidebar__name">Dashboard</span>
        <span class="cp-sidebar__badge">12</span>
      </a>
    </div>
  </nav>
</aside>

Color axis · --collapsed (icon-only mode) · --right (mirror).

Toolbar (cp-toolbar)

Horizontal command bar with grouped buttons.

<div class="cp-toolbar cp-toolbar--cyan" role="toolbar">
  <div class="cp-toolbar__group">
    <button class="cp-toolbar__btn cp-toolbar__btn--active" aria-pressed="true">▶</button>
    <button class="cp-toolbar__btn">⏸</button>
  </div>
  <div class="cp-toolbar__divider"></div>
  <div class="cp-toolbar__spacer"></div>
  <span class="cp-toolbar__label">Signal</span>
  <span class="cp-toolbar__value">87%</span>
</div>

Color axis · --floating (centered overlay).

Breadcrumb (cp-breadcrumb)

<nav class="cp-breadcrumb" aria-label="Breadcrumb">
  <a class="cp-breadcrumb__item" href="#">Net</a>
  <a class="cp-breadcrumb__item" href="#">Sector 7</a>
  <span class="cp-breadcrumb__item cp-breadcrumb__item--current">Node 42</span>
</nav>

Auto-inserts separators via ::before. Color axis.

Pagination (cp-pagination)

<nav class="cp-pagination" aria-label="Pagination">
  <a class="cp-pagination__item" href="#">‹</a>
  <a class="cp-pagination__item cp-pagination__item--active" href="#">1</a>
  <a class="cp-pagination__item" href="#">2</a>
  <span class="cp-pagination__ellipsis">…</span>
  <a class="cp-pagination__item" href="#">9</a>
  <a class="cp-pagination__item" href="#">›</a>
</nav>

Color · --sm / --lg · --cut (corner-cut items).

Stepper (cp-stepper)

Wizard / boot sequence.

<ol class="cp-stepper">
  <li class="cp-stepper__step cp-stepper__step--complete">
    <span class="cp-stepper__marker cp-stepper__marker--check"></span>
    <span class="cp-stepper__title">Identify</span>
    <span class="cp-stepper__desc">Handle verified</span>
  </li>
  <li class="cp-stepper__step cp-stepper__step--current">
    <span class="cp-stepper__marker">2</span>
    <span class="cp-stepper__title">Encrypt</span>
  </li>
  <li class="cp-stepper__step">
    <span class="cp-stepper__marker">3</span>
    <span class="cp-stepper__title">Engage</span>
  </li>
</ol>

--vertical · --inline (compact horizontal). Step states: --complete / --current / (default = pending).

Segmented (cp-segmented)

Mode switcher / radio strip.

<!-- Framework-driven (aria-pressed) -->
<div class="cp-segmented" role="group" aria-label="View">
  <button class="cp-segmented__option" aria-pressed="true">Grid</button>
  <button class="cp-segmented__option">List</button>
  <button class="cp-segmented__option">Map</button>
</div>

<!-- Pure-CSS (radio inputs) -->
<div class="cp-segmented cp-segmented--magenta">
  <input class="cp-segmented__input" type="radio" name="v" id="v-a" checked>
  <label class="cp-segmented__option" for="v-a">Day</label>
  <input class="cp-segmented__input" type="radio" name="v" id="v-b">
  <label class="cp-segmented__option" for="v-b">Week</label>
</div>

Color · --sm / --lg · --block (stretches).

Command palette (cp-cmd)

⌘K-style search palette with keyboard hints.

<div class="cp-cmd cp-cmd--magenta cp-cmd--open">
  <div class="cp-cmd__overlay" data-close></div>
  <div class="cp-cmd__panel" role="dialog">
    <div class="cp-cmd__search">
      <span class="cp-cmd__icon">⌖</span>
      <input class="cp-cmd__input" placeholder="Type a command…">
      <kbd class="cp-kbd cp-kbd--sm">ESC</kbd>
    </div>
    <div class="cp-cmd__results">
      <div class="cp-cmd__group">
        <div class="cp-cmd__group-label">Recent</div>
        <button class="cp-cmd__item cp-cmd__item--active">
          <span class="cp-cmd__item-icon">▶</span>
          <span class="cp-cmd__item-text">Jack into Sector 7</span>
        </button>
      </div>
    </div>
    <div class="cp-cmd__footer">…hint kbd legend…</div>
  </div>
</div>

Color axis. Filtering / fuzzy-match is your responsibility — only the look is here.

Tree (cp-tree)

Nested file/folder list, zero-JS via nested <details>.

<ul class="cp-tree cp-tree--cyan cp-tree--lines" role="tree">
  <li role="treeitem">
    <details class="cp-tree__node" open>
      <summary class="cp-tree__label">
        <span class="cp-tree__caret"></span>
        <span class="cp-tree__icon">▣</span>
        <span class="cp-tree__name">netrunner</span>
      </summary>
      <ul class="cp-tree__children">
        <li class="cp-tree__item cp-tree__item--active">
          <span class="cp-tree__leaf"></span>
          <span class="cp-tree__name">handles.json</span>
          <span class="cp-tree__meta">2 KB</span>
        </li>
      </ul>
    </details>
  </li>
</ul>

Color · --lines (connector lines) · --lg size.

Accordion (cp-accordion)

Zero-JS via native <details>.

<details class="cp-accordion">
  <summary class="cp-accordion__trigger">Encryption keys</summary>
  <div class="cp-accordion__panel">…</div>
</details>

Color axis · --bordered (boxed look) · --cut (corner-cut).


Data display

Table (cp-table)

<table class="cp-table cp-table--striped">
  <thead><tr><th>ID</th><th>Node</th><th>Status</th></tr></thead>
  <tbody>
    <tr><td>01</td><td>nightcity</td><td><span class="cp-badge cp-badge--green">UP</span></td></tr>
  </tbody>
</table>

Modifiers: --striped · --bordered · --compact · --hover · --cut · color axis (header tint).

Diff (cp-diff)

Inline / unified code diff with header + line markers.

<pre class="cp-diff cp-diff--unified cp-diff--bordered">
  <div class="cp-diff__header">
    <span class="cp-diff__filename">crypto/handshake.rs</span>
    <span class="cp-diff__counts"><span class="add">+2</span> <span class="del">-1</span></span>
  </div>
  <code class="cp-diff__row cp-diff__row--meta">@@ -12,4 +12,5 @@</code>
  <code class="cp-diff__row">  let key = derive(seed);</code>
  <code class="cp-diff__row cp-diff__row--del">- send(key);</code>
  <code class="cp-diff__row cp-diff__row--add">+ send(encrypt(key, nonce));</code>
</pre>

Modes: --unified · --split. --bordered · --compact.

Calendar (cp-cal)

Static date grid (you provide the days).

<div class="cp-cal cp-cal--cyan cp-cal--frame" role="grid" aria-label="April 2026">
  <div class="cp-cal__header">
    <button class="cp-cal__nav" aria-label="Previous">‹</button>
    <div class="cp-cal__title">APRIL · 2026</div>
    <button class="cp-cal__nav" aria-label="Next">›</button>
  </div>
  <div class="cp-cal__weekdays">
    <span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span><span>Su</span>
  </div>
  <div class="cp-cal__grid">
    <button class="cp-cal__day cp-cal__day--out">31</button>
    <button class="cp-cal__day">1</button>
    <button class="cp-cal__day cp-cal__day--today">29</button>
    <button class="cp-cal__day cp-cal__day--selected">30</button>
  </div>
</div>

Day modifiers: --out (other-month) · --today · --selected · --in-range · --disabled. Color · --frame (HUD wrap).

Timeline (cp-timeline)

Chronological log with marker per item.

<ol class="cp-timeline">
  <li class="cp-timeline__item cp-timeline__item--green">
    <span class="cp-timeline__time">12:04</span>
    <span class="cp-timeline__marker"></span>
    <div class="cp-timeline__content">
      <p class="cp-timeline__title">Connection established</p>
    </div>
  </li>
  <li class="cp-timeline__item cp-timeline__item--magenta">
    <span class="cp-timeline__time">12:09</span>
    <span class="cp-timeline__marker cp-timeline__marker--pulse"></span>
    <div class="cp-timeline__content">…</div>
  </li>
  <li class="cp-timeline__item cp-timeline__item--danger">
    <span class="cp-timeline__time">12:11</span>
    <span class="cp-timeline__marker cp-timeline__marker--icon">!</span>
    <div class="cp-timeline__content">…</div>
  </li>
</ol>

Per-item color · --alt (alternating sides) · marker variants --pulse / --icon / --ring.

Progress (cp-progress)

Linear bar / meter; --cp-progress: 64% drives the fill.

<div class="cp-progress cp-progress--magenta cp-progress--striped"
     style="--cp-progress: 64%"
     role="progressbar" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100">
  <div class="cp-progress__bar"></div>
</div>

<!-- Indeterminate -->
<div class="cp-progress cp-progress--indeterminate cp-progress--cyan">
  <div class="cp-progress__bar"></div>
</div>

<!-- HUD center-glow strip -->
<div class="cp-progress cp-progress--strip cp-progress--magenta"
     style="--cp-progress: 62%">
  <div class="cp-progress__bar"></div>
</div>

<!-- Segmented stat-bar (set --cp-progress-segments) -->
<div class="cp-progress cp-progress--segmented cp-progress--cyan"
     style="--cp-progress: 50%; --cp-progress-segments: 12">
  <div class="cp-progress__bar"></div>
</div>

| Axis | Modifiers | | ---- | --------- | | Color | accents | | Size | --sm · --md · --lg | | Style | --striped (animated diagonal stripes) · --indeterminate (sliding) · --segmented (HUD-tick stat bar) · --strip (tapered glow line) |

Gauge (cp-gauge)

Radial dial; --cp-gauge-value: 72 (0–100) drives the arc.

<div class="cp-gauge cp-gauge--cyan cp-gauge--ticks" style="--cp-gauge-value: 72">
  <div class="cp-gauge__track"></div>
  <div class="cp-gauge__center">
    <div class="cp-gauge__value" data-suffix="%">72</div>
    <div class="cp-gauge__label">SIGNAL</div>
  </div>
</div>

Color · --ticks (tick marks) · size axis.

Equalizer (cp-eq)

Animated audio bars (decorative).

<div class="cp-eq cp-eq--cyan" aria-hidden="true">
  <span class="cp-eq__bar"></span>
  <span class="cp-eq__bar"></span>
  <span class="cp-eq__bar"></span>
  <span class="cp-eq__bar"></span>
  <span class="cp-eq__bar"></span>
</div>

Color · --paused (freezes) · --sm / --lg. Each bar has a stable random animation-delay.


HUD / decorative

HUD (cp-hud)

Wrap any content in cyberpunk panel chrome — corner brackets, frame, notched panel, scanlines.

<!-- 4 corner brackets + scanlines -->
<div class="cp-hud cp-hud--cyan cp-hud--brackets cp-hud--scan">
  <span class="cp-hud__corner cp-hud__corner--tl">SECTOR-7</span>
  <span class="cp-hud__corner cp-hud__corner--tr">04:22 UTC</span>
  <span class="cp-hud__corner cp-hud__corner--bl">SIGNAL: 98%</span>
  <span class="cp-hud__corner cp-hud__corner--br">v2.077</span>
  <div class="cp-hud__body">…</div>
</div>

<!-- Ornate notched HUD panel + center glow stripe -->
<div class="cp-hud cp-hud--panel cp-hud--magenta cp-hud--strip">
  <span class="cp-hud__strip"></span>
  <div class="cp-hud__body">…</div>
</div>

<!-- Round bezel button-like element -->
<span class="cp-hud cp-hud--circle cp-hud--cyan cp-hud--lg">▶</span>

| Axis | Modifiers | | ---- | --------- | | Shape | --brackets 4 corner L's · --frame simple neon border · --panel notched HUD frame · --circle round bezel | | Color | --cyan (default) · --magenta · --pink · --yellow · --green · --purple · --danger | | Size | --sm · --md · --lg | | Overlay | --scan scanline overlay · --grid faint grid bg · --strip (with <span class="cp-hud__strip"> child) |

Reticle (cp-reticle)

HUD targeting crosshair.

<div class="cp-reticle cp-reticle--cyan cp-reticle--pulse">
  <span class="cp-reticle__corner cp-reticle__corner--tl"></span>
  <span class="cp-reticle__corner cp-reticle__corner--tr"></span>
  <span class="cp-reticle__corner cp-reticle__corner--bl"></span>
  <span class="cp-reticle__corner cp-reticle__corner--br"></span>
  <span class="cp-reticle__cross"></span>
  <span class="cp-reticle__label">LOCKED</span>
</div>

Color · --pulse (animated) · --scan (sweeping line) · sizes.

Radar (cp-radar)

Sonar sweep + blip overlay.

<div class="cp-radar cp-radar--green">
  <div class="cp-radar__sweep"></div>
  <span class="cp-radar__blip" style="--x: 30%; --y: 40%;"></span>
  <span class="cp-radar__blip cp-radar__blip--strong" style="--x: 70%; --y: 60%;"></span>
  <span class="cp-radar__blip cp-radar__blip--hostile" style="--x: 60%; --y: 25%;"></span>
  <div class="cp-radar__label">RADAR · 04km</div>
</div>

Color · sweep speed via --cp-radar-speed. Blip variants: --strong · --hostile.

Marquee / Ticker (cp-marquee, cp-ticker)

<div class="cp-marquee">
  <div class="cp-marquee__track">
    PACKETS 4.7K/S · LATENCY 12 MS · ICE CLEAR
  </div>
</div>

<!-- Seamless ticker (duplicate the group) -->
<div class="cp-ticker cp-ticker--cyan cp-ticker--fade">
  <div class="cp-ticker__track">
    <div class="cp-ticker__group">
      <span class="cp-ticker__item">PACKETS 4.7K/S</span>
      <span class="cp-ticker__sep">▰</span>
      <span class="cp-ticker__item">LATENCY 12 MS</span>
    </div>
    <div class="cp-ticker__group" aria-hidden="true"><!-- duplicate --></div>
  </div>
</div>

--fade adds left/right gradient mask. Pauses on :hover.

Terminal (cp-terminal)

Faux console window.

<div class="cp-terminal cp-terminal--green cp-terminal--scanlines">
  <div class="cp-terminal__header">
    <span class="cp-terminal__dots"><i></i><i></i><i></i></span>
    <span class="cp-terminal__title">root@nightcity ~ %</span>
  </div>
  <div class="cp-terminal__body">
    <div class="cp-terminal__line cp-terminal__line--cmd">whoami</div>
    <div class="cp-terminal__line">netrunner-77</div>
    <div class="cp-terminal__line cp-terminal__line--ok">connection encrypted</div>
    <div class="cp-terminal__line cp-terminal__line--err">ICE detected at 0x4f72</div>
    <div class="cp-terminal__line cp-terminal__line--prompt">
      <span class="cp-terminal__cursor"></span>
    </div>
  </div>
</div>

Color · --scanlines (CRT overlay) · --frame (wrap with HUD chrome).


Charts (basic primitives)

The library ships a set of small chart helpers — ASCII-style outputs styled to match the cyberpunk look. They expect data wired in via inline custom properties, so they're SSR-safe and framework-free.

| Class | What | | ----- | ---- | | cp-bars | Vertical / horizontal bar chart. Bars set width via --cp-bar-value. | | cp-spark | Sparkline (one line / one bar series via SVG <polyline> markup). | | cp-donut | Donut / radial percentage. --cp-donut-value controls the arc. | | cp-heatmap | Calendar / grid heatmap, intensity via --cp-heat-level (0–4). | | cp-rank | Ranked-list bar chart (race-table look). | | cp-funnel | Funnel / waterfall conversion view. | | cp-bullet | Bullet chart (one row, target marker). | | cp-stack | Stacked bar / area composition. | | cp-scatter | Scatter plot via positioned dots. | | cp-spider | Radar / spider chart. | | cp-gantt | Gantt-style timeline rows. | | cp-candle | Candlestick (OHLC) row. | | cp-waterfall | Waterfall add/subtract chart. | | cp-histogram | Histogram bins. | | cp-calheat | Year-grid contribution heatmap. | | cp-bubble | Bubble plot (size = --cp-bubble-size). | | cp-boxplot | Box-and-whisker. | | cp-lollipop | Dot + stem chart. | | cp-wave | Sine/wave audio scrubber. |

Each chart owns its own CSS file under src/components/<name>.css and follows the same color / size axes as the rest of the library. The storybook ships at least one story per chart with realistic data — inspect those for the exact markup.


See examples/preview.html for the full kitchen-sink page, or examples/storybook/ for one story per component with all variants.

CSS-only tabs

The cp-tabs component supports two modes:

Framework-driven (recommended for React/Vue/Svelte) — toggle data-state="active" on the active tab and panel:

<div className="cp-tabs">
  <div className="cp-tabs__list" role="tablist">
    <button
      className="cp-tabs__tab"
      data-state={tab === "a" ? "active" : undefined}
      onClick={() => setTab("a")}
    >Overview</button>
    …
  </div>
  <div className="cp-tabs__panels">
    <div className="cp-tabs__panel"
         data-state={tab === "a" ? "active" : undefined}>…</div>
  </div>
</div>

Pure CSS (no JS) — radio inputs + a per-id rule. SSR-safe, but you need to author one rule per tab id:

<div class="cp-tabs">
  <input class="cp-tabs__input" type="radio" name="t1" id="t1-a" checked>
  <input class="cp-tabs__input" type="radio" name="t1" id="t1-b">
  <div class="cp-tabs__list">
    <label class="cp-tabs__tab" for="t1-a">A</label>
    <label class="cp-tabs__tab" for="t1-b">B</label>
  </div>
  <div class="cp-tabs__panels">
    <div class="cp-tabs__panel" data-for="t1-a">A content</div>
    <div class="cp-tabs__panel" data-for="t1-b">B content</div>
  </div>
</div>

<style>
  /* one block per tabset */
  .cp-tabs:has(#t1-a:checked) [for="t1-a"]            { color: var(--cp-cyan); border-bottom-color: var(--cp-cyan); text-shadow: 0 0 8px var(--cp-cyan); }
  .cp-tabs:has(#t1-a:checked) [data-for="t1-a"]       { display: block; }
  .cp-tabs:has(#t1-b:checked) [for="t1-b"]            { color: var(--cp-cyan); border-bottom-color: var(--cp-cyan); text-shadow: 0 0 8px var(--cp-cyan); }
  .cp-tabs:has(#t1-b:checked) [data-for="t1-b"]       { display: block; }
</style>

If :has() isn't an option, the framework-driven path is the cleaner choice — the visual styling is identical.

Custom dropdowns (zero-JS)

Native <select> is fully themed via cp-select, but the open <option> list is rendered by the OS — Safari and Chrome on macOS ignore most CSS on <option>. When you need the dropdown panel itself to look cyberpunk (left accent bar, neon hover, glow), pair <details> with cp-menu:

<details class="cp-dd">
  <summary class="cp-dd__trigger">Sector 7</summary>
  <ul class="cp-menu" role="menu">
    <li role="none"><a class="cp-menu__item cp-menu__item--active" role="menuitem" href="#">Sector 7</a></li>
    <li role="none"><a class="cp-menu__item" role="menuitem" href="#">Watson</a></li>
    <li role="none"><a class="cp-menu__item" role="menuitem" href="#">Pacifica</a></li>
  </ul>
</details>

Three working stories ship in examples/storybook/stories/Select.stories.ts (CustomDropdown, DropdownGrouped, DropdownInForm) including the small wrapper styles for the trigger + chevron rotation.

For full keyboard nav (arrow keys, type-ahead) wire it up with your framework's state — the visual classes don't change.

Effects you'll actually use

| Class | What it does | | --- | --- | | cp-grid-bg | Subtle scan grid as page background. | | cp-grid-bg cp-grid-bg--dense | Tighter grid. | | cp-grid-floor | "Tron"-style vanishing-point grid. | | cp-scanlines | CRT scanline overlay on any positioned element. | | cp-scanlines cp-scanlines--animated | Adds a slow downward sweep. | | cp-crt | Dark vignette to fake a CRT bezel. | | cp-glitch (with data-text) | RGB-split glitch on text. Add --hover to trigger only on hover. | | cp-holo | Animated holographic text gradient. | | cp-flicker | Faulty-neon flicker. | | cp-pulse | Soft glow pulse. | | cp-shimmer | Loading shimmer (used by cp-skeleton). | | cp-marquee + cp-marquee__track | Ticker tape. Pauses on hover. | | cp-typewriter | Single-line typing animation. | | cp-caret | Adds a blinking caret to text. | | cp-hud | Wraps content with corner brackets. | | cp-border-anim | Rotating conic-gradient border ring. | | cp-clip-corner | Cyberpunk corner-cut clip-path on any box. | | cp-clip-arrow-r / cp-clip-tag-l | Arrowhead / tag shapes. |

Layout helpers

| Class | What it does | | --- | --- | | cp-root | Apply on <body> (or page root). Sets dark surface, scrollbar, focus rings, monospace selection. Required for dark backgrounds and scoped resets. | | cp-container | Centered content, fluid padding (narrow/wide/full modifiers). | | cp-section | Vertical breathing room. | | cp-stack | Vertical flex with --cp-stack-gap (xsxl). | | cp-cluster | Wrap-friendly horizontal flex with gap. | | cp-grid cp-grid--auto | auto-fit responsive grid (--cp-grid-min controls min track). | | cp-grid--2/3/4 | Fixed columns that collapse on small screens. |

Accessibility

  • All interactive components ship a visible focus ring via :focus-visible. The ring lives on .cp-root so any framework's a11y plumbing keeps working.
  • Color-only state (active tab, switch on/off) is paired with shape / position changes.
  • Animations honor prefers-reduced-motion.
  • Glow can be globally disabled with data-cp-glow="off" (opt-in for high-contrast users).
  • Form components use real <input> elements — screen reader & autofill behavior is preserved.

Server-side rendering

Cyberpunk UI is 100% CSS. There is nothing to hydrate. Components are plain HTML elements with classes, so:

  • Renders perfectly in React Server Components — no "use client" needed.
  • No layout shift, no FOUC if the stylesheet is in <head>.
  • Works in Astro / Hugo / Jekyll / plain HTML out of the box.

A working Next.js 15 (RSC, app router) demo lives in examples/nextjs/.

Tailwind cheat-sheet

The @theme block (or v3 preset) registers these utility namespaces:

text-cp-cyan-500            bg-cp-bg-soft
border-cp-magenta-400       text-cp-fg-muted
shadow-cp-glow-purple       shadow-cp-glow-magenta
font-cp-display             font-cp-mono             font-cp-body
animate-cp-flicker          animate-cp-pulse         animate-cp-shimmer

Mix freely with the component classes:

<button class="cp-btn cp-btn--neon hover:scale-105 transition">
  Engage
</button>

Storybook

Yes — the library plays well with Storybook. Because everything is pure CSS, the natural fit is @storybook/html-vite: each story returns plain HTML, no framework wrapper needed. A working setup ships in examples/storybook/:

cd examples/storybook
npm install
npm run storybook

The bundled config wraps every story in .cp-root so dark surface and focus rings apply automatically, registers a Themes toolbar (data-cp-theme dark/light), and pre-loads all components.

If you're already on @storybook/react you can author the same stories as React — the classes don't care:

export const Neon = () => (
  <button className="cp-btn cp-btn--neon cp-btn--cut">Engage</button>
);

License

MIT