A beautiful 3D bookshelf component with cover peek on hover. Perfect for showcasing your reading list on your personal site.
- 3D book spines with cover peek on hover
- Automatic book sizing based on page count
- Reading progress indicators
- Book covers from Open Library API
- Built-in loading state with fun messages
- Optional modal for book details
- Zero dependencies
- TypeScript support
- Random tilt, spacing, and colors (no manual configuration needed!)
npm install simpleshelfOr via CDN:
<link
rel="stylesheet"
href="https://unpkg.com/simpleshelf/dist/simpleshelf.css"
/>
<script src="https://unpkg.com/simpleshelf/dist/simpleshelf.js"></script>Just provide a simple array of books - that's it! Tilt, spacing, and colors are automatically randomized if not provided.
import SimpleShelf from 'simpleshelf';
import 'simpleshelf/dist/simpleshelf.css';
const books = [
{ title: 'Project Hail Mary', author: 'Andy Weir', isbn: '9780593135204' },
{ title: 'Dark Matter', author: 'Blake Crouch', isbn: '9781101904220' },
{ title: 'The Passage', author: 'Justin Cronin', isbn: '9780345504968' }
];
const container = document.getElementById('bookshelf');
const shelf = SimpleShelf.createShelf(books);
container.appendChild(shelf);const books = [
{
title: 'Project Hail Mary',
author: 'Andy Weir',
isbn: '9780593135204',
pages: 496,
color: '#1C1C1C',
progress: 75,
status: 'Reading'
},
{
title: 'Dark Matter',
author: 'Blake Crouch',
isbn: '9781101904220',
pages: 342,
color: '#0D1B2A',
progress: 100,
status: 'Finished'
}
];
const shelf = SimpleShelf.createShelf(books, {
showProgress: true,
onBookClick: (book) => console.log('Clicked:', book.title)
});<!DOCTYPE html>
<html>
<head>
<link
rel="stylesheet"
href="https://unpkg.com/simpleshelf/dist/simpleshelf.css"
/>
</head>
<body>
<div id="bookshelf"></div>
<script src="https://unpkg.com/simpleshelf/dist/simpleshelf.js"></script>
<script>
const books = [
{
title: 'Project Hail Mary',
author: 'Andy Weir',
isbn: '9780593135204'
},
{ title: 'Dark Matter', author: 'Blake Crouch', isbn: '9781101904220' }
];
const container = document.getElementById('bookshelf');
const shelf = SimpleShelf.default.createShelf(books);
container.appendChild(shelf);
</script>
</body>
</html>| Property | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Book title |
author |
string | Author name | |
isbn |
string | ISBN (fetches cover from Open Library) | |
pages |
number | Page count (affects book size, default 300) | |
color |
string | Spine color (hex, random if not provided) | |
progress |
number | Reading progress 0-100 | |
status |
string | Status text (e.g., "Reading", "TBR") | |
url |
string | URL to open when book is clicked |
Note: tilt and gap are automatically randomized - no need to specify them!
Creates a shelf element with books.
const shelf = SimpleShelf.createShelf(books, {
align: 'left', // 'left' | 'center' | 'right'
showProgress: true, // Show reading progress bars
onBookClick: (book, element) => {} // Click handler
});Creates a single book element.
const bookEl = SimpleShelf.createBook(
{
title: 'My Book',
isbn: '1234567890'
},
{
showProgress: true,
onClick: (book, element) => {}
}
);Convert minimal book input(s) into full book objects with all properties filled in.
const fullBook = SimpleShelf.normalizeBook({ title: 'My Book' });
// Returns: { title: 'My Book', author: '', isbn: '', pages: 300, color: '#...', tilt: 2, gap: 28, ... }Shows a loading message while book covers are loading from Open Library.
<div id="bookshelf-wrapper" class="simpleshelf-wrapper--loading">
<div id="bookshelf"></div>
</div>
<div id="bookshelf-loading" class="simpleshelf-loading">
<span class="simpleshelf-loading__text"></span>
</div>
<script>
// After creating your shelf...
SimpleShelf.initLoadingState('bookshelf-wrapper', 'bookshelf-loading');
</script>Initialize a modal for displaying book details on click.
const modal = SimpleShelf.initModal('book-modal');
const shelf = SimpleShelf.createShelf(books, {
onBookClick: (book) => modal?.open(book)
});
// Later: modal?.close() to close programmaticallySimpleShelf.getThickness(pages); // Calculate book width from pages
SimpleShelf.getHeight(pages); // Calculate book height from pages
SimpleShelf.muteColor(hex); // Desaturate a color
SimpleShelf.getTextColor(bg); // Get contrasting text color
SimpleShelf.isLightColor(hex); // Check if color needs dark text
SimpleShelf.getRandomLoadingMessage(); // Get a random book-themed loading messageFull TypeScript support included:
import SimpleShelf, {
BookInput,
Book,
ShelfOptions,
BookOptions,
ModalController
} from 'simpleshelf';
const books: BookInput[] = [{ title: 'My Book', isbn: '1234567890' }];
const modal: ModalController | null = SimpleShelf.initModal('book-modal');
const options: ShelfOptions = {
showProgress: true,
onBookClick: (book: Book) => modal?.open(book)
};
const shelf = SimpleShelf.createShelf(books, options);Override CSS variables:
:root {
--shelf-accent: #d97757;
--shelf-bg: #f0eee9;
--shelf-text: #1a1a1a;
}MIT - Katrina Tantay
