diff --git a/client/src/App.tsx b/client/src/App.tsx
index 9e704ea..25cc53c 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -1,10 +1,8 @@
import { BrowserRouter, Routes, Route } from "react-router";
import AppShell from "./components/AppShell/AppShell";
+import { BrowsePage } from "./pages/BrowsePage";
/* ---------- Temporary placeholder pages (delete when the real ones exist) ---------- */
-function BrowsePlaceholder() {
-return
Browse page
;
-}
function ShelfPlaceholder() {
return My shelf page
;
}
@@ -18,7 +16,7 @@ export default function App() {
}>
- } />
+ } />
} />
} />
diff --git a/client/src/data/mockBooks.ts b/client/src/data/mockBooks.ts
new file mode 100644
index 0000000..d1d408d
--- /dev/null
+++ b/client/src/data/mockBooks.ts
@@ -0,0 +1,11 @@
+import type {Book} from '../types/book';
+import booksJson from '../../../server/test-data/books.json';
+
+export type ShelfEntry = {bookId: Book['id']; rating?: number};
+
+export const mockBooks: Book[] = booksJson.map(({googleBooksId, ...rest}) => ({
+ id: googleBooksId,
+ ...rest,
+}));
+
+export const mockShelfEntries: ShelfEntry[] = [];
\ No newline at end of file
diff --git a/client/src/pages/BrowsePage.module.css b/client/src/pages/BrowsePage.module.css
new file mode 100644
index 0000000..417a4ff
--- /dev/null
+++ b/client/src/pages/BrowsePage.module.css
@@ -0,0 +1,46 @@
+/*mobile first */
+ /* TODO: replace the raw values with the shared design tokens in src/styles/. */
+
+.page {
+ 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;
+}
+
+.heading {
+ margin: 0;
+}
+
+.count {
+ 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;
+}
+
+@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
diff --git a/client/src/pages/BrowsePage.tsx b/client/src/pages/BrowsePage.tsx
new file mode 100644
index 0000000..7092a7e
--- /dev/null
+++ b/client/src/pages/BrowsePage.tsx
@@ -0,0 +1,49 @@
+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