NOIR // NEXT

Setup Guide

One-time setup for using NOIR // NEXT components. Add these to your project, then copy any component from the theme.

1
Install dependencies

The cn() utility combines clsx and tailwind-merge for conditional class names. Install both packages.

BASH
npm install clsx tailwind-merge
2
Add utility function

Create lib/utils.ts with the cn() helper. Every component imports this for class name merging.

TS
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn(...inputs: ClassValue[]): string {
  return twMerge(clsx(inputs))
}
3
Add design tokens

Add the NOIR // NEXT design tokens to your globals.css. These define colors, fonts, and animation tokens used by every component in the theme.

CSS
@theme {
  /* ── Colors ─────────────────────────────────────────── */
  --color-noir-black:          #060608;
  --color-noir-base:           #080809;
  --color-noir-surface:        #0f0f12;
  --color-noir-surface-2:      #161619;
  --color-noir-surface-3:      #1d1d22;
  --color-noir-border:         #2a2a33;
  --color-noir-border-soft:    #1c1c22;
  --color-noir-text:           #eeeef4;
  --color-noir-text-muted:     #6c6c84;
  --color-noir-text-dim:       #38384a;
  --color-noir-violet:         #7c3aed;
  --color-noir-violet-light:   #a78bfa;
  --color-noir-violet-dark:    #5b21b6;
  --color-noir-cyan:           #22d3ee;
  --color-noir-cyan-light:     #a5f3fc;
  --color-noir-cyan-dark:      #0891b2;
  --color-noir-green:          #22c55e;
  --color-noir-green-dark:     #16a34a;
  --color-noir-amber:          #f59e0b;
  --color-noir-amber-dark:     #d97706;
  --color-noir-red:            #f43f5e;
  --color-noir-red-dark:       #e11d48;
  --color-noir-white:          #ffffff;

  /* ── Fonts ───────────────────────────────────────────── */
  --font-body: var(--font-barlow);
  --font-mono: var(--font-ibm-plex-mono);

  /* ── Animations ──────────────────────────────────────── */
  --animate-blink:       blink-cursor 1s step-end infinite;
  --animate-pulse-glow:  pulse-glow 3s ease-in-out infinite;
  --animate-float:       gentle-float 5s ease-in-out infinite;
  --animate-scan:        scan-line 4s linear infinite;
}
4
Add component styles

Each component may need its own CSS classes in your globals.css. Copy the styles for the components you use.

CSS
/* ─── Buttons ────────────────────────────────────────── */

.btn-base {
  display:         inline-flex;
  align-items:     center;
  justify-content: center;
  gap:             8px;
  font-family:     var(--font-body);
  font-weight:     600;
  border:          1px solid transparent;
  border-radius:   8px;
  cursor:          pointer;
  text-decoration: none;
  line-height:     1;
  letter-spacing:  0.02em;
  transition:      all 0.15s ease;
  white-space:     nowrap;
}

.btn-base:hover  { text-decoration: none; transform: translateY(-1px); }

.btn-base:active { transform: translateY(0) !important; }

.btn-sm { font-size: 0.78rem; padding: 7px 14px; }

.btn-md { font-size: 0.875rem; padding: 10px 20px; }

.btn-lg { font-size: 0.95rem;  padding: 13px 28px; }

.btn-primary {
  background:   var(--color-noir-violet);
  color:        var(--color-noir-white);
  border-color: var(--color-noir-violet-dark);
  box-shadow:   0 0 0 0 rgba(124, 58, 237, 0);
}

.btn-primary:hover {
  background:  var(--color-noir-violet-light);
  border-color: var(--color-noir-violet);
  box-shadow:  0 0 20px rgba(124, 58, 237, 0.35), 0 4px 16px rgba(0, 0, 0, 0.4);
  color:        var(--color-noir-white);
}

.btn-secondary {
  background:   transparent;
  color:        var(--color-noir-cyan);
  border-color: rgba(34, 211, 238, 0.3);
}

.btn-secondary:hover {
  background:   rgba(34, 211, 238, 0.07);
  border-color: rgba(34, 211, 238, 0.6);
  box-shadow:   0 0 16px rgba(34, 211, 238, 0.15);
  color:        var(--color-noir-cyan);
}

.btn-ghost {
  background:   transparent;
  color:        var(--color-noir-text-muted);
  border-color: var(--color-noir-border);
}

.btn-ghost:hover {
  background:   var(--color-noir-surface-2);
  border-color: var(--color-noir-border);
  color:        var(--color-noir-text);
}
CSS
/* ─── Badges ─────────────────────────────────────────── */

.badge {
  display:        inline-flex;
  align-items:    center;
  gap:            5px;
  font-family:    var(--font-mono);
  font-weight:    500;
  font-size:      0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding:        3px 9px;
  border-radius:  4px;
  border:         1px solid transparent;
}

.badge--violet { background: rgba(124, 58, 237, 0.12); color: var(--color-noir-violet-light); border-color: rgba(124, 58, 237, 0.25); }

.badge--cyan   { background: rgba(34, 211, 238, 0.08);  color: var(--color-noir-cyan);        border-color: rgba(34, 211, 238, 0.2); }

.badge--green  { background: rgba(34, 197, 94, 0.08);   color: var(--color-noir-green);       border-color: rgba(34, 197, 94, 0.2); }

.badge--amber  { background: rgba(245, 158, 11, 0.10);  color: var(--color-noir-amber);       border-color: rgba(245, 158, 11, 0.22); }

.badge--red    { background: rgba(244, 63, 94, 0.08);   color: var(--color-noir-red);         border-color: rgba(244, 63, 94, 0.2); }
CSS
/* ─── Features Section ───────────────────────────────── */

.noir-card {
  background:    var(--color-noir-surface-2);
  border:        1px solid var(--color-noir-border);
  border-radius: 10px;
  padding:       24px;
  transition:    border-color 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease;
  position:      relative;
  overflow:      hidden;
}

.noir-card::before {
  content:    '';
  position:   absolute;
  top:        0; left: 0; right: 0;
  height:     1px;
  background: linear-gradient(90deg, transparent, rgba(124, 58, 237, 0.4), transparent);
  opacity:    0;
  transition: opacity 0.2s ease;
}

.noir-card:hover {
  border-color: rgba(124, 58, 237, 0.3);
  box-shadow:   0 0 0 1px rgba(124, 58, 237, 0.08), 0 8px 32px rgba(0, 0, 0, 0.4);
  transform:    translateY(-2px);
}

.noir-card:hover::before { opacity: 1; }

.card-title {
  font-weight:   600;
  font-size:     0.975rem;
  color:         var(--color-noir-text);
  margin-bottom: 8px;
  line-height:   1.3;
  letter-spacing: -0.01em;
}

.card-description {
  font-size:   0.855rem;
  font-weight: 400;
  color:       var(--color-noir-text-muted);
  line-height: 1.65;
}
CSS
/* ─── Text Variants ──────────────────────────────────── */

.text-body    { font-size: 0.95rem; line-height: 1.75; font-weight: 400; color: var(--color-noir-text-muted); }

.text-caption { font-size: 0.8rem;  font-weight: 400;  color: var(--color-noir-text-muted); }

.text-label   { font-family: var(--font-mono); font-size: 0.65rem; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; }

.text-code    { font-family: var(--font-mono); font-size: 0.85rem; color: var(--color-noir-cyan-light); }
NOIR // NEXT Components
NOIR // NEXTUI

Button

Dark-mode button with primary, secondary, and ghost variants in three sizes. Renders as a Next.js Link when an href is provided.

NOIR // NEXTUI

Badge

Inline badge with violet, cyan, green, amber, and red variants for status labels and category tags in the NOIR dark palette.

NOIR // NEXTUI

Card

Composable dark card container with header, body, and footer sub-components in the NOIR aesthetic.

NOIR // NEXTUI

Text

Polymorphic text primitive with body, caption, label, and code variants styled for the NOIR dark palette.