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.
#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
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.
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.
| Token | Light | Dark | Use for |
|---|---|---|---|
| bg | #FFFFFF | #0A0A0A | The page itself. |
| bg-alt | #F9FAFB | #111111 | Anything inset: panels, inputs, chips, tab tracks. |
| card | #FFFFFF | #18181B | Cards. Always give it a border — in light mode it matches the page. |
| border | #E5E7EB | #27272A | Every line and divider. |
| text | #374151 | #A1A1AA | Body copy. |
| text-heading | #111827 | #FAFAFA | Headings, input values, numbers you want read. |
| muted | #6B7280 | #71717A | Labels and secondary text. Don’t go lighter — this is the floor for AA contrast. |
| primary | #F97316 | #FB923C | Brand fill that lifts in dark mode. Pair with primary-fg. |
| primary-fg | #FFFFFF | #0A0A0A | Text on top of a primary fill. |
| accent | #F97316 | #FB923C | Links. |
Status
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.
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.
- Headings are
font-extrabold; UI isfont-semibold; de-emphasised text isfont-medium. tracking-tighton page headings, fromtext-3xlup. 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.
| Radius | Use for |
|---|---|
| rounded-lg | Inputs, textareas, selects, small buttons, tab tracks. |
| rounded-xl | Default buttons, square icon tiles, inset panels, alerts, toasts, dropdowns. |
| rounded-2xl | Cards, modals, empty states, token and profile artwork. |
| rounded-full | Pills, nav chrome, round avatars, progress bars, spinners, status dots. |
| rounded-md | Badges 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.
<!-- 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.)
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>- Variants —
solidis the one real call to action per view,accentis the quieter branded option,neutralis the workhorse for secondary actions,outlineandghostare for rows of equal-weight choices. - Sizes —
sminside dense UI,defaulteverywhere else,herofor the landing call to action. - The size sets the radius:
smisrounded-lg,defaultisrounded-xl,herois a pill. Don’t override it. - Use
asChildfor links so an<a>gets the full treatment instead of a button-shaped copy of it. - Tap targets are 40px at
smand 44px atdefault, built in — exceptghostatdefault, which opts out on purpose so it can sit inline in a row of links.
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
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
<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:
1200pxfor index and dashboard pages (the same width as the navbar and footer),900pxfor single-column reading pages,720pxfor focused flows. Two pages sit outside on purpose — the token page is full-bleed, and the launch form runs its own640px→1040pxpair. - Page padding —
px-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 padding —
p-5, orp-4when dense. - Gaps — mostly
gap-2,gap-3,gap-4, withgap-1.5for icon-and-label pairs. Grids go togap-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-colorswith no explicit duration is the default. - Lift is
hover:-translate-y-0.5when something really should lift. Scale is reserved for imagery inside a card ongroup-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-motionon 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.