Setup Guide
One-time setup for using CRATE // NEXT components. Add these to your project, then copy any component from the theme.
The cn() utility combines clsx and tailwind-merge for conditional class names. Install both packages.
npm install clsx tailwind-mergeCreate lib/utils.ts with the cn() helper. Every component imports this for class name merging.
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs))
}Add the CRATE // NEXT design tokens to your globals.css. These define colors, fonts, and animation tokens used by every component in the theme.
@theme {
/* ── Palette ── */
--color-kraft: #c4a882;
--color-kraft-light: #d9c4a5;
--color-kraft-dark: #a68960;
--color-kraft-deep: #8b6f47;
--color-cardboard: #e8d5b7;
--color-cardboard-light: #f2e6d0;
--color-ink: #2a1f14;
--color-ink-light: #4a3928;
--color-ink-faded: #6b5a45;
--color-accent: #E85D26;
--color-accent-light: #f17a4a;
--color-tape: #f5e6c8;
--color-tape-border: #d4c4a0;
--color-white: #faf6f0;
--color-stamp-red: #c0392b;
/* ── Fonts ── */
--font-display: 'Archivo Black', sans-serif;
--font-body: 'DM Sans', sans-serif;
/* ── Radius (zero everywhere) ── */
--radius-sm: 0px;
--radius-md: 0px;
--radius-lg: 0px;
/* ── Shadows ── */
--shadow-float: 0 14px 40px rgba(42, 31, 20, 0.18), 0 4px 12px rgba(42, 31, 20, 0.1);
--shadow-soft: 0 8px 24px rgba(42, 31, 20, 0.12);
--shadow-deep: 0 20px 60px rgba(42, 31, 20, 0.22), 0 8px 20px rgba(42, 31, 20, 0.14);
}Each component may need its own CSS classes in your globals.css. Copy the styles for the components you use.
.btn-primary {
display: inline-block;
font-family: var(--font-display);
font-size: 0.8rem;
letter-spacing: 0.08em;
text-transform: uppercase;
background: var(--color-ink);
color: var(--color-cardboard-light);
padding: 1rem 2.2rem;
text-decoration: none;
box-shadow: var(--shadow-float);
transition: transform 0.2s, box-shadow 0.2s;
border: none;
cursor: pointer;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-deep);
}
.btn-secondary {
display: inline-block;
font-family: var(--font-display);
font-size: 0.8rem;
letter-spacing: 0.08em;
text-transform: uppercase;
background: transparent;
color: var(--color-ink);
padding: 1rem 2.2rem;
text-decoration: none;
border: 2px solid var(--color-ink);
transition: all 0.2s;
cursor: pointer;
}
.btn-secondary:hover {
background: var(--color-ink);
color: var(--color-cardboard-light);
}
.btn-ghost {
font-family: var(--font-body);
font-size: 0.85rem;
font-weight: 500;
color: var(--color-ink-faded);
text-decoration: none;
border-bottom: 1px dashed var(--color-kraft-dark);
padding-bottom: 2px;
background: none;
border-top: none;
border-left: none;
border-right: none;
cursor: pointer;
transition: color 0.2s;
}
.btn-ghost:hover {
color: var(--color-accent);
}
.btn-accent {
display: inline-block;
font-family: var(--font-display);
font-size: 0.8rem;
letter-spacing: 0.08em;
text-transform: uppercase;
background: var(--color-accent);
color: var(--color-white);
padding: 1rem 2.2rem;
text-decoration: none;
box-shadow: var(--shadow-float);
transition: transform 0.2s, box-shadow 0.2s;
border: none;
cursor: pointer;
}
.btn-accent:hover {
background: var(--color-accent-light);
transform: translateY(-2px);
box-shadow: var(--shadow-deep);
}
.btn-sm { padding: 0.5rem 1.2rem; font-size: 0.7rem; }
.btn-md { padding: 1rem 2.2rem; font-size: 0.8rem; }
.btn-lg { padding: 1.2rem 3rem; font-size: 0.85rem; }.badge {
display: inline-block;
font-family: var(--font-display);
font-size: 0.65rem;
letter-spacing: 0.15em;
text-transform: uppercase;
padding: 0.3rem 0.8rem;
border: 2px solid currentcolor;
}
.badge--accent { color: var(--color-accent); border-color: var(--color-accent); }
.badge--ink { color: var(--color-ink); border-color: var(--color-ink); }
.badge--kraft { color: var(--color-kraft-deep); border-color: var(--color-kraft-deep); }
.badge--stamp { color: var(--color-stamp-red); border-color: var(--color-stamp-red); }
.badge--faded { color: var(--color-ink-faded); border-color: var(--color-ink-faded); }.text-body {
font-family: var(--font-body);
font-size: 1rem;
line-height: 1.7;
color: var(--color-ink-light);
}
.text-caption {
font-family: var(--font-body);
font-size: 0.8rem;
line-height: 1.5;
color: var(--color-ink-faded);
}
.text-label {
font-family: var(--font-display);
font-size: 0.65rem;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--color-ink-faded);
}
.text-code {
font-family: 'DM Mono', monospace;
font-size: 0.85rem;
background: rgba(139, 111, 71, 0.1);
padding: 0.15rem 0.4rem;
color: var(--color-ink);
}.crate-card {
background: var(--color-cardboard-light);
padding: 2.5rem;
box-shadow: var(--shadow-float);
position: relative;
}
.crate-card::before {
content: '';
position: absolute;
top: -6px;
left: 40%;
width: 80px;
height: 22px;
background: var(--color-tape);
border: 1px solid var(--color-tape-border);
}
.crate-card-region {
font-family: var(--font-display);
font-size: 0.65rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--color-accent);
margin-bottom: 0.5rem;
}
.crate-card-title {
font-family: var(--font-display);
font-size: 1.6rem;
text-transform: uppercase;
color: var(--color-ink);
margin-bottom: 0.8rem;
}
.crate-card-notes {
font-size: 0.95rem;
color: var(--color-ink-faded);
line-height: 1.7;
margin-bottom: 1.2rem;
}
.crate-card-meta {
display: flex;
gap: 2rem;
}
.crate-card-meta-item {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-ink-faded);
}
.crate-card-meta-item strong {
display: block;
font-family: var(--font-display);
font-size: 1.1rem;
color: var(--color-ink);
margin-top: 0.15rem;
}
.crate-origins-cards .crate-card:nth-child(1) { transform: rotate(-0.5deg); }
.crate-origins-cards .crate-card:nth-child(2) { transform: rotate(0.3deg); margin-left: 2rem; }
.crate-origins-cards .crate-card:nth-child(3) { transform: rotate(-0.8deg); margin-left: 1rem; }
@media (max-width: 768px) {
.crate-nav {
padding: 1rem 1.5rem;
}
.crate-nav-links { display: none; }
.crate-nav-hamburger { display: block; }
.crate-nav-drawer {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: var(--color-cardboard);
z-index: 99;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2rem;
}
.crate-nav-drawer-close {
position: absolute;
top: 1rem;
right: 1.5rem;
}
.crate-nav-drawer-link {
font-family: var(--font-display);
font-size: 1.2rem;
letter-spacing: 0.08em;
text-transform: uppercase;
text-decoration: none;
color: var(--color-ink);
}
.crate-hero {
padding: 7rem 1.5rem 3rem;
}
.crate-hero-layout {
grid-template-columns: 1fr;
gap: 3rem;
}
.crate-hero h1 {
font-size: 2.8rem;
}
.crate-hero-package {
transform: rotate(1deg);
padding: 2rem;
}
.crate-origins {
padding: 4rem 1.5rem;
}
.crate-origins-inner {
grid-template-columns: 1fr;
gap: 2rem;
}
.crate-origins-aside {
position: static;
}
.crate-origins-cards .crate-card:nth-child(2),
.crate-origins-cards .crate-card:nth-child(3) {
margin-left: 0;
}
.crate-process {
padding: 3.5rem 1.5rem;
}
.crate-process-steps {
flex-direction: column;
gap: 2rem;
}
.crate-process-steps::before {
display: none;
}
.crate-process-step {
text-align: left;
display: flex;
gap: 1rem;
align-items: flex-start;
padding: 0;
}
.crate-step-num {
margin: 0;
flex-shrink: 0;
}
.crate-step-desc {
max-width: none;
}
.crate-testimonials {
padding: 4rem 1.5rem;
}
.crate-testimonial-grid {
grid-template-columns: 1fr;
}
.crate-testimonial:nth-child(1) {
grid-row: auto;
}
.crate-testimonial:nth-child(3) {
margin-top: 0;
}
.crate-pricing {
padding: 4rem 1.5rem;
}
.crate-pricing-boxes {
flex-direction: column;
align-items: center;
}
.crate-price-box {
max-width: 100%;
width: 100%;
}
.crate-price-box:nth-child(2) {
transform: rotate(0.5deg);
}
.crate-newsletter {
padding: 3.5rem 1.5rem;
}
.crate-newsletter-form {
flex-direction: column;
}
.crate-newsletter-input {
border-right: 2px solid var(--color-ink-faded);
border-bottom: none;
}
.crate-footer-inner {
grid-template-columns: 1fr;
gap: 2rem;
}
.crate-footer-bottom {
flex-direction: column;
gap: 0.5rem;
text-align: center;
}
.crate-showcase {
padding: 6rem 1.5rem 3rem;
}
}Button
Kraft-paper button with primary, secondary, ghost, and accent variants in three sizes. Archivo Black labels, warm cardboard palette. Renders as a Next.js Link when an href is provided.
Badge
Artisanal badge with accent, ink, kraft, stamp, and faded variants. Uppercase tracking on warm cardboard surfaces.
Text
Polymorphic text component with body, caption, label, and code variants. Archivo Black for display, DM Sans for body.
Card
Origin card with shipping label aesthetic, tape motif, dashed borders, and slight rotation transform. Displays weight, roast, origin, and date fields.