From 3a1a5ff8131492dc7afa2c83fb4d5cfd50145328 Mon Sep 17 00:00:00 2001
From: Robert Andreas Kyllo
Date: Mon, 14 Sep 2026 20:56:45 +0200
Subject: [PATCH 1/3] feat: refine responsive book viewer layout (#33)
---
src/App.css | 32 ++++++++++++++++++++++++++++++++
src/App.tsx | 6 ++++--
src/components/BookCard.css | 16 ++++++++++++++--
src/components/BookCard.tsx | 14 ++++++++++++--
4 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/src/App.css b/src/App.css
index 7188ed3..12675f8 100644
--- a/src/App.css
+++ b/src/App.css
@@ -29,6 +29,20 @@
padding: 1.5rem;
}
+.library-controls {
+ display: grid;
+ gap: 1rem;
+ max-width: 42rem;
+ margin: 0 auto 1.5rem;
+}
+
+.library-controls .subject-filter,
+.library-controls .sort-select {
+ width: 100%;
+ max-width: none;
+ margin: 0;
+}
+
.book-viewer {
max-width: 32rem;
margin: 0 auto;
@@ -40,3 +54,21 @@
color: #616e7c;
font-size: 0.875rem;
}
+
+@media (min-width: 42rem) {
+ .library-controls {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 32rem) {
+ .app-header,
+ .app-main {
+ padding-right: 1rem;
+ padding-left: 1rem;
+ }
+
+ .app-header h1 {
+ font-size: 1.5rem;
+ }
+}
diff --git a/src/App.tsx b/src/App.tsx
index 25a4c54..ed9215b 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -41,8 +41,10 @@ function App() {
Browse reading material from OpenLibrary, one book at a time.
-
-
+
+
+
+
{isLoading && Loading books…
}
{isError && Could not load books. Please try refreshing the page.
}
{!isLoading && !isError && books.length === 0 && (
diff --git a/src/components/BookCard.css b/src/components/BookCard.css
index a5aad97..243c922 100644
--- a/src/components/BookCard.css
+++ b/src/components/BookCard.css
@@ -1,6 +1,6 @@
.book-card {
display: grid;
- grid-template-columns: minmax(8rem, 12rem) 1fr;
+ grid-template-columns: minmax(8rem, 12rem) minmax(0, 1fr);
gap: 1.5rem;
overflow: hidden;
border: 1px solid #d5dde5;
@@ -11,6 +11,7 @@
.book-card-cover {
display: grid;
+ align-self: stretch;
min-height: 12rem;
place-items: center;
background-color: #edf2f7;
@@ -35,6 +36,7 @@
.book-card-content h2 {
margin: 0;
color: #243b53;
+ overflow-wrap: anywhere;
}
.book-card-content p {
@@ -56,11 +58,20 @@
}
.book-card-subjects li {
+ min-width: 0;
+ max-width: 100%;
border-radius: 999px;
background-color: #d9e2ec;
color: #243b53;
padding: 0.25rem 0.625rem;
font-size: 0.875rem;
+ overflow-wrap: anywhere;
+}
+
+.book-card-subjects .book-card-subjects-more {
+ background-color: #edf2f7;
+ color: #52606d;
+ font-weight: 600;
}
@media (max-width: 32rem) {
@@ -69,7 +80,8 @@
}
.book-card-cover {
- min-height: 16rem;
+ min-height: 0;
+ aspect-ratio: 4 / 3;
}
.book-card-content {
diff --git a/src/components/BookCard.tsx b/src/components/BookCard.tsx
index 72d95fd..7518ce4 100644
--- a/src/components/BookCard.tsx
+++ b/src/components/BookCard.tsx
@@ -3,6 +3,8 @@ import type { Book } from "../types";
import { FavoriteButton } from "./FavoriteButton";
import "./BookCard.css";
+const MAX_VISIBLE_SUBJECTS = 5;
+
interface BookCardProps {
book: Book;
isFavorite?: boolean;
@@ -12,6 +14,8 @@ interface BookCardProps {
export function BookCard({ book, isFavorite = false, onFavoriteToggle }: BookCardProps) {
const imageUrl = coverUrl(book.coverId);
const authors = book.authors.length > 0 ? book.authors.join(", ") : "Unknown author";
+ const visibleSubjects = book.subjects.slice(0, MAX_VISIBLE_SUBJECTS);
+ const remainingSubjectCount = book.subjects.length - visibleSubjects.length;
return (
@@ -27,10 +31,16 @@ export function BookCard({ book, isFavorite = false, onFavoriteToggle }: BookCar
{authors}
{book.firstPublishYear !== null && First published: {book.firstPublishYear}
}
{book.subjects.length > 0 && (
-
- {book.subjects.map((subject) => (
+
+ {visibleSubjects.map((subject) => (
- {subject}
))}
+ {remainingSubjectCount > 0 && (
+ - +{remainingSubjectCount} more
+ )}
)}
{onFavoriteToggle && (
From d99cf50d0cc9883eee2b7516571f88e66c9d0002 Mon Sep 17 00:00:00 2001
From: Robert Andreas Kyllo
Date: Mon, 14 Sep 2026 20:57:09 +0200
Subject: [PATCH 2/3] test: cover limited book subject tags (#33)
---
src/__snapshots__/App.test.tsx.snap | 136 +++++++++---------
src/components/BookCard.test.tsx | 17 ++-
.../__snapshots__/BookCard.test.tsx.snap | 2 +-
3 files changed, 87 insertions(+), 68 deletions(-)
diff --git a/src/__snapshots__/App.test.tsx.snap b/src/__snapshots__/App.test.tsx.snap
index 69cfc6c..99d18d7 100644
--- a/src/__snapshots__/App.test.tsx.snap
+++ b/src/__snapshots__/App.test.tsx.snap
@@ -18,76 +18,80 @@ exports[`App > matches the snapshot 1`] = `
-
-
+
+
+
+
+
+
+
+
matches the snapshot 1`] = `
1968
-
diff --git a/src/components/BookCard.test.tsx b/src/components/BookCard.test.tsx
index 4e74de9..e7d853f 100644
--- a/src/components/BookCard.test.tsx
+++ b/src/components/BookCard.test.tsx
@@ -28,7 +28,9 @@ describe("BookCard", () => {
expect(screen.getByRole("heading", { name: fullBook.title })).toBeInTheDocument();
expect(screen.getByText("Ursula K. Le Guin, Another Author")).toBeInTheDocument();
expect(screen.getByRole("img", { name: "Cover of A Wizard of Earthsea" })).toBeInTheDocument();
- expect(screen.getByRole("list", { name: "Subjects" })).toHaveTextContent("Fantasy");
+ expect(screen.getByRole("list", { name: "Subjects, showing 2 of 2" })).toHaveTextContent(
+ "Fantasy",
+ );
expect(container).toMatchSnapshot();
});
@@ -41,4 +43,17 @@ describe("BookCard", () => {
expect(screen.queryByRole("img")).not.toBeInTheDocument();
expect(container).toMatchSnapshot();
});
+
+ it("limits long subject lists and shows how many subjects remain", () => {
+ const bookWithManySubjects: Book = {
+ ...fullBook,
+ subjects: ["Fantasy", "Magic", "Adventure", "Dragons", "Wizards", "Earthsea"],
+ };
+
+ render();
+
+ expect(screen.getByRole("list", { name: "Subjects, showing 5 of 6" })).toBeInTheDocument();
+ expect(screen.getByText("+1 more")).toBeInTheDocument();
+ expect(screen.queryByText("Earthsea")).not.toBeInTheDocument();
+ });
});
diff --git a/src/components/__snapshots__/BookCard.test.tsx.snap b/src/components/__snapshots__/BookCard.test.tsx.snap
index 3d54a76..f60ba5c 100644
--- a/src/components/__snapshots__/BookCard.test.tsx.snap
+++ b/src/components/__snapshots__/BookCard.test.tsx.snap
@@ -29,7 +29,7 @@ exports[`BookCard > renders a complete book 1`] = `
1968
-
From 404de02fb235418541ce7f82fc212fd7af0abf22 Mon Sep 17 00:00:00 2001
From: Robert Andreas Kyllo
Date: Wed, 16 Sep 2026 10:26:44 +0200
Subject: [PATCH 3/3] fix: improve mobile book card contrast (#33)
---
src/components/BookCard.css | 4 ++++
src/components/FavoriteButton.css | 1 +
2 files changed, 5 insertions(+)
diff --git a/src/components/BookCard.css b/src/components/BookCard.css
index 243c922..3ab6294 100644
--- a/src/components/BookCard.css
+++ b/src/components/BookCard.css
@@ -85,6 +85,10 @@
}
.book-card-content {
+ position: relative;
+ z-index: 1;
padding: 0 1.25rem 1.25rem;
+ border-top: 1px solid #d5dde5;
+ background-color: #ffffff;
}
}
diff --git a/src/components/FavoriteButton.css b/src/components/FavoriteButton.css
index 5007bc2..e443cfe 100644
--- a/src/components/FavoriteButton.css
+++ b/src/components/FavoriteButton.css
@@ -1,5 +1,6 @@
.favorite-button {
align-self: flex-start;
+ margin-top: 1rem;
padding: 0.5rem 0.75rem;
border: 1px solid #cbd2d9;
border-radius: 0.375rem;