Bookshelf
Books stand spine out. The one you open turns on its spine to show its cover, and its neighbours slide aside by exactly the space it takes.
Open a book: click its spine, or Tab to it and press Enter. Arrow keys move along the shelf; Escape closes the book.
Features
- The turn is CSS: one registered angle animates from 0 to 90 degrees, and the book's width (spine x cos + cover x sin) and its 3D turn are both computed from it, so the neighbours move aside by exactly the cover's room.
- Hover and focus lift a book; the open one stays raised.
- One book open at a time, controlled or not; arrow keys move along the shelf, Escape closes.
- Each book has its own height, thickness and colour; the look is plain CSS at zero specificity, so a site's own classes win.
Use it for
- A reading list.
- Albums, records, case files: anything with a spine and a cover.
On this site
- My Stuff: the shelvesA reading list you take off the shelf one book at a time, its cover turning out to meet you.
Installation
- Get runets.
git clone https://github.com/ziyam-khan/runets - Copy these files into your project, keeping their folders.
- Install what they use.
pnpm add react - Load the styles once. Bring your own base tokens, or use runets’ default theme.
import 'runets/styles/theme.css' import 'runets/styles/kit.css'
API
| Prop | Type | What it does |
|---|---|---|
books | { id, title, by?, height, thickness, coverWidth?, color?, ink?, spine?, cover? }[] | spine and cover replace what each face shows. |
open / onOpenChange | string | null | The open book, controlled or not. |
label | string | Names the shelf. |
Accessibility
- Each book is a button with aria-expanded and the title and author as its name.
- The cover is inert while it faces away, so focus and screen readers never reach a hidden face.
- Escape closes the open book without closing a dialog around the shelf.
- Reduced motion opens a book in place, without the turn.
Source
The code is in src/kit/bookshelf.tsx in runets, MIT licensed.