All projects

Case study

Bibliopile

A digital bookshelf for the books you’ve read, listened to or DNF’d, arranged and styled the way you would a real one.

Role
Product design, branding and full-stack engineering
Team
Solo project
Timeline
2026
Bibliopile landing page asking "Out of shelf space?" next to four large book spines: organise, import, choose a theme, customise spines

The problem

Reading trackers show your books as a grid of covers or a spreadsheet. That works for logging, but you lose the fun of a real bookshelf, like arranging it and watching it grow.

And if you read e-books or listen to audiobooks, those never make it onto a real bookshelf at all.

What I did

  • Came up with the idea, designed the UI and brand, and built the whole app: frontend, Supabase schema and auth, and the Hardcover API integration.
  • Built importers for Goodreads, StoryGraph and Hardcover exports, so people can bring their existing library with them.

Key decisions

  1. 1.Generating book spines from data

    Real spine images don’t exist for most books, and covers squashed into a strip look wrong. Every spine is rendered from the book’s data instead: width comes from page count, height from series and length, and the colours and fonts come from a theme.

    Books can stand, lean, lie flat in a stack, or face out to show their cover. Leaning books use a little trigonometry to work out how much space they need, so a tilted book doesn’t overlap its neighbours.

    Two shelves of generated spines in different widths and heights, with leaning books, a stacked Lord of the Rings trilogy, and The Hobbit and Project Hail Mary facing out
    Spine width follows page count. Books can lean, stack or face out, and their neighbours make room for them.

    Tradeoff: Generated spines are less faithful to the real book, but every book on a shelf looks like part of the same set, and users can override sizes and styles per book.

  2. 2.Testable drag and drop

    Dragging one or several books within a shelf or between shelves had a lot of edge cases, and the bugs were hard to reproduce by hand.

    I separated the decision from the UI: a pure function takes the drag event and current shelves and returns a plan (reorder in place, or move across shelves). The UI applies that plan optimistically and syncs it to the database. As the app grew, I moved shared state from React Context to Zustand stores so drag, filter and display state stopped re-rendering the whole library.

    Moving books between shelf sections. The planner works out where they land. The UI updates straight away and saves in the background.

    Tradeoff: The planner is more code than handling everything in the drag handler, but it’s covered by unit tests and the drag bugs stopped coming back.

  3. 3.Readable spine themes

    Users can pick spine themes and customise background colours, text colours and fonts. Free choice of colours makes it easy to end up with unreadable spines.

    Each theme is checked against a minimum contrast ratio, and text colours are paired with backgrounds that pass it.

    Edit appearance panel with the Fantasy theme’s background colours, text colours and fonts, next to shelves in editing mode
    The theme editor. Background and text colours are paired so spine titles stay readable.

    Tradeoff: I set the floor at 3:1 (WCAG’s large-text level) rather than 4.5:1. Spine titles are short and decorative, and the stricter level ruled out most of the rich, muted colours that make a shelf look like a real one.

Outcome

Bibliopile is live, with public shareable shelves, light and dark themes, and imports from the three main reading trackers.

It was my most UI-heavy project so far. It taught me to move complex interaction logic into small functions that I could test on their own.

What's next

  • Making the shelf and editing tools work well on mobile.
  • Integrating Hardcover OAuth, so people can connect their Hardcover account directly.

More screens

Bibliopile library with three shelves and a book details sidebar
Library with book details
Bulk import screen listing 72 books from a Hardcover export, with checkboxes, read status and ratings
Bulk import from Goodreads, Hardcover or StoryGraph
Search results for Murderbot showing book covers with ratings and Add to library buttons
Searching the Hardcover database to add books