From 65c0adfd769b9ef11b897c96a3634c61120055b8 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 8 Dec 2025 00:18:03 +0900 Subject: feat(client): redesign frontend with TailwindCSS v4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace inline styles with TailwindCSS, implementing a cohesive Japanese-inspired design system with custom colors (cream, teal primary), typography (Fraunces, DM Sans), and animations. Update all pages and components with consistent styling, improve accessibility by adding aria-hidden to decorative SVGs, and configure Biome for Tailwind CSS syntax support. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/client/App.test.tsx | 8 +- src/client/components/CreateCardModal.test.tsx | 20 +- src/client/components/CreateCardModal.tsx | 163 ++++----- src/client/components/CreateDeckModal.test.tsx | 20 +- src/client/components/CreateDeckModal.tsx | 160 ++++----- src/client/components/DeleteCardModal.tsx | 122 +++---- src/client/components/DeleteDeckModal.tsx | 125 ++++--- src/client/components/EditCardModal.test.tsx | 24 +- src/client/components/EditCardModal.tsx | 169 +++++---- src/client/components/EditDeckModal.test.tsx | 24 +- src/client/components/EditDeckModal.tsx | 164 +++++---- src/client/components/OfflineBanner.test.tsx | 3 +- src/client/components/OfflineBanner.tsx | 31 +- src/client/components/SyncButton.tsx | 65 +++- src/client/components/SyncStatusIndicator.tsx | 116 +++++-- src/client/main.tsx | 1 + src/client/pages/DeckDetailPage.test.tsx | 68 ++-- src/client/pages/DeckDetailPage.tsx | 452 +++++++++++++++--------- src/client/pages/HomePage.test.tsx | 65 ++-- src/client/pages/HomePage.tsx | 252 ++++++++++---- src/client/pages/LoginPage.test.tsx | 15 +- src/client/pages/LoginPage.tsx | 139 ++++++-- src/client/pages/NotFoundPage.tsx | 50 ++- src/client/pages/StudyPage.test.tsx | 13 +- src/client/pages/StudyPage.tsx | 454 ++++++++++++++----------- src/client/pwa.test.ts | 4 +- src/client/styles.css | 129 +++++++ 27 files changed, 1715 insertions(+), 1141 deletions(-) create mode 100644 src/client/styles.css (limited to 'src') diff --git a/src/client/App.test.tsx b/src/client/App.test.tsx index 8359e67..fe870b7 100644 --- a/src/client/App.test.tsx +++ b/src/client/App.test.tsx @@ -114,14 +114,16 @@ describe("App routing", () => { it("renders login page at /login", () => { renderWithRouter("/login"); - expect(screen.getByRole("heading", { name: "Login" })).toBeDefined(); + expect(screen.getByRole("heading", { name: "Kioku" })).toBeDefined(); + expect(screen.getByRole("heading", { name: "Welcome back" })).toBeDefined(); }); it("renders 404 page for unknown routes", () => { renderWithRouter("/unknown-route"); + expect(screen.getByRole("heading", { name: "404" })).toBeDefined(); expect( - screen.getByRole("heading", { name: "404 - Not Found" }), + screen.getByRole("heading", { name: "Page Not Found" }), ).toBeDefined(); - expect(screen.getByRole("link", { name: "Go to Home" })).toBeDefined(); + expect(screen.getByRole("link", { name: /Go Home/i })).toBeDefined(); }); }); diff --git a/src/client/components/CreateCardModal.test.tsx b/src/client/components/CreateCardModal.test.tsx index 6b429c8..7244824 100644 --- a/src/client/components/CreateCardModal.test.tsx +++ b/src/client/components/CreateCardModal.test.tsx @@ -84,7 +84,7 @@ describe("CreateCardModal", () => { expect(screen.getByLabelText("Front")).toBeDefined(); expect(screen.getByLabelText("Back")).toBeDefined(); expect(screen.getByRole("button", { name: "Cancel" })).toBeDefined(); - expect(screen.getByRole("button", { name: "Create" })).toBeDefined(); + expect(screen.getByRole("button", { name: "Create Card" })).toBeDefined(); }); it("disables create button when front is empty", async () => { @@ -93,7 +93,7 @@ describe("CreateCardModal", () => { await user.type(screen.getByLabelText("Back"), "Answer"); - const createButton = screen.getByRole("button", { name: "Create" }); + const createButton = screen.getByRole("button", { name: "Create Card" }); expect(createButton).toHaveProperty("disabled", true); }); @@ -103,7 +103,7 @@ describe("CreateCardModal", () => { await user.type(screen.getByLabelText("Front"), "Question"); - const createButton = screen.getByRole("button", { name: "Create" }); + const createButton = screen.getByRole("button", { name: "Create Card" }); expect(createButton).toHaveProperty("disabled", true); }); @@ -114,7 +114,7 @@ describe("CreateCardModal", () => { await user.type(screen.getByLabelText("Front"), "Question"); await user.type(screen.getByLabelText("Back"), "Answer"); - const createButton = screen.getByRole("button", { name: "Create" }); + const createButton = screen.getByRole("button", { name: "Create Card" }); expect(createButton).toHaveProperty("disabled", false); }); @@ -181,7 +181,7 @@ describe("CreateCardModal", () => { await user.type(screen.getByLabelText("Front"), "What is 2+2?"); await user.type(screen.getByLabelText("Back"), "4"); - await user.click(screen.getByRole("button", { name: "Create" })); + await user.click(screen.getByRole("button", { name: "Create Card" })); await waitFor(() => { expect( @@ -213,7 +213,7 @@ describe("CreateCardModal", () => { await user.type(screen.getByLabelText("Front"), " Question "); await user.type(screen.getByLabelText("Back"), " Answer "); - await user.click(screen.getByRole("button", { name: "Create" })); + await user.click(screen.getByRole("button", { name: "Create Card" })); await waitFor(() => { expect( @@ -241,7 +241,7 @@ describe("CreateCardModal", () => { await user.type(screen.getByLabelText("Front"), "Question"); await user.type(screen.getByLabelText("Back"), "Answer"); - await user.click(screen.getByRole("button", { name: "Create" })); + await user.click(screen.getByRole("button", { name: "Create Card" })); expect(screen.getByRole("button", { name: "Creating..." })).toBeDefined(); expect(screen.getByRole("button", { name: "Creating..." })).toHaveProperty( @@ -271,7 +271,7 @@ describe("CreateCardModal", () => { await user.type(screen.getByLabelText("Front"), "Question"); await user.type(screen.getByLabelText("Back"), "Answer"); - await user.click(screen.getByRole("button", { name: "Create" })); + await user.click(screen.getByRole("button", { name: "Create Card" })); await waitFor(() => { expect(screen.getByRole("alert").textContent).toContain( @@ -291,7 +291,7 @@ describe("CreateCardModal", () => { await user.type(screen.getByLabelText("Front"), "Question"); await user.type(screen.getByLabelText("Back"), "Answer"); - await user.click(screen.getByRole("button", { name: "Create" })); + await user.click(screen.getByRole("button", { name: "Create Card" })); await waitFor(() => { expect(screen.getByRole("alert").textContent).toContain( @@ -358,7 +358,7 @@ describe("CreateCardModal", () => { // Create a card await user.type(screen.getByLabelText("Front"), "Question"); await user.type(screen.getByLabelText("Back"), "Answer"); - await user.click(screen.getByRole("button", { name: "Create" })); + await user.click(screen.getByRole("button", { name: "Create Card" })); await waitFor(() => { expect(onClose).toHaveBeenCalled(); diff --git a/src/client/components/CreateCardModal.tsx b/src/client/components/CreateCardModal.tsx index c28cf0f..3913e82 100644 --- a/src/client/components/CreateCardModal.tsx +++ b/src/client/components/CreateCardModal.tsx @@ -83,18 +83,7 @@ export function CreateCardModal({ role="dialog" aria-modal="true" aria-labelledby="create-card-title" - style={{ - position: "fixed", - top: 0, - left: 0, - right: 0, - bottom: 0, - backgroundColor: "rgba(0, 0, 0, 0.5)", - display: "flex", - alignItems: "center", - justifyContent: "center", - zIndex: 1000, - }} + className="fixed inset-0 bg-ink/40 backdrop-blur-sm flex items-center justify-center z-50 p-4 animate-fade-in" onClick={(e) => { if (e.target === e.currentTarget) { handleClose(); @@ -106,88 +95,82 @@ export function CreateCardModal({ } }} > -
-

- Create New Card -

+
+
+

+ Create New Card +

-
- {error && ( -
- {error} -
- )} + + {error && ( +
+ {error} +
+ )} -
- -