Reference

Documentation

Technical reference for the Bonker token factory.

Design System

Everything you need to build something that looks like it belongs on Bonker — the mark, the colours, the type, the shapes, and the components we ship. Every swatch and every button below is rendered by the real thing, so what you see here is what the site actually uses.

The short version. Orange #F97316 on near-black, Inter, five border radii, and a border instead of a shadow. If you copy one thing, copy the token block at the bottom.

Logo

Bonker logobonker
bonker

The lockup is the mark plus the wordmark as live text, never as an image. The wordmark is lowercase bonker, bold, in the body typeface, and it takes the page’s heading colour so it flips with the theme. Header size is a 36px mark with a 10px gap; footer size is 28px with an 8px gap.

  • /logo.svg is the only logo file, and it carries exactly one colour: #F97316.
  • The mark does not change between light and dark. The gap inside it is transparent, not white, which is what keeps it readable on a dark background.
  • Don’t recolour, rotate, outline or add effects. If it has to sit on artwork, put it on a dark scrim rather than restyling it.
  • Writing the name out in running text? It’s bonker.wtf anywhere the reader can’t see the mark next to it.

Colour

Brand ramp

Ten fixed steps, the same in both themes. bonker-500 is the brand orange in light mode and bonker-400 in dark; bonker-600 is the hover state on a solid fill.

bonker-50
#FFF7ED
bonker-100
#FFEED5
bonker-200
#FDD7AA
bonker-300
#FDBA74
bonker-400
#FB923C
bonker-500
#F97316
bonker-600
#EA580C
bonker-700
#C2410C
bonker-800
#9A3412
bonker-900
#7C2D12

Surfaces and text

These are the only colours that change between themes. Reach for the name, not the hex, and never for a raw grey scale — the whole point of the table is that one class works in both modes.

TokenLightDarkUse for
bg#FFFFFF#0A0A0AThe page itself.
bg-alt#F9FAFB#111111Anything inset: panels, inputs, chips, tab tracks.
card#FFFFFF#18181BCards. Always give it a border — in light mode it matches the page.
border#E5E7EB#27272AEvery line and divider.
text#374151#A1A1AABody copy.
text-heading#111827#FAFAFAHeadings, input values, numbers you want read.
muted#6B7280#71717ALabels and secondary text. Don’t go lighter — this is the floor for AA contrast.
primary#F97316#FB923CBrand fill that lifts in dark mode. Pair with primary-fg.
primary-fg#FFFFFF#0A0A0AText on top of a primary fill.
accent#F97316#FB923CLinks.

Status

green-400
#34D399
red-400
#F87171
amber-400
#FBBF24
purple-400
#C084FC
neon-400
#22D3EE

Green is up or good, red is down or broken, amber is a warning that isn’t a failure, purple is locked or time-gated. Those never bend. Cyan (neon-*) is a second brand accent for presale, launchpad and referral surfaces — not a status colour. Orange never means success or failure; it means “this is Bonker”.

To tint anything by status, use the same three-part recipe the components use: bg-{colour}-500/10 for the fill, border-{colour}-500/20 for the edge, text-{colour}-400 for the label.

Those six families — bonker, neon, green, red, amber, purple— are the palette, and all of them are in the theme block at the bottom of this page. Three things sit outside it on purpose: plain white and black at low alpha, for chrome laid over artwork where a theme-following colour would vanish; third-party brand colours (Farcaster #8A63D2, Telegram #229ED9), which must not follow our theme; and the flat near-black the 404 artwork sits on. Anything else you reach for is not a Bonker colour.

One trap worth naming. Tailwind ships its own orange-400 / orange-500 at #FF8904 / #FF6900— noticeably hotter than ours, and close enough to look like a mistake rather than a choice. Anything orange goes through bonker-*.

Type

font-family: 'Inter', -apple-system, BlinkMacSystemFont,
             'Segoe UI', system-ui, sans-serif;
font-size: 16px;
line-height: 1.6;

Inter, and nothing else — there is no second display face. Worth knowing if you copy this: Bonker declares Inter as src: local('Inter') and ships no webfont file, so a visitor who does not already have Inter installed falls through to the system stack. Self-host a woff2 if you want it guaranteed. Addresses, hashes and numeric readouts go in the monospace stack.

Page heading
text-3xl md:text-4xl · font-extrabold · tracking-tight
Section heading
text-2xl · font-extrabold
Card title
text-lg · font-semibold
Panel title
text-sm · font-semibold
Body copy sits at text-sm in UI and 16px in prose.
text-sm · text-muted
Micro label
text-[10px] · font-semibold · uppercase · tracking-wider
  • Headings are font-extrabold; UI is font-semibold; de-emphasised text is font-medium.
  • tracking-tight on page headings, from text-3xl up. Below that, leave tracking alone.
  • Uppercase always ships with a tracking-* class. Uppercase without extra letter-spacing looks broken at these sizes.
  • Paragraphs get leading-relaxed.

Shape

Five steps, chosen by what the thing is rather than by eye. This is the rule most worth following exactly — mixed corner radii are what makes an interface look assembled rather than designed.

rounded-md
6px
nested only
rounded-lg
8px
dense controls
rounded-xl
12px
default
rounded-2xl
16px
cards
rounded-full
9999px
pills
RadiusUse for
rounded-lgInputs, textareas, selects, small buttons, tab tracks.
rounded-xlDefault buttons, square icon tiles, inset panels, alerts, toasts, dropdowns.
rounded-2xlCards, modals, empty states, token and profile artwork.
rounded-fullPills, nav chrome, round avatars, progress bars, spinners, status dots.
rounded-mdBadges and tags, small square controls like checkboxes, and the inner segment of a rounded-lg track.

That is the whole scale: no sixth radius utility and no arbitrary value, anywhere in the app or the stylesheet. If something seems to want a corner between two steps, it wants one of the two steps.

Two rules follow from it. When you nest one rounded box inside another, the inner one drops a step — a rounded-2xl card holds rounded-xl panels — because equal corners inside equal corners read as a mistake rather than a choice. And a leaf component should never bake in its own radius: take it from the call site, so the same avatar can be square in one place and rounded in another without an !important fight.

Surfaces

Three levels, bottom to top: the page (bg-bg), the card sitting on it (bg-card), and anything inset into the card (bg-bg-alt). Depth comes from a border, not a shadow — prefer border border-border and reach for a shadow only when something genuinely floats, like a modal or a menu.

bg-bg — page
bg-card — card (rounded-2xl, p-5)
bg-bg-alt — inset panel (rounded-xl, p-4)
<!-- card -->
<div class="bg-card border border-border rounded-2xl p-5">
  <!-- panel inside it -->
  <div class="bg-bg-alt border border-border rounded-xl p-4">…</div>
</div>

Chrome that floats over an image is the one exception: a low-alpha white border (border-white/15) on a black scrim of bg-black/55/65, with a backdrop blur — a theme-following border simply vanishes against artwork. Glass chrome like the navbar is bg-bg/80 plus backdrop-blur-lg.

Components

Three shared components, in client/components/ui/. Button carries the radius, the focus ring, the disabled state and the hover; Input carries the focus border and the disabled state; Badge is purely presentational. Reaching for them is faster than restyling a bare element, and it is the only way to get the focus ring for free.

Button

Five general-purpose variants and three sizes, below. (Two more exist for the Genesis campaign pages: a genesis size, and a genesis-glow variant with an animated frame that bypasses the shared class string and therefore has no focus ring. Neither is meant for general use.)

variant="solid" size="default"
variant="accent" size="default"
variant="neutral" size="default"
variant="outline" size="hero"
variant="ghost" size="default"
variant="solid" size="hero"
variant="solid" size="sm"
variant="neutral" size="sm"
variant="ghost" size="sm"
import { Button } from '@/components/ui/button.jsx';

<Button variant="solid" size="default">Launch</Button>

// as a link
<Button asChild variant="neutral" size="sm">
  <Link href="/explore">Explore</Link>
</Button>
  • Variantssolid is the one real call to action per view, accent is the quieter branded option, neutral is the workhorse for secondary actions, outline and ghost are for rows of equal-weight choices.
  • Sizessm inside dense UI, default everywhere else, hero for the landing call to action.
  • The size sets the radius: sm is rounded-lg, default is rounded-xl, hero is a pill. Don’t override it.
  • Use asChild for links so an <a> gets the full treatment instead of a button-shaped copy of it.
  • Tap targets are 40px at sm and 44px at default, built in — except ghost at default, which opts out on purpose so it can sit inline in a row of links.
Building a button by hand? Give it the app-btn class. Bonker’s own stylesheet restyles RainbowKit’s buttons to match the site, and to do it, it force-pills every <button> inside a [data-rk] subtree that is not marked. The wallet stack mounts automatically for anyone with a prior wallet session — so an unmarked button looks right in a fresh browser and wrong for returning visitors, which is a hard bug to see. Button carries the marker on every render path.

Badge

defaultsuccessdestructiveneutral
warninglockedpillpill
import { Badge } from '@/components/ui/badge.jsx';

<Badge variant="success">Live</Badge>
<Badge variant="success" shape="pill">Live</Badge>

Six variants covering every status meaning, and two shapes: tag (the default, square-ish) and pill. Badges are small caps — 10px, semibold, uppercase, with letter-spacing. Keep the text to a word or two; anything longer wants to be a label, not a badge.

Input

import { Input } from '@/components/ui/input.jsx';

<label class="block text-xs font-medium text-muted mb-1.5">
  Ticker <span class="text-bonker-500">*</span>
</label>
<Input placeholder="BONK" />
<p class="text-xs text-red-400 mt-1.5">Ticker is required.</p>

Focus is a border-colour change to bonker-500, not a ring. Errors go underneath the field in text-red-400; the field itself keeps its normal border.

Card

relative overflow-hidden bg-card border border-border rounded-2xl
transition-colors hover:border-bonker-400/30

That’s the whole card recipe — on Bonker it’s wrapped in a component that adds a cursor-tracking glow, but the string above is what gives you the shape. Padding is the call site’s job: p-5 by default, p-4 when it’s dense, p-6 or p-8 for a hero or feature card.

Page eyebrow

Live index
Explore
<p class="page-kicker"><i aria-hidden="true"></i> Live index</p>

The small orange label above a page heading, with a pulsing dot when the thing below it is live. The dot is a bare <i> and takes its colour and glow from the label, so it needs no classes of its own. It respects prefers-reduced-motion. Add page-kicker--block if something inline precedes it on the same line.

Loading

<div class="w-6 h-6 border-2 border-bonker-500 border-t-transparent
            rounded-full animate-spin"
     role="status" aria-label="Loading"></div>

w-6 for a page or panel, w-4 in white inside a solid button, w-3 inline. Skeletons are animate-pulse on a bg-bg-alt block at whatever radius the real content will have.

Layout

  • Page width — three tiers: 1200px for index and dashboard pages (the same width as the navbar and footer), 900px for single-column reading pages, 720px for focused flows. Two pages sit outside on purpose — the token page is full-bleed, and the launch form runs its own 640px1040px pair.
  • Page paddingpx-5 md:px-8, unless the page is full-bleed.
  • Below the navbar — 48px before the first block, on every route.
  • Intro paragraph under a heading — max-w-xl, so it stays readable.
  • Card paddingp-5, or p-4 when dense.
  • Gaps — mostly gap-2, gap-3, gap-4, with gap-1.5 for icon-and-label pairs. Grids go to gap-5 / gap-6.

Card grids collapse at sm (grid-cols-1 sm:grid-cols-2) and button rows go vertical at the same point (flex-col sm:flex-row). Breakpoints are Tailwind’s defaults, unchanged.

Motion

  • Hover is a colour change, not movement. transition-colors with no explicit duration is the default.
  • Lift is hover:-translate-y-0.5 when something really should lift. Scale is reserved for imagery inside a card on group-hover.
  • Entrances are opacity plus a small upward offset, 0.4–0.6s.
  • Decoration doesn’t loop. Shimmer and float run a couple of times and stop. Only live-status dots and skeletons run forever.
  • Honour prefers-reduced-motion on anything you animate.

Dark mode

Dark is the default. The theme is a dark class on <html>, so it follows the site’s own toggle rather than the operating system. Build with the surface and text tokens above and both themes work with no extra effort — if you find yourself writing a dark: override to fix a colour, you almost certainly wanted a token instead.

Copy the tokens

Drop this into a Tailwind v4 project and you have the Bonker palette, both themes, with all the class names used on this page.

@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));

@theme {
  --color-bonker-50:  #FFF7ED;
  --color-bonker-100: #FFEED5;
  --color-bonker-200: #FDD7AA;
  --color-bonker-300: #FDBA74;
  --color-bonker-400: #FB923C;
  --color-bonker-500: #F97316;
  --color-bonker-600: #EA580C;
  --color-bonker-700: #C2410C;
  --color-bonker-800: #9A3412;
  --color-bonker-900: #7C2D12;

  --color-neon-300: #67E8F9;
  --color-neon-400: #22D3EE;
  --color-neon-500: #06B6D4;

  --color-green-400:  #34D399;   /* up / good      */
  --color-green-500:  #10B981;
  --color-red-400:    #F87171;   /* down / broken  */
  --color-red-500:    #EF4444;
  --color-red-600:    #DC2626;
  --color-amber-300:  #FCD34D;   /* warning        */
  --color-amber-400:  #FBBF24;
  --color-amber-500:  #F59E0B;
  --color-amber-700:  #B45309;   /* warning text on a light background */
  --color-purple-300: #D8B4FE;   /* locked         */
  --color-purple-400: #C084FC;
  --color-purple-500: #A855F7;

  /* light */
  --color-bg:           #FFFFFF;
  --color-bg-alt:       #F9FAFB;
  --color-card:         #FFFFFF;
  --color-border:       #E5E7EB;
  --color-text:         #374151;
  --color-text-heading: #111827;
  --color-muted:        #6B7280;
  --color-primary:      #F97316;
  --color-primary-fg:   #FFFFFF;
  --color-accent:       #F97316;
}

@layer base {
  html { color-scheme: light; }

  html.dark {
    color-scheme: dark;
    --color-bg:           #0A0A0A;
    --color-bg-alt:       #111111;
    --color-card:         #18181B;
    --color-border:       #27272A;
    --color-text:         #A1A1AA;
    --color-text-heading: #FAFAFA;
    --color-muted:        #71717A;
    --color-primary:      #FB923C;
    --color-primary-fg:   #0A0A0A;
    --color-accent:       #FB923C;
  }

  body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont,
                 'Segoe UI', system-ui, sans-serif;
    font-size: 16px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
  }
}

Declaring color-scheme on both selectors is what makes scrollbars, native dropdowns and autofill follow the theme — those are browser chrome that CSS can’t otherwise reach. And binding the dark variant to the class is what stops dark: utilities from quietly following the operating system while your tokens follow the toggle.