Skip to the main content
onedollarpixel
The gridAdvertisersAboutRulesFAQ
Documentation / Design system

outbid design system

Read this before ANY visual change. Colour, typography, spacing, borders, surfaces, radius, shadow, layout, motion, component styling — all of it.

For a template, the default look is a product feature. A buyer judges the purchase on a screenshot and restyles the board before they touch anything else. Every value below is named so they can change it in one place.

The board's public name comes from board.name in outbid.config.ts. The visual tokens below were captured from a live board on 2026-08-21. The values here are read off a running site, not invented. An earlier draft of this file guessed at a cool blue-grey system and got the brand, the row component, and the money colour wrong. Where you are unsure, match the screenshots.


1. The brand idea — warm, friendly, and competitive

The surprise, and the thing to protect: a site about people spending thousands of dollars to beat each other does not look aggressive. It looks like warm paper. Cream ground, coral accents, generous rounding, soft tinted cards.

That contrast is the design. The warmth makes a $10,000 bid feel like a game rather than an invoice, and a game is something you play twice.

Two words to design against: warm and alive. Cream instead of white or grey. Coral instead of blue. Rounded instead of square. A live visitor count in the first 40 pixels.

The anti-brand. Not a fintech dashboard. Not a dark "trading floor". No blue, no grey-on-grey density, no hard corners, no data-table look. If it starts feeling like an admin panel, it has drifted.


2. Token sources — the only place values live

ConcernFileRule
Colour, radius, shadowsrc/styles/tokens.cssChange values here, never in a component
Type rolessrc/styles/globals.cssComponents use role classes, never raw sizes
Product copy and numbersoutbid.config.tsBoard name, tagline, prices, page size

2.1 Colour

Coral is the entire accent system. One hue does the work that a blue, a green and a red would do elsewhere.

:root {
  /* Ground — warm cream, NOT white and NOT grey */
  --background: #fdf9f6;
  --surface: #ffffff; /* the board container */

  /* Ink */
  --foreground: #1f2328;
  --muted-foreground: #6b7280; /* descriptions, timestamps */

  /* Coral — primary, and the colour of money */
  --primary: #f4622f;
  --primary-foreground: #ffffff;
  --primary-soft: #f9a68d; /* the Outbid button before input */

  /* Podium — the top three */
  --podium-surface: #fde4db; /* tinted card ground */
  --podium-border: #f8c8b6;
  --podium-badge: #f9b8a4; /* the #1 #2 #3 pill */
  --podium-badge-fg: #b8391a;

  /* Structure */
  --border: #ece7e3; /* hairline between rows 4+ */
  --ring: #f4622f;

  --live: #22c55e; /* the "visitors online" dot */

  --radius-card: 14px; /* listing cards, board container */
  --radius-control: 999px; /* inputs, buttons, pills — FULL round */
}

:root[data-theme='dark'] {
  --background: #14100e;
  --surface: #1c1815;
  --foreground: #f6f1ed;
  --muted-foreground: #a29a94;
  --primary: #ff7a4d;
  --primary-foreground: #1a0d07;
  --primary-soft: #7a3a26;
  --podium-surface: #3a201a;
  --podium-border: #5c3125;
  --podium-badge: #6b3524;
  --podium-badge-fg: #ffb499;
  --border: #2b2521;
  --ring: #ff7a4d;
  --live: #4ade80;
}

Money is ALWAYS --primary. Every price, at every rank, from $10,000 down to $65. This is the opposite of the usual advice and it is right here: the price _is_ the rank, so colouring it makes the board scannable as a single coral column. Do not grade the colour by amount — rank 50 is a paying customer.

Controls are fully round (--radius-control: 999px). The input, the Outbid button, the rank pills, the pagination, the takeover button. Cards use 14px. Nothing in this system has a square corner.

2.2 Type

One face, matching the reference board.

FaceWhy
DisplayInter (variable)The wordmark and the claim headline match the reference board
Body & dataInter (variable)Descriptions, prices, meta. Superb at small sizes and for numerals, which is most of this page

The original board uses Inter for everything. The public board uses the same family so the claim, signals, and listing cards keep the reference proportions.

The face is set once, on <body>, by next/font. Type roles set size and weight only. Never name a local Inter in a fallback stack: a Mac with Inter installed will mix that cut with the committed file. The committed file is the Inter latin variable cut, weights 100–900.

No monospace anywhere. An earlier draft of this file specified monospace prices; the real board uses proportional numerals, and right-alignment is what keeps the column readable.

RoleFaceSize / lineWeightUsed for
type-claimInter44/52700"Claim #1 for $10001" — the biggest thing on the page
type-wordmarkInter26/26600The board name
type-navInter14/20600Public header links: Leaderboard, About, Rules, FAQ
type-ledeInter20/30400The two-line tagline under the counter
type-titleInter28/36600/rules, /operator, inner pages
type-domainInter16/24600The listing's domain
type-bodyInter16/26400Listing descriptions
type-priceInter18/24600Every money value. Coral. Right-aligned
type-metaInter14/20400"10 hours ago", pagination count, footer
type-clicksInter14/20600The click count. Darker than its timestamp

Body is 16px and meta is 14px, not 15 and 13. The reference uses the smaller sizes and they fall below the readable floor — 13px carries the click count, which is the evidence a bid was worth paying for. The visual difference is nearly invisible; the legibility difference is not.

Never above weight 700. The claim headline is the only 700 on the page.

The click count is bolder than the timestamp beside it, because the click count is proof and the timestamp is only context.

2.3 Geometry, spacing, motion

  • Spacing scale: 4 8 12 16 24 32 48 64.
  • Page is centered, max 840px. Everything, including the hero. This is a single-column product.
  • Controls: 48px for the main bid input and Outbid button (they are the primary action); 32px for secondary pills like Refresh and Take over.
  • Shadows: none on cards. Separation is tint, border, and space.
  • Motion: 150ms ease-out on hover and focus. A rank change animates to its new position over 200ms. The success page plays one coral confetti burst.
  • prefers-reduced-motion: reduce makes the rank change instant and hides the confetti.

3. The listing — a card, and the top three are special

This is the component that matters. Get it right and the product looks right.

PODIUM  (ranks 1-3) — tinted card, coral border, rank as a disc
┌──────────────────────────────────────────────────────────────────────┐
│  ╭──╮   ┌────┐  trycomp.ai                                           │
│  │1 │   │icon│  Automate SOC 2, ISO 27001, HIPAA, and GDPR. 580+     │
│  ╰──╯   └────┘  integrations, 1,000+ companies, audit-ready in days. │
│  48px           10 hours ago · ● 7913 clicks              $10,000    │
└──────────────────────────────────────────────────────────────────────┘
   ↑ filled coral disc for rank 1. Rank 2 is 40px podium-badge.
     Rank 3 is 32px podium-badge. Same 48px column so rows line up.

REST  (ranks 4+) — no card, no tint, hairline separator, plain number
    4    ┌────┐  joinklover.com                                        │
         │icon│  Need cash fast? Klover lets you get a cash advance…   │
         └────┘  10 hours ago · ● 1614 clicks               $2,000     │
   ─────────────────────────────────────────────────────────────────────

The podium promotion is the best idea in this design. Rank 1 is a filled --primary disc. Ranks 2 and 3 are smaller --podium-badge discs. Ranks 4 and below are a plain number in the same 48px column. The size gap is what a bidder buys. All podium rows still sit in a coral-tinted card.

Both live inside one white --surface container with --radius-card, sitting on the cream --background. The container is what makes the board read as one object rather than a list of floating cards.

Anatomy, in reading order: rank, icon, domain, description, meta, price. The price is right-aligned and vertically centred against the whole card.

  • Icon: 40px, --radius-card, real favicon. Missing icon → a 40px square in --muted with the domain's first letter. That is a designed state, not a fallback; a hole in the column is worse than a letter.
  • Description: up to two lines, then ellipsis. Not one line.
  • Meta line: 10 hours ago · ● 1614 clicks — the dot is --primary.
  • No per-row claim button. Bidding happens once, at the top of the page. An earlier draft put a "claim this rank" button on every row; that is 50 competing calls to action and it is not what the real board does.

4. The page, top to bottom

Centred, single column, max 840px.

BandContentNotes
WordmarkRank-bar mark + board.name, left-alignedSticky cream bar. Hairline. Inner 840px. Same mark as the favicon.
Live pill● 2,250 visitors online · 31,715 in the last 12 hours · see stats→Green dot. Rounded pill on a tinted ground
LedeTwo centred lines. Second half in coralThe coral half is the hook: "Will you take #1 when this site goes viral?"
ClaimClaim #1 for ⊖ $10001 ⊕The largest text on the page. The stepper buttons are round and coral-tinted
HelperTwo muted centred linesExplains that a lower bid still ranks
Bid formOne full-width round input with a globe icon + attached Outbid buttonThe button is --primary-soft until the input has content
Helper 2"Already on the list? Enter the same URL and up your bid."
TakeoverCentred rounded strip, coral text, Take over buttonNew: prefix in coral
RefreshSmall round pill, right-aligned above the board
BoardWhite container: 3 podium cards + rows§3
Pagination‹ 1 2 3 4 … 7 › as round buttons, active filled coral, centred1 - 50 of 321 beneath in meta
FooterCreator strip, then board name, version, legal links, social marksCreator and social marks only when saved. Official glyphs.

The claim price is the loudest thing on the page, above the fold, before the board. A visitor must know what winning costs before they scroll.

4.1 The badge and share card are distribution, not decoration

The badge (E2) is an SVG, 160x32, cream ground, coral rank, board name. It must be legible on a light and a dark host page, so it ships in both themes. Nobody pastes an ugly badge, and a badge nobody pastes is a growth loop that never runs.

The share card (E3) is 1200x630 on the cream ground with the coral rank as the largest element, domain second, board name third. It has to read as a 300px thumbnail in a timeline, so the rank cannot be under a quarter of the height.

4.2 The empty board is a designed state

A new buyer's board has zero listings. That is the most common first impression this template will ever make. "No listings yet" would be a product failure at the exact moment it matters.

The empty board keeps the claim headline, sets it to the minimum bid, and says rank 1 is open. An invitation, not an apology. See docs/cold-start.md.


4.3 The operator console — App UI rules, not the board's rules

The board is a marketing composition. The console is App UI and gets the opposite treatment: calm, dense, minimal chrome, one accent. A back office dressed in cream-and-coral hero styling looks unsuitable. This screen controls listings, payment checks, settings, and the audit log.

┌ /operator ────────────────────────────────────────────────────────┐
│  Listings   Payments   Settings   Activity   [ drift: 0 ] [ sign out]│
├───────────────────────────────────────────────────────────────────┤
│  [ search domain… ]         status: all ▾    kind: all ▾           │
├──┬──────────────┬─────────┬────────┬─────────┬────────────────────┤
│# │ domain       │ total   │ clicks │ status  │                    │
│1 │ trycomp.ai   │ $10,000 │  7,913 │ live    │ hide  refuse  ↻    │
│2 │ lathire.com  │  $3,100 │  1,398 │ live    │ hide  refuse  ↻    │
│4 │ spam.example │      $2 │      0 │ refused │ unhide        ↻    │
└──┴──────────────┴─────────┴────────┴─────────┴────────────────────┘

Rules for this surface, all deliberate departures from the board:

  • Surface is --surface, ground is --surface. No cream, no tint, no podium treatment. Rank is a plain number.
  • Radius is 6px, not 999px. Controls are rectangles here. Round pills on a data table read as toys.
  • Coral appears exactly twice: the active nav item, and a non-zero reconciliation drift. Nothing else. On this screen coral means _look here_.
  • Density over comfort. 36px rows, 14px type, hairline separators.
  • Destructive actions confirm. refuse opens a dialog that names the listing. A wrong selection removes the listing from the board.
  • Every action writes to operator_audit (spec §35, E-4) and the row shows who did it and when. The console is the accountability surface, so it has to show its own history.
  • Reconciliation drift is the one number that is always visible, in the header, on every tab. Zero is quiet; non-zero is coral and links to the report.

4.4 The journey, and where the money is

STEP | USER DOES              | USER FEELS        | WHAT THE DESIGN DOES
-----|------------------------|-------------------|--------------------------
1    | Lands on the board     | curiosity         | Live counter + the price of
     |                        |                   | #1 above the fold, in 5 sec
2    | Scrolls the board      | competitiveness   | Podium tint makes the top 3
     |                        |                   | look like a prize
3    | Reads a description    | recognition       | "these are real companies"
4    | Types a URL            | commitment        | One input, one button, no
     |                        |                   | signup between them
5    | Pays                   | small anxiety     | Stripe, a known surface
6    | Returns                | relief            | "Payment received" even
     |                        |                   | before the webhook lands
7    | Sees their rank        | satisfaction      | Their row briefly highlighted
8    | **Gets outbid**        | **irritation**    | **The email. This is step 8
     |                        |                   | and it is where the revenue
     |                        |                   | actually is**
9    | Re-bids                | back to step 4    | Pay only the difference

Time horizons. Five seconds: the price of rank 1 and a live counter, so the board is obviously real and obviously contested. Five minutes: the click count, which is the evidence the money bought traffic. Five months: the badge on their own site, which keeps the board in their eyeline long after the bid.

Step 8 is the product. Everything before it happens once. The design's job after step 7 is to make losing a rank feel like something you can fix for one dollar.

4.5 Public site composition

The public site is the live board. Do not put a separate sales site in front of it. A visitor must see the current rank price and the real listing data.

Use one public header on the board and the public documents. Keep the header sticky so Leaderboard, About, Rules, and FAQ stay in view. Put the legal links in the shared footer. The footer also shows the operator creator strip and the filled social profile marks when those settings have values.

The home page uses this order:

  1. Show the board identity and promise.
  2. Show the current price and the bid form.
  3. Show recent rank movement and click proof.
  4. Show the optional takeover and throne offers.
  5. Show the ranked board.
  6. Show page controls and the public footer.

The live activity panels use only database values. Never publish an invented visitor count, revenue number, customer logo, or testimonial.

The About page explains this board. It uses four live fact tiles instead of a fake growth chart. Money facts use --primary. It does not identify a founder or an operator until the operator saves a name, a photo, or an X handle.

Rules, Terms, Refunds, and Privacy use the same document frame. The prose stays in content/. The frame owns navigation, related links, and the public footer. Document copy uses the full 840px frame. Do not nest a second, narrower measure inside it.

The public pages use the board tokens and type roles in this document. Do not create a second marketing token system.

Visual rhythm

Do not build an endless card grid. Use a strong claim, one compact proof area, the live board, and quiet document surfaces.

Cards remain appropriate for podium rows, the two live signal panels, and the four live About fact tiles. A plain border or open space separates all other public sections.

Conversion rule

Use Set your rank for the main board action. Use Read the rules for the secondary action. Keep one purchase form on the page.

Links from a listing or a signal row can explain that listing. They do not open a second checkout form.


5. Interaction state coverage

A blank cell is a bug.

SurfaceLoadingEmptyErrorSuccessPartial
BoardSkeleton cards at real height§4.2 invitation"Board unavailable, retry" — never blankRanked cardsNo icon → letter square
Bid formButton spinner, input disabledn/aNamed message per spec §19Redirect to StripeMetadata pending → host shown
Return from Stripe"Confirming your payment…"n/a"Payment received, rank applying"New rank, briefly highlightedWebhook late → poll, never claim failure
Takeover stripSkeletonHidden when none liveHiddenCountdown to expiry—
Operator tableSkeleton"No listings yet"Error banner + retryToast—
Badge / card—Board name onlyBoard name onlyRank—

The third row matters most. Somebody has paid; the webhook may be seconds behind. The naive build shows "listing not found" to a paying customer. This screen polls and says _payment received_, because the payment is real even when the row has not moved.


6. Responsive

Mobile first. The page is already one narrow column, so mobile is mostly a scale-down rather than a re-layout.

DESKTOP ≥768   [#1] [icon]  domain / 2-line description / meta        $10,000
MOBILE  <768   [#1] [icon]  domain                                    $10,000
                            description (2 lines)
                            meta

The claim price and the bid form stay above the fold at every width. The price never wraps and never shrinks. Touch targets are 44px minimum, which is why the stepper and pagination buttons get more padding on mobile than their desktop size.


7. Accessibility

  • The bid input has a VISIBLE label. The reference uses placeholder-as-label ("Your product URL or @handle" and nothing else), which disappears the moment somebody types and leaves a screen-reader user with an unnamed field on the one control that completes a purchase. Ours keeps a visible label above the input. This is a deliberate divergence from the reference and it is not negotiable.
  • Body is 16px, meta is 14px (§2.2). Nothing carrying meaning goes below 14.
  • Contrast: coral #f4622f on cream #fdf9f6 is the pair to watch. It passes AA for the 18/24 weight-600 price as large text. It does NOT pass AA at small sizes, so coral is never used for small body copy — only for prices, links, and the meta dot.
  • The live dot and the coral meta dot are decoration. Never encode meaning in colour alone; the click count is always written out.
  • Keyboard: form, stepper, every card link, and pagination reachable in visual order. Focus ring --ring at 2px, never removed.
  • Screen readers: each listing is a list item announcing rank, domain, and amount in that order.
  • Live updates (E1): the board is aria-live="polite". A rank change must not steal focus.
  • Skip link to the board, before the wordmark.

8. Reach-for order

  1. An existing token.
  2. An existing component in src/components/ — extend with a prop.
  3. A new primitive, only if two surfaces need it, and retrofit every copy-paste.
  4. A new token, only when the change is global, changed at source in tokens.css.

An ad-hoc hex, font size, or spacing number in a component is a defect.


9. Do

  • Keep the ground cream and the accent coral.
  • Colour every price, at every rank.
  • Give the top three the tinted card and the pill; give the rest a plain row.
  • Round every control fully.
  • Put the claim price above the fold.
  • Design the empty board before the full one.

10. Don't

  • Don't use blue, or a grey-on-grey dashboard palette.
  • Don't square a corner.
  • Don't add a shadow to a card.
  • Don't put a claim button on every row.
  • Don't use monospace for prices.
  • Don't use coral for small text.
  • Don't grade the price colour by amount.
  • Don't animate anything except a rank change and the one success-page burst.
  • Don't add a hero image, a feature grid, or a testimonial section.
All documentationBack to the board

onedollarpixel · rank is the bid. · v2.13.1

AboutRulesFAQTermsDocs