BBSL Appspiration

A mobile-first auction app that runs free agency for a 30-team fantasy basketball league, with the app as referee so the commissioner can play.

Client
BBSL fantasy basketball league
Role
Commissioner, sole designer and developer
Year
2026
Disciplines
Product design, Interaction design, UI design, Full-stack development
The Your Positions screen on a phone, in a deep green interface. A strip at the top reads Roster 7 of 12, 2 of 6 bids, Maximum Bid $18.0M. Below, Derrick White is listed as won for $16.5M, and two amber Outbid cards follow. Saddiq Bey's says the next legal bid of $4.0M is within reach; Chet Holmgren's says you cannot re-enter at $21.5M, which exceeds your Maximum Bid of $18.0M by $3.5M.

The problem

The BBSL is a 30-team salary dynasty fantasy basketball league. Every offseason its free agents are sold in an open auction. Managers nominate players and bid in $500K steps, and every bid resets a 24-hour clock. A hard $165M salary cap and a 12-player roster limit apply throughout. An opening bid of exactly $1M starts a lottery other teams can join, and the winners then spend a fixed allotment of contract lengths.

For five offseasons all of that ran by hand. Somebody had to notice when a clock expired, check that a team could afford the bid it had just posted, and remember who had already used their four-year contract. When somebody got it wrong at 1am, thirty people found out in the morning. That somebody was usually me: I’m the league’s commissioner, and I also manage a team in it.

Appspiration is the app I designed and built to take that job over. The league used it to run its 2026 free agency.

The app is the referee, so the commissioner can play.

Who it’s for

Thirty managers across US, European and Australian time zones, all adults with jobs who check their phones between other things. The auction runs as a relay: one cluster bids before bed, and the next wakes up to find they’ve been outbid. Their needs were short and concrete. Don’t let me lose a player because I wasn’t looking. Tell me what I can afford without making me do arithmetic. Let me bid from my phone in ninety seconds.

So the app is designed for a phone held in one hand, at 375px first. Touch targets are at least 44px, and the bid field is 46px. Desktop gets density, and mobile loses nothing. The commissioner tools work at phone width too, because an outage happens wherever the commissioner happens to be.

There was a second constraint, and it shaped more of the design than the screen size did. The app is built and run by one of the competitors. Every rule it enforces had to be checkable by the people it was enforced against.

The refusal is the product

Placing a bid is the easy part. The moment that decides whether thirty people trust an app built by a rival is being told no at 4am. I designed that moment first. Every refusal has the same parts, in the same order:

  1. A plain headline: “This bid was not placed.”
  2. How far over, in money: $20.0M exceeds your Maximum Bid of $18.0M by $2.0M.
  3. Reassurance that nothing changed. No event was written, the leader didn’t move, and no clock was touched.
  4. Every gate, passed or refused. The cap and the roster limit are always reported together, so a pass is proof that the check ran.
  5. The arithmetic, with the time it was calculated. Every amount sits on the $500K grid, so the one-decimal figures are exact and the breakdown visibly adds up.

What can I afford?

The number every manager needs is the Maximum Bid: the most they can legally offer right now. It’s cap space, minus every bid they’re currently leading, $1M for each lottery entry, and $1M reserved for each roster spot still to fill. It sits in a strip at the top of every screen, next to the roster count and the number of bids in play. Each auction page breaks it down.

The Chet Holmgren auction page. The price card shows $21.0M led by Minnesota Timberwolves, Northstar, with 22h 49m left. Below it, a Maximum Bid card lists Cap Space $32.0M, Committed Bids minus $13.0M, Roster Reserve minus $1.0M and Maximum Bid $18.0M, with a link to show the full calculation.
The Maximum Bid as a sum the manager can check. The full calculation opens beneath it.

The same arithmetic runs on the Your Positions screen, which is where a Discord “you’ve been outbid” mention lands. Each outbid card says in words whether it’s worth coming back. In the screenshot at the top of this page, Saddiq Bey’s next legal bid of $4.0M is within reach. Chet Holmgren’s is $21.5M, which is $3.5M over the limit, and the card says so before the manager has to work it out.

Calm on purpose

An auction app could easily feel like a casino, so I wrote down what this one would never do: no pulsing countdowns, one-tap raises, suggested amounts, “ending soon” banners or celebration animations. Every bid needs a second, deliberate confirmation. Bid volume is listed in the product spec as something not to optimise.

Every time is shown twice: as time remaining, and as a local time in the viewer’s own time zone. Amber is the only attention colour, and it marks outbids and refusals and nothing else. Colour never carries state alone, and the acceptance test was that a greyscale screenshot of any screen had to stay fully readable.

On the Bid Board, each state has its own colour and shape: amber for Outbid, a pale green kept apart from the brand mint for Leading, and a periwinkle bar for the lottery. The other goal was fitting as many auctions as possible on a phone screen. The cards are flat, with no padding spent on shadows, and each fits its facts into four tight rows. Closed auctions collapse to a single row.

The Bid Board sorted by price. Payton Pritchard at $4.5M has a pale green left edge and a filled pale green Leading chip; Saddiq Bey at $3.5M has a filled amber Outbid chip; Neemias Queta at $1.0M has a periwinkle left bar, a diamond with the word Minimum, and Contender, with 4 Contenders so far. Below, Devin Booker reads Unbid with no opening bid, and the Closed group begins. Each card lists the leading team and manager, time left and the local close time.
Leading, Outbid and a Minimum lottery side by side. Each state has its own colour, plus a shape and a word, so it reads the same in greyscale.

The visual identity is a joke told with a straight face. The app dresses as a sustainable-banking startup, after Aspiration, the company at the centre of the alleged Clippers salary-cap circumvention, while being built so that nobody can get round the cap. The tone follows: refusals read the way a bank declines a transfer, and there are no exclamation marks anywhere in the app.

A lottery anyone can check

When a player opens at exactly $1M, any team can join at the same price, and the winner is drawn when the clock runs out. A draw run by a competing commissioner needs proof that it’s fair. The app uses commit-reveal. When the lottery starts, it publishes a hash of a secret seed. At the draw it reveals the seed, and the winner follows from that seed and the entry order. A public page explains how to re-run any draw by hand.

A closed lottery for Jay Huff, won by Memphis Grizzlies for $1.0M. A periwinkle-edged Minimum Lottery panel shows a long published commitment hash, the revealed seed, the line The draw selected position 2 of 3, and the entrants Indiana Pacers, Memphis Grizzlies marked Selected, and Toronto Raptors, with a link: How to check this draw yourself.
The commitment was public before the draw, so nobody, including me, could change the outcome after the fact.

Building it

Appspiration is a SvelteKit app on Supabase’s Postgres, running on free tiers with no budget. Every action is an event in an append-only log, so the audit trail is the system’s own record, not a report written afterwards. Writes go through one at a time behind a single lock. Locking each auction separately wouldn’t be enough, because two co-managers bidding on different players at once could jointly break their team’s cap. The rules live in a pure core with no clock or database access, and the worked examples in the product spec run as tests. The suite has 4,335 of them.

Discord handles sign-in and notifications, because that’s where the league already talks. Fantrax, the league’s host, has no write API, so the app never writes to it. Every 30 minutes a read-only check flags any roster that has drifted from Fantrax, and the commissioner confirms each correction.

I built it on my own, about four weeks from the first spec to the live auction. I used an AI-assisted, spec-first workflow: a product spec, a set of numbered architecture decisions and a UX spec came first, then small stories with layered review.

In use

Before the league saw it, the app ran two pilots with seven league moderators, then a third with all 30 managers. Eight of the nine bids in the first two pilots came from phones or tablets. The live auction opened in mid-September 2026, and real use found what the spec had missed. One was Brooklyn winning a player it only had room for because a starter sat in an injury reserve slot against league rules, which led to a commissioner tool for reversing a closed auction. Another was a minor-league rule that wrongly let teams bid past their cap on eligible players. It was fixed mid-auction, and no settled result needed repair.

Reflection

Designing the refusal first changed everything after it. Once the hardest message had a fixed shape, every other screen inherited its habits: show the arithmetic, name the cause before the effect, and never leave a manager guessing whether something happened. The other lesson came from the live auction. The spec went through eleven revisions, several of them while managers were bidding. An append-only log made those changes safe, because correcting the past only ever meant adding to it.

Screenshots are from a local copy of the app seeded with simulated auction activity. Apart from mine, the manager names are invented.