From 58d132139ba8d5fa17c8681a0275047ce4cca809 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Wed, 3 Dec 2025 03:51:29 +0900 Subject: feat(frontend): design update --- frontend/src/components/AddFeedForm.tsx | 26 ++++---- frontend/src/components/ArticleItem.tsx | 24 ++++---- frontend/src/components/ArticleList.tsx | 16 ++--- frontend/src/components/FeedItem.tsx | 37 ++++++------ frontend/src/components/FeedList.tsx | 20 +++++-- frontend/src/components/Layout.tsx | 4 +- frontend/src/components/MenuItem.tsx | 6 +- frontend/src/components/Navigation.tsx | 19 +++--- frontend/src/index.css | 34 +++++++++++ frontend/src/pages/Login.tsx | 101 ++++++++++++++++++-------------- frontend/src/pages/NotFound.tsx | 16 ++++- frontend/src/pages/ReadArticles.tsx | 18 ++++-- frontend/src/pages/Settings.tsx | 21 +++---- frontend/src/pages/UnreadArticles.tsx | 20 +++++-- 14 files changed, 224 insertions(+), 138 deletions(-) diff --git a/frontend/src/components/AddFeedForm.tsx b/frontend/src/components/AddFeedForm.tsx index 6d18318..9a56574 100644 --- a/frontend/src/components/AddFeedForm.tsx +++ b/frontend/src/components/AddFeedForm.tsx @@ -48,43 +48,43 @@ export function AddFeedForm({ onFeedAdded }: Props) { const isUrlValid = !url || isValidUrl(url); return ( -
-
-

+ +
+

Subscribe to New Feed

-
+
setUrl(e.target.value)} placeholder="https://example.com/feed.xml" - className={`w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 ${ + className={`w-full rounded-lg border bg-white px-4 py-2.5 text-sm text-stone-900 placeholder-stone-400 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-1 ${ isUrlValid - ? "border-gray-300 focus:border-blue-500 focus:ring-blue-500" - : "border-red-300 focus:border-red-500 focus:ring-red-500" + ? "border-stone-200 focus:border-sky-500 focus:ring-sky-500/20" + : "border-red-300 focus:border-red-500 focus:ring-red-500/20" }`} disabled={fetching} /> {!isUrlValid && ( -

+

Please enter a valid URL (http:// or https://)

)} - {error &&

{error}

} + {error &&

{error}

}
diff --git a/frontend/src/components/ArticleItem.tsx b/frontend/src/components/ArticleItem.tsx index c61923a..f8fac24 100644 --- a/frontend/src/components/ArticleItem.tsx +++ b/frontend/src/components/ArticleItem.tsx @@ -51,38 +51,36 @@ export function ArticleItem({ article, onReadChange }: Props) { return (
-
+
diff --git a/frontend/src/components/ArticleList.tsx b/frontend/src/components/ArticleList.tsx index 574f529..afadb25 100644 --- a/frontend/src/components/ArticleList.tsx +++ b/frontend/src/components/ArticleList.tsx @@ -30,7 +30,9 @@ export function ArticleList({ articles, isReadView }: Props) { if (visibleArticles.length === 0) { return ( -
No articles found.
+
+

No articles found.

+
); } @@ -54,14 +56,14 @@ export function ArticleList({ articles, isReadView }: Props) { ); return ( -
+
{Object.values(articlesByFeed).map(({ feed, articles: feedArticles }) => ( -
-

+
+

{feed.title} - - ({feedArticles.length} article - {feedArticles.length !== 1 ? "s" : ""}) + + {feedArticles.length} article + {feedArticles.length !== 1 ? "s" : ""}

diff --git a/frontend/src/components/FeedItem.tsx b/frontend/src/components/FeedItem.tsx index 80c8992..8333f75 100644 --- a/frontend/src/components/FeedItem.tsx +++ b/frontend/src/components/FeedItem.tsx @@ -41,26 +41,29 @@ export function FeedItem({ feed, onFeedUnsubscribed }: Props) { }; return ( -
-
-
-

{feed.title}

-

- - {feed.url} - +

+
+
+

+ {feed.title} +

+ + {feed.url} + +

+ Last fetched: {formatDateTime(new Date(feed.fetchedAt))}

-
- - Last fetched: {formatDateTime(new Date(feed.fetchedAt))} - -
-
+
)}
diff --git a/frontend/src/index.css b/frontend/src/index.css index f1d8c73..ffebab9 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1 +1,35 @@ @import "tailwindcss"; + +@theme { + --animate-fade-in: fade-in 0.3s ease-out; + --animate-slide-up: slide-up 0.3s ease-out; +} + +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes slide-up { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +html { + scroll-behavior: smooth; +} + +:focus-visible { + outline: 2px solid var(--color-sky-500); + outline-offset: 2px; +} diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index 4d81234..76a775a 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -25,51 +25,64 @@ export function Login() { }; return ( -
-
-

Feedaka Login

- -
- - setUsername(e.target.value)} - required - className="w-full p-2 border border-gray-300 rounded disabled:opacity-70 disabled:cursor-not-allowed" - disabled={isLoading} - /> -
-
- - setPassword(e.target.value)} - required - className="w-full p-2 border border-gray-300 rounded disabled:opacity-70 disabled:cursor-not-allowed" - disabled={isLoading} - /> -
- {error && ( -
- {error} +
+
+
+

+ feedaka +

+

Sign in to your account

+
+
+ +
+ + setUsername(e.target.value)} + required + className="w-full rounded-lg border border-stone-200 bg-white px-4 py-2.5 text-sm text-stone-900 transition-all duration-200 placeholder:text-stone-400 focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-500/20 disabled:cursor-not-allowed disabled:opacity-70" + disabled={isLoading} + />
- )} - - +
+ + setPassword(e.target.value)} + required + className="w-full rounded-lg border border-stone-200 bg-white px-4 py-2.5 text-sm text-stone-900 transition-all duration-200 placeholder:text-stone-400 focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-500/20 disabled:cursor-not-allowed disabled:opacity-70" + disabled={isLoading} + /> +
+ {error && ( +
+ {error} +
+ )} + + +
); diff --git a/frontend/src/pages/NotFound.tsx b/frontend/src/pages/NotFound.tsx index 23c1184..adc782b 100644 --- a/frontend/src/pages/NotFound.tsx +++ b/frontend/src/pages/NotFound.tsx @@ -1,14 +1,24 @@ +import { Link } from "wouter"; + export function NotFound() { return (
-

404

-

+

+ 404 +

+

Page Not Found

-

+

The page you're looking for doesn't exist.

+ + Go back home +
); diff --git a/frontend/src/pages/ReadArticles.tsx b/frontend/src/pages/ReadArticles.tsx index ccbd2dc..f90c3c9 100644 --- a/frontend/src/pages/ReadArticles.tsx +++ b/frontend/src/pages/ReadArticles.tsx @@ -11,19 +11,27 @@ export function ReadArticles() { }); if (fetching) { - return
Loading read articles...
; + return ( +
+

Loading read articles...

+
+ ); } if (error) { - return
Error: {error.message}
; + return ( +
+ Error: {error.message} +
+ ); } return (
-
-

Read Articles

+
+

Read

{data?.readArticles && ( -

+

{data.readArticles.length} article {data.readArticles.length !== 1 ? "s" : ""}

diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx index b10cbf1..9b1e04c 100644 --- a/frontend/src/pages/Settings.tsx +++ b/frontend/src/pages/Settings.tsx @@ -16,24 +16,17 @@ export function Settings() { }; return ( -
-

Feed Settings

- - {/* Subscribe to New Feed Section */} -
-

- Subscribe to New Feed -

+
+
-
+ - {/* Manage Feeds Section */} -
-

- Manage Feeds +
+

+ Your Feeds

-

+
); } diff --git a/frontend/src/pages/UnreadArticles.tsx b/frontend/src/pages/UnreadArticles.tsx index dc4c377..28cc8b5 100644 --- a/frontend/src/pages/UnreadArticles.tsx +++ b/frontend/src/pages/UnreadArticles.tsx @@ -11,21 +11,29 @@ export function UnreadArticles() { }); if (fetching) { - return
Loading unread articles...
; + return ( +
+

Loading unread articles...

+
+ ); } if (error) { - return
Error: {error.message}
; + return ( +
+ Error: {error.message} +
+ ); } return (
-
-

Unread Articles

+
+

Unread

{data?.unreadArticles && ( -

+

{data.unreadArticles.length} article - {data.unreadArticles.length !== 1 ? "s" : ""} + {data.unreadArticles.length !== 1 ? "s" : ""} to read

)}
-- cgit v1.2.3-70-g09d2