From e470c5b9fe6a804c39586ea7100e778167635b01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ilse=20=C3=85sbakk=20Resell?=
Date: Thu, 8 Oct 2026 08:58:56 +0200
Subject: [PATCH 01/18] feat: add Bookish favicon + icon file cleanup
Add bookishFavicon.svg in public folder. Change favicon path in index.html.
Clean up unnecessary comments and formatting in a couple icon files. No change to funcitonality.
---
client/index.html | 2 +-
client/public/bookishFavicon.svg | 6 ++++++
client/src/icons/BrowseIcon.tsx | 1 -
client/src/icons/SparkleIcon.tsx | 2 --
client/src/icons/StarIcon.tsx | 1 +
5 files changed, 8 insertions(+), 4 deletions(-)
create mode 100644 client/public/bookishFavicon.svg
diff --git a/client/index.html b/client/index.html
index f3dcf16..0738587 100644
--- a/client/index.html
+++ b/client/index.html
@@ -2,7 +2,7 @@
-
+
t14-project-2
diff --git a/client/public/bookishFavicon.svg b/client/public/bookishFavicon.svg
new file mode 100644
index 0000000..8c84107
--- /dev/null
+++ b/client/public/bookishFavicon.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/client/src/icons/BrowseIcon.tsx b/client/src/icons/BrowseIcon.tsx
index 8d6c212..5cb1a70 100644
--- a/client/src/icons/BrowseIcon.tsx
+++ b/client/src/icons/BrowseIcon.tsx
@@ -1,4 +1,3 @@
-/* ---------- Props ---------- */
type Props = { size?: number };
/* ---------- BrowseIcon: open book (nav tab for Browse) ---------- */
diff --git a/client/src/icons/SparkleIcon.tsx b/client/src/icons/SparkleIcon.tsx
index 8476bb6..990fa32 100644
--- a/client/src/icons/SparkleIcon.tsx
+++ b/client/src/icons/SparkleIcon.tsx
@@ -1,8 +1,6 @@
-/* ---------- SparkleIcon.tsx ---------- */
type Props = { size?: number; className?: string };
/* ---------- SparkleIcon: four-pointed sparkle, color follows the parent's text color ---------- */
-// size is optional: leave it out when CSS (className) sets the width/height
export function SparkleIcon({ size, className }: Props) {
return (
);
-}
\ No newline at end of file
+}
diff --git a/client/src/components/MyNotesPanel/MyNotesPanel.module.css b/client/src/components/MyNotesPanel/MyNotesPanel.module.css
index 53389d4..bcdf524 100644
--- a/client/src/components/MyNotesPanel/MyNotesPanel.module.css
+++ b/client/src/components/MyNotesPanel/MyNotesPanel.module.css
@@ -94,4 +94,4 @@
display: flex;
align-items: center;
gap: var(--space-2);
-}
\ No newline at end of file
+}
diff --git a/client/src/components/MyNotesPanel/MyNotesPanel.tsx b/client/src/components/MyNotesPanel/MyNotesPanel.tsx
index 6acc14b..0a61597 100644
--- a/client/src/components/MyNotesPanel/MyNotesPanel.tsx
+++ b/client/src/components/MyNotesPanel/MyNotesPanel.tsx
@@ -140,4 +140,4 @@ export function MyNotesPanel({
)}
);
-}
\ No newline at end of file
+}
diff --git a/client/src/components/ShelfRibbon/ShelfRibbon.module.css b/client/src/components/ShelfRibbon/ShelfRibbon.module.css
index a76f1b8..71bb001 100644
--- a/client/src/components/ShelfRibbon/ShelfRibbon.module.css
+++ b/client/src/components/ShelfRibbon/ShelfRibbon.module.css
@@ -35,4 +35,4 @@
fill: var(--color-ribbon);
stroke: color-mix(in srgb, var(--color-on-accent) 80%, transparent);
stroke-width: 1;
-}
\ No newline at end of file
+}
diff --git a/client/src/components/ShelfRibbon/ShelfRibbon.tsx b/client/src/components/ShelfRibbon/ShelfRibbon.tsx
index 731b4be..58dfdcd 100644
--- a/client/src/components/ShelfRibbon/ShelfRibbon.tsx
+++ b/client/src/components/ShelfRibbon/ShelfRibbon.tsx
@@ -21,4 +21,4 @@ export function ShelfRibbon({ title, isSaved, onToggle, className }: ShelfRibbon
);
-}
\ No newline at end of file
+}
diff --git a/client/src/components/StarRatingInput/StarRatingInput.module.css b/client/src/components/StarRatingInput/StarRatingInput.module.css
index ff7fc38..c8e6fd1 100644
--- a/client/src/components/StarRatingInput/StarRatingInput.module.css
+++ b/client/src/components/StarRatingInput/StarRatingInput.module.css
@@ -18,8 +18,12 @@
}
/* Star colors */
-.starOn { fill: var(--color-star); }
-.starOff { fill: var(--color-star-off); }
+.starOn {
+ fill: var(--color-star);
+}
+.starOff {
+ fill: var(--color-star-off);
+}
/* ===== Hover preview ===== */
/* Only on devices that can really hover, so a tap on a phone doesn't leave the preview stuck */
@@ -35,4 +39,4 @@
.starButton:hover ~ .starButton .starOff {
fill: var(--color-star-off);
}
-}
\ No newline at end of file
+}
diff --git a/client/src/components/StarRatingInput/StarRatingInput.tsx b/client/src/components/StarRatingInput/StarRatingInput.tsx
index b658b14..f9ad629 100644
--- a/client/src/components/StarRatingInput/StarRatingInput.tsx
+++ b/client/src/components/StarRatingInput/StarRatingInput.tsx
@@ -34,4 +34,4 @@ export function StarRatingInput({ value, onChange }: StarRatingInputProps) {
))}
);
-}
\ No newline at end of file
+}
diff --git a/client/src/data/mockBooks.ts b/client/src/data/mockBooks.ts
index 82fed61..18f7f1e 100644
--- a/client/src/data/mockBooks.ts
+++ b/client/src/data/mockBooks.ts
@@ -1,16 +1,16 @@
-import type {Book} from '../types/book';
+import type { Book } from '../types/book';
import booksJson from '../../../server/test-data/books.json';
export type ShelfEntry = {
- bookId: Book['id'];
- rating?: number;
- note?: string;
- updatedAt?: string; // ISO date, set whenever the note is saved
+ bookId: Book['id'];
+ rating?: number;
+ note?: string;
+ updatedAt?: string; // ISO date, set whenever the note is saved
};
-export const mockBooks: Book[] = booksJson.map(({googleBooksId, ...rest}) => ({
- id: googleBooksId,
- ...rest,
+export const mockBooks: Book[] = booksJson.map(({ googleBooksId, ...rest }) => ({
+ id: googleBooksId,
+ ...rest,
}));
-export const mockShelfEntries: ShelfEntry[] = [];
\ No newline at end of file
+export const mockShelfEntries: ShelfEntry[] = [];
diff --git a/client/src/icons/BrowseIcon.tsx b/client/src/icons/BrowseIcon.tsx
index c390b59..8cb1dec 100644
--- a/client/src/icons/BrowseIcon.tsx
+++ b/client/src/icons/BrowseIcon.tsx
@@ -17,4 +17,4 @@ export function BrowseIcon({ size = 22 }: BrowseIconProps) {
);
-}
\ No newline at end of file
+}
diff --git a/client/src/icons/ChevronIcon.tsx b/client/src/icons/ChevronIcon.tsx
index b1508fc..50a5ec3 100644
--- a/client/src/icons/ChevronIcon.tsx
+++ b/client/src/icons/ChevronIcon.tsx
@@ -17,4 +17,4 @@ export function ChevronIcon({ size = 20 }: ChevronIconProps) {
);
-}
\ No newline at end of file
+}
diff --git a/client/src/icons/FilterIcon.tsx b/client/src/icons/FilterIcon.tsx
index ee63589..83d2f5f 100644
--- a/client/src/icons/FilterIcon.tsx
+++ b/client/src/icons/FilterIcon.tsx
@@ -16,4 +16,4 @@ export function FilterIcon({ size = 16 }: FilterIconProps) {
);
-}
\ No newline at end of file
+}
diff --git a/client/src/icons/LogoIcon.tsx b/client/src/icons/LogoIcon.tsx
index 0de327b..4c6369f 100644
--- a/client/src/icons/LogoIcon.tsx
+++ b/client/src/icons/LogoIcon.tsx
@@ -3,13 +3,7 @@ type LogoIconProps = { size?: number };
/* ---------- LogoIcon: open book with a gold sparkle (header logo) ---------- */
export function LogoIcon({ size = 30 }: LogoIconProps) {
return (
-
+
{/* Book outline and spine take the surrounding text color */}
);
-}
\ No newline at end of file
+}
diff --git a/client/src/icons/RibbonIcon.tsx b/client/src/icons/RibbonIcon.tsx
index ad8917d..584273b 100644
--- a/client/src/icons/RibbonIcon.tsx
+++ b/client/src/icons/RibbonIcon.tsx
@@ -7,4 +7,4 @@ export function RibbonIcon({ width = 24, height = 40 }: RibbonIconProps) {
);
-}
\ No newline at end of file
+}
diff --git a/client/src/icons/SearchIcon.tsx b/client/src/icons/SearchIcon.tsx
index 6ee35ac..cd1992a 100644
--- a/client/src/icons/SearchIcon.tsx
+++ b/client/src/icons/SearchIcon.tsx
@@ -17,4 +17,4 @@ export function SearchIcon({ size = 18 }: SearchIconProps) {
);
-}
\ No newline at end of file
+}
diff --git a/client/src/icons/ShelfIcon.tsx b/client/src/icons/ShelfIcon.tsx
index 4a25850..ca85066 100644
--- a/client/src/icons/ShelfIcon.tsx
+++ b/client/src/icons/ShelfIcon.tsx
@@ -16,4 +16,4 @@ export function ShelfIcon({ size = 22 }: ShelfIconProps) {
);
-}
\ No newline at end of file
+}
diff --git a/client/src/icons/SparkleIcon.tsx b/client/src/icons/SparkleIcon.tsx
index 2b84fcf..17d2d88 100644
--- a/client/src/icons/SparkleIcon.tsx
+++ b/client/src/icons/SparkleIcon.tsx
@@ -3,17 +3,8 @@ type SparkleIconProps = { size?: number; className?: string };
/* ---------- SparkleIcon: four-pointed sparkle, color follows the parent's text color ---------- */
export function SparkleIcon({ size, className }: SparkleIconProps) {
return (
-
-
+
+
);
-}
\ No newline at end of file
+}
diff --git a/client/src/icons/StarIcon.tsx b/client/src/icons/StarIcon.tsx
index c091139..f76e34a 100644
--- a/client/src/icons/StarIcon.tsx
+++ b/client/src/icons/StarIcon.tsx
@@ -3,14 +3,8 @@ type StarIconProps = { size?: number; className?: string };
/* ---------- StarIcon: rating star, color comes from the className's CSS (fill) ---------- */
export function StarIcon({ size = 15, className }: StarIconProps) {
return (
-
+
);
-}
\ No newline at end of file
+}
diff --git a/client/src/main.tsx b/client/src/main.tsx
index 34dbef9..57df51d 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -1,10 +1,10 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import './styles/index.css'
-import { App } from './App.tsx'
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import './styles/index.css';
+import { App } from './App.tsx';
createRoot(document.getElementById('root')!).render(
,
-)
+);
diff --git a/client/src/pages/BookDetailPage/BookDetailPage.module.css b/client/src/pages/BookDetailPage/BookDetailPage.module.css
index fc2bf0a..f57d6b8 100644
--- a/client/src/pages/BookDetailPage/BookDetailPage.module.css
+++ b/client/src/pages/BookDetailPage/BookDetailPage.module.css
@@ -1,15 +1,15 @@
/* ===== Page layout: mobile first (stacked) ===== */
.page {
- display: flex;
- flex-direction: column;
- gap: var(--space-6);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-6);
}
/* ===== Cover column (positioning box for the ribbon) ===== */
.coverColumn {
- position: relative;
- width: 11.875rem;
- align-self: center;
+ position: relative;
+ width: 11.875rem;
+ align-self: center;
}
/* ===== Ribbon position and size (mockup: 44px high on mobile, 52px on desktop) ===== */
@@ -29,127 +29,126 @@
/* ===== Content column ===== */
.content {
- display: flex;
- flex-direction: column;
- gap: var(--space-6);
- max-width: 41rem;
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-6);
+ max-width: 41rem;
}
.header {
- display: flex;
- flex-direction: column;
- gap: var(--space-3);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
}
/* ===== Title block ===== */
.header h1 {
- font-size: clamp(2.125rem, 4vw, 2.75rem);
- line-height: 1.1;
+ font-size: clamp(2.125rem, 4vw, 2.75rem);
+ line-height: 1.1;
}
.subtitle {
- margin-block-start: var(--space-2);
- font-size: var(--text-lg);
- color: var(--color-text-soft);
+ margin-block-start: var(--space-2);
+ font-size: var(--text-lg);
+ color: var(--color-text-soft);
}
.authors {
- font-size: var(--text-md);
- font-weight: 600;
- color: var(--color-text);
+ font-size: var(--text-md);
+ font-weight: 600;
+ color: var(--color-text);
}
.authors span {
- font-weight: 400;
- color: var(--color-text-soft);
+ font-weight: 400;
+ color: var(--color-text-soft);
}
/* ===== Genre chips ===== */
.tags {
- display: flex;
- flex-wrap: wrap;
- gap: var(--space-2);
- padding: 0;
- list-style: none;
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+ padding: 0;
+ list-style: none;
}
.tags li {
- padding: var(--space-1) var(--space-3);
- border-radius: 999px;
- background: var(--color-chip-bg);
- color: var(--color-chip-text);
- font-size: var(--text-sm);
+ padding: var(--space-1) var(--space-3);
+ border-radius: 999px;
+ background: var(--color-chip-bg);
+ color: var(--color-chip-text);
+ font-size: var(--text-sm);
}
/* ===== Divider ===== */
.divider {
- border: 0;
- border-block-start: 1px solid var(--color-line);
+ border: 0;
+ border-block-start: 1px solid var(--color-line);
}
/* ===== Description (with drop cap) ===== */
.description {
- max-width: 65ch;
- font-size: var(--text-md);
- line-height: 1.7;
+ max-width: 65ch;
+ font-size: var(--text-md);
+ line-height: 1.7;
}
.description::first-letter {
- float: left;
- padding: 6px 10px 0 0;
- font-family: var(--font-heading);
- font-weight: 600;
- font-size: 3.5em;
- line-height: 0.85;
- color: var(--color-accent);
+ float: left;
+ padding: 6px 10px 0 0;
+ font-family: var(--font-heading);
+ font-weight: 600;
+ font-size: 3.5em;
+ line-height: 0.85;
+ color: var(--color-accent);
}
/* ===== Facts ===== */
.facts {
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: var(--space-4);
- padding-block-start: var(--space-4);
- border-block-start: 1px solid var(--color-line);
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: var(--space-4);
+ padding-block-start: var(--space-4);
+ border-block-start: 1px solid var(--color-line);
}
.facts dt {
- font-size: 0.75rem;
- font-weight: 700;
- letter-spacing: 0.14em;
- text-transform: uppercase;
- color: var(--color-text-soft);
+ font-size: 0.75rem;
+ font-weight: 700;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--color-text-soft);
}
.facts dd {
- font-weight: 600;
+ font-weight: 600;
}
/* ===== Desktop: cover left, content right ===== */
@media (min-width: 48rem) {
- .page {
- flex-direction: row;
- align-items: flex-start;
- gap: var(--space-8);
- }
+ .page {
+ flex-direction: row;
+ align-items: flex-start;
+ gap: var(--space-8);
+ }
- .coverColumn {
- flex: 0 0 clamp(14rem, 28%, 21.25rem);
- width: auto;
- align-self: flex-start;
- }
+ .coverColumn {
+ flex: 0 0 clamp(14rem, 28%, 21.25rem);
+ width: auto;
+ align-self: flex-start;
+ }
- .content {
- flex: 1;
- min-width: 0;
- }
+ .content {
+ flex: 1;
+ min-width: 0;
+ }
- .description {
- font-size: 1.1875rem;
- }
+ .description {
+ font-size: 1.1875rem;
+ }
- .facts {
- gap: var(--space-6);
- }
+ .facts {
+ gap: var(--space-6);
+ }
}
-
diff --git a/client/src/pages/BookDetailPage/BookDetailPage.tsx b/client/src/pages/BookDetailPage/BookDetailPage.tsx
index 0348b65..9dc872b 100644
--- a/client/src/pages/BookDetailPage/BookDetailPage.tsx
+++ b/client/src/pages/BookDetailPage/BookDetailPage.tsx
@@ -12,165 +12,163 @@ import styles from './BookDetailPage.module.css';
type PendingConfirm = 'removeFromShelf' | 'deleteNote' | null;
export function BookDetailPage() {
- const { id } = useParams<{ id: string }>();
-
- // ===== TEMPORARY! TODO: replace with a query by id once the backend is ready =====
- const book = mockBooks.find((b) => b.id === id);
-
- // ===== TEMPORARY: the user's entry for this book (undefined = not on shelf) =====
- const [entry, setEntry] = useState(
- mockShelfEntries.find((e) => e.bookId === id),
- );
- const isSaved = entry !== undefined;
-
- const [pendingConfirm, setPendingConfirm] = useState(null);
-
- if (!book) return Book not found.
;
-
- // ===== Shelf actions =====
- // Ribbon: add an empty entry, or remove it. Removing deletes the rating and note too,
- // so ask first if there is something to lose.
- const toggleShelf = () => {
- const hasUserData = entry?.rating !== undefined || Boolean(entry?.note);
- if (isSaved && hasUserData) {
- setPendingConfirm('removeFromShelf');
- } else {
- setEntry(isSaved ? undefined : { bookId: book.id });
- }
- };
-
- // Rating: creates the entry if it doesn't exist (rating puts the book on the shelf)
- const setRating = (rating: number) => {
- setEntry((prev) => ({ ...prev, bookId: book.id, rating }));
- };
-
- // Clear rating: keeps the book on the shelf
- const clearRating = () => {
- setEntry((prev) => prev && { ...prev, rating: undefined });
- };
-
- // Save note: creates the entry if needed (a note puts the book on the shelf too)
- const saveNote = (note: string) => {
- setEntry((prev) => ({
- ...prev,
- bookId: book.id,
- note,
- updatedAt: new Date().toISOString(),
- }));
- };
-
- // ===== Confirm popup actions =====
- const closeConfirm = () => {
- setPendingConfirm(null);
- };
-
- const confirmRemoveFromShelf = () => {
- setEntry(undefined);
- closeConfirm();
- };
-
- // Delete note: keeps the book (and its rating) on the shelf
- const confirmDeleteNote = () => {
- setEntry((prev) => prev && { ...prev, note: undefined, updatedAt: undefined });
- closeConfirm();
- };
-
- return (
-
-
-
-
+ const { id } = useParams<{ id: string }>();
+
+ // ===== TEMPORARY! TODO: replace with a query by id once the backend is ready =====
+ const book = mockBooks.find((b) => b.id === id);
+
+ // ===== TEMPORARY: the user's entry for this book (undefined = not on shelf) =====
+ const [entry, setEntry] = useState
(
+ mockShelfEntries.find((e) => e.bookId === id),
+ );
+ const isSaved = entry !== undefined;
+
+ const [pendingConfirm, setPendingConfirm] = useState(null);
+
+ if (!book) return Book not found.
;
+
+ // ===== Shelf actions =====
+ // Ribbon: add an empty entry, or remove it. Removing deletes the rating and note too,
+ // so ask first if there is something to lose.
+ const toggleShelf = () => {
+ const hasUserData = entry?.rating !== undefined || Boolean(entry?.note);
+ if (isSaved && hasUserData) {
+ setPendingConfirm('removeFromShelf');
+ } else {
+ setEntry(isSaved ? undefined : { bookId: book.id });
+ }
+ };
+
+ // Rating: creates the entry if it doesn't exist (rating puts the book on the shelf)
+ const setRating = (rating: number) => {
+ setEntry((prev) => ({ ...prev, bookId: book.id, rating }));
+ };
+
+ // Clear rating: keeps the book on the shelf
+ const clearRating = () => {
+ setEntry((prev) => prev && { ...prev, rating: undefined });
+ };
+
+ // Save note: creates the entry if needed (a note puts the book on the shelf too)
+ const saveNote = (note: string) => {
+ setEntry((prev) => ({
+ ...prev,
+ bookId: book.id,
+ note,
+ updatedAt: new Date().toISOString(),
+ }));
+ };
+
+ // ===== Confirm popup actions =====
+ const closeConfirm = () => {
+ setPendingConfirm(null);
+ };
+
+ const confirmRemoveFromShelf = () => {
+ setEntry(undefined);
+ closeConfirm();
+ };
+
+ // Delete note: keeps the book (and its rating) on the shelf
+ const confirmDeleteNote = () => {
+ setEntry((prev) => prev && { ...prev, note: undefined, updatedAt: undefined });
+ closeConfirm();
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {book.description &&
{book.description}
}
+
+
+ {book.publishedYear !== null && (
+
+
- Published
+ -
+
+
-
-
-
-
-
-
- {book.description &&
{book.description}
}
-
-
- {book.publishedYear !== null && (
-
-
- Published
- -
-
-
-
- )}
- {book.pageCount !== null && (
-
-
- Pages
- - {book.pageCount}
-
- )}
- {book.language && (
-
-
- Language
- - {book.language}
-
- )}
-
-
-
setPendingConfirm('deleteNote')}
- />
+ )}
+ {book.pageCount !== null && (
+
+
- Pages
+ - {book.pageCount}
-
-
-
-
- );
-}
\ No newline at end of file
+ )}
+ {book.language && (
+
+
- Language
+ - {book.language}
+
+ )}
+
+
+ setPendingConfirm('deleteNote')}
+ />
+
+
+
+
+
+ );
+}
diff --git a/client/src/styles/tokens.css b/client/src/styles/tokens.css
index 16a0f1f..297b7f1 100644
--- a/client/src/styles/tokens.css
+++ b/client/src/styles/tokens.css
@@ -22,7 +22,6 @@
--color-shelf-plank-edge: #9a7050;
--color-focus: #4a1f4f;
--color-danger: #b3261e;
-
/* Generated book cover tones (bg / text pairs) */
--cover-0-bg: #6a2a5e;
From 6cc0ebc8ccee486ff834a552136c812e9a79a224 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ilse=20=C3=85sbakk=20Resell?=
Date: Thu, 8 Oct 2026 21:36:10 +0200
Subject: [PATCH 17/18] fix: add browse page to its own folder inside pages/
Update dependent imports to reflect this change.
---
client/src/App.tsx | 2 +-
client/src/pages/{ => BrowsePage}/BrowsePage.module.css | 0
client/src/pages/{ => BrowsePage}/BrowsePage.tsx | 6 +++---
3 files changed, 4 insertions(+), 4 deletions(-)
rename client/src/pages/{ => BrowsePage}/BrowsePage.module.css (100%)
rename client/src/pages/{ => BrowsePage}/BrowsePage.tsx (89%)
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 0fdbe2f..b23f7ad 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -1,7 +1,7 @@
import { BrowserRouter, Routes, Route } from 'react-router';
import { AppShell } from './components/AppShell/AppShell';
import { BookDetailPage } from './pages/BookDetailPage/BookDetailPage';
-import { BrowsePage } from "./pages/BrowsePage";
+import { BrowsePage } from "./pages/BrowsePage/BrowsePage";
/* ---------- Temporary placeholder pages (delete when the real ones exist) ---------- */
function ShelfPlaceholder() {
diff --git a/client/src/pages/BrowsePage.module.css b/client/src/pages/BrowsePage/BrowsePage.module.css
similarity index 100%
rename from client/src/pages/BrowsePage.module.css
rename to client/src/pages/BrowsePage/BrowsePage.module.css
diff --git a/client/src/pages/BrowsePage.tsx b/client/src/pages/BrowsePage/BrowsePage.tsx
similarity index 89%
rename from client/src/pages/BrowsePage.tsx
rename to client/src/pages/BrowsePage/BrowsePage.tsx
index 7092a7e..6a7df24 100644
--- a/client/src/pages/BrowsePage.tsx
+++ b/client/src/pages/BrowsePage/BrowsePage.tsx
@@ -1,7 +1,7 @@
import {useState} from 'react';
-import type {Book} from '../types/book';
-import {BookCard} from '../components/BookCard/BookCard';
-import {mockBooks, mockShelfEntries, type ShelfEntry} from '../data/mockBooks';
+import type {Book} from '../../types/book';
+import {BookCard} from '../../components/BookCard/BookCard';
+import {mockBooks, mockShelfEntries, type ShelfEntry} from '../../data/mockBooks';
import styles from './BrowsePage.module.css';
export function BrowsePage(){
From 827241cc4ab45403a0209d9cb54e6b26cee2d787 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ilse=20=C3=85sbakk=20Resell?=
Date: Thu, 8 Oct 2026 21:38:40 +0200
Subject: [PATCH 18/18] style: run Prettier formatting on recently updated
files
Note: Checked that `npm run build` does not throw errors at this stage. Neat.
---
client/src/App.tsx | 2 +-
.../BookDetailPage/BookDetailPage.module.css | 2 +-
.../pages/BrowsePage/BrowsePage.module.css | 57 ++++++------
client/src/pages/BrowsePage/BrowsePage.tsx | 89 ++++++++++---------
4 files changed, 76 insertions(+), 74 deletions(-)
diff --git a/client/src/App.tsx b/client/src/App.tsx
index b23f7ad..d91ecbc 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -1,7 +1,7 @@
import { BrowserRouter, Routes, Route } from 'react-router';
import { AppShell } from './components/AppShell/AppShell';
import { BookDetailPage } from './pages/BookDetailPage/BookDetailPage';
-import { BrowsePage } from "./pages/BrowsePage/BrowsePage";
+import { BrowsePage } from './pages/BrowsePage/BrowsePage';
/* ---------- Temporary placeholder pages (delete when the real ones exist) ---------- */
function ShelfPlaceholder() {
diff --git a/client/src/pages/BookDetailPage/BookDetailPage.module.css b/client/src/pages/BookDetailPage/BookDetailPage.module.css
index f57d6b8..357ed65 100644
--- a/client/src/pages/BookDetailPage/BookDetailPage.module.css
+++ b/client/src/pages/BookDetailPage/BookDetailPage.module.css
@@ -16,7 +16,7 @@
.ribbon {
position: absolute;
top: 0;
- right: var(--space-2);
+ right: var(--space-1);
--ribbon-height: 44px;
}
diff --git a/client/src/pages/BrowsePage/BrowsePage.module.css b/client/src/pages/BrowsePage/BrowsePage.module.css
index 417a4ff..c4685ac 100644
--- a/client/src/pages/BrowsePage/BrowsePage.module.css
+++ b/client/src/pages/BrowsePage/BrowsePage.module.css
@@ -1,46 +1,47 @@
/*mobile first */
- /* TODO: replace the raw values with the shared design tokens in src/styles/. */
+/* TODO: replace the raw values with the shared design tokens in src/styles/. */
.page {
- max-width: 1200px;
- margin: 0 auto;
- padding: 1.5rem 1rem;
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 1.5rem 1rem;
}
.header {
- display: flex;
- flex-wrap: wrap;
- align-items: baseline;
- justify-content: space-between;
- gap: 0.25rem 1rem;
- margin-bottom: 1.25rem;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 0.25rem 1rem;
+ margin-bottom: 1.25rem;
}
.heading {
- margin: 0;
+ margin: 0;
}
.count {
- margin: 0;
- opacity: 0.75;
+ margin: 0;
+ opacity: 0.75;
}
.grid {
- list-style: none;
- margin: 0;
- padding: 0;
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 1.5rem 1rem;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 1.5rem 1rem;
}
-@media (min-width: 40rem) { /*tablets and bigger devices*/
- .page {
- padding: 2rem 1.5rem;
- }
+@media (min-width: 40rem) {
+ /*tablets and bigger devices*/
+ .page {
+ padding: 2rem 1.5rem;
+ }
- .grid {
- grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
- gap: 2rem 1.5rem;
- }
-}
\ No newline at end of file
+ .grid {
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+ gap: 2rem 1.5rem;
+ }
+}
diff --git a/client/src/pages/BrowsePage/BrowsePage.tsx b/client/src/pages/BrowsePage/BrowsePage.tsx
index 6a7df24..013ebc0 100644
--- a/client/src/pages/BrowsePage/BrowsePage.tsx
+++ b/client/src/pages/BrowsePage/BrowsePage.tsx
@@ -1,49 +1,50 @@
-import {useState} from 'react';
-import type {Book} from '../../types/book';
-import {BookCard} from '../../components/BookCard/BookCard';
-import {mockBooks, mockShelfEntries, type ShelfEntry} from '../../data/mockBooks';
+import { useState } from 'react';
+import type { Book } from '../../types/book';
+import { BookCard } from '../../components/BookCard/BookCard';
+import { mockBooks, mockShelfEntries, type ShelfEntry } from '../../data/mockBooks';
import styles from './BrowsePage.module.css';
-export function BrowsePage(){
- const [shelf, setShelf] = useState