aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-12-03 03:51:29 +0900
committernsfisis <nsfisis@gmail.com>2025-12-04 23:27:28 +0900
commit58d132139ba8d5fa17c8681a0275047ce4cca809 (patch)
tree7dd18f5de496483b7d318e12e43379a51f3a6056
parentaac4e9ccdebe52c156506d1899d5a38e99366f69 (diff)
downloadfeedaka-58d132139ba8d5fa17c8681a0275047ce4cca809.tar.gz
feedaka-58d132139ba8d5fa17c8681a0275047ce4cca809.tar.zst
feedaka-58d132139ba8d5fa17c8681a0275047ce4cca809.zip
feat(frontend): design update
-rw-r--r--frontend/src/components/AddFeedForm.tsx26
-rw-r--r--frontend/src/components/ArticleItem.tsx24
-rw-r--r--frontend/src/components/ArticleList.tsx16
-rw-r--r--frontend/src/components/FeedItem.tsx37
-rw-r--r--frontend/src/components/FeedList.tsx20
-rw-r--r--frontend/src/components/Layout.tsx4
-rw-r--r--frontend/src/components/MenuItem.tsx6
-rw-r--r--frontend/src/components/Navigation.tsx19
-rw-r--r--frontend/src/index.css34
-rw-r--r--frontend/src/pages/Login.tsx101
-rw-r--r--frontend/src/pages/NotFound.tsx16
-rw-r--r--frontend/src/pages/ReadArticles.tsx18
-rw-r--r--frontend/src/pages/Settings.tsx21
-rw-r--r--frontend/src/pages/UnreadArticles.tsx20
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 (
- <form onSubmit={handleSubmit} className="space-y-4 p-4">
- <div className="rounded-lg border border-gray-200 bg-white p-4 shadow-sm">
- <h3 className="text-lg font-semibold text-gray-900 mb-4">
+ <form onSubmit={handleSubmit}>
+ <div className="rounded-xl border border-stone-200 bg-white p-5">
+ <h3 className="mb-4 text-sm font-semibold uppercase tracking-wide text-stone-900">
Subscribe to New Feed
</h3>
- <div className="flex gap-2">
+ <div className="flex gap-3">
<div className="flex-1">
<input
type="url"
value={url}
onChange={(e) => 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 && (
- <p className="mt-1 text-sm text-red-600">
+ <p className="mt-2 text-sm text-red-600">
Please enter a valid URL (http:// or https://)
</p>
)}
- {error && <p className="mt-1 text-sm text-red-600">{error}</p>}
+ {error && <p className="mt-2 text-sm text-red-600">{error}</p>}
</div>
<button
type="submit"
disabled={fetching || !url.trim() || !isUrlValid}
- className="rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed"
+ className="inline-flex items-center gap-2 rounded-lg bg-sky-600 px-5 py-2.5 text-sm font-medium text-white transition-all duration-200 hover:bg-sky-700 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:bg-stone-200 disabled:text-stone-400"
>
{fetching ? (
- <FontAwesomeIcon icon={faSpinner} spin className="mr-2" />
+ <FontAwesomeIcon icon={faSpinner} spin />
) : (
- <FontAwesomeIcon icon={faPlus} className="mr-2" />
+ <FontAwesomeIcon icon={faPlus} />
)}
- Subscribe
+ <span>Subscribe</span>
</button>
</div>
</div>
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 (
<div
- className={`group flex items-center gap-3 rounded-lg border p-3 hover:bg-gray-50 ${
+ className={`group flex items-center gap-3 rounded-lg p-3 transition-all duration-200 ${
article.isRead
- ? "border-gray-200 bg-white"
- : "border-blue-200 bg-blue-50"
+ ? "bg-white hover:bg-stone-50"
+ : "border-l-2 border-l-sky-500 bg-sky-50/50"
}`}
>
<button
type="button"
onClick={() => handleToggleRead(article.id, article.isRead)}
- className={`flex-shrink-0 rounded p-1 transition-colors ${
+ className={`flex-shrink-0 rounded-md p-1.5 transition-all duration-150 ${
article.isRead
- ? "text-gray-400 hover:text-gray-600"
- : "text-blue-600 hover:text-blue-700"
+ ? "text-stone-300 hover:bg-stone-100 hover:text-stone-500"
+ : "text-sky-500 hover:bg-sky-100 hover:text-sky-600"
}`}
title={article.isRead ? "Mark as unread" : "Mark as read"}
>
<FontAwesomeIcon
icon={article.isRead ? faCheck : faCircle}
- className="w-4 h-4"
+ className="h-4 w-4"
/>
</button>
- <div className="flex-1 min-w-0">
+ <div className="min-w-0 flex-1">
<button
type="button"
onClick={() => handleArticleClick(article)}
- className={`text-left w-full group-hover:text-blue-600 transition-colors ${
- article.isRead ? "text-gray-700" : "text-gray-900 font-medium"
+ className={`w-full text-left transition-colors duration-150 group-hover:text-sky-700 ${
+ article.isRead ? "text-stone-500" : "font-medium text-stone-900"
}`}
>
- <div className="flex items-center gap-2 break-words">
- {article.title}
- </div>
+ <span className="break-words">{article.title}</span>
</button>
</div>
</div>
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 (
- <div className="p-4 text-center text-gray-500">No articles found.</div>
+ <div className="py-8 text-center">
+ <p className="text-sm text-stone-400">No articles found.</p>
+ </div>
);
}
@@ -54,14 +56,14 @@ export function ArticleList({ articles, isReadView }: Props) {
);
return (
- <div className="space-y-6 p-4">
+ <div className="space-y-8">
{Object.values(articlesByFeed).map(({ feed, articles: feedArticles }) => (
- <div key={feed.id} className="space-y-2">
- <h3 className="text-lg font-semibold text-gray-900 border-b border-gray-200 pb-2">
+ <div key={feed.id} className="space-y-3">
+ <h3 className="border-b border-stone-200 pb-2 text-sm font-semibold uppercase tracking-wide text-stone-900">
{feed.title}
- <span className="ml-2 text-sm font-normal text-gray-500">
- ({feedArticles.length} article
- {feedArticles.length !== 1 ? "s" : ""})
+ <span className="ml-2 text-xs font-normal normal-case tracking-normal text-stone-400">
+ {feedArticles.length} article
+ {feedArticles.length !== 1 ? "s" : ""}
</span>
</h3>
<div className="space-y-1">
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 (
- <div className="rounded-lg border border-gray-200 bg-white p-4 shadow-sm">
- <div className="flex items-start justify-between">
- <div className="flex-1">
- <h3 className="text-lg font-semibold text-gray-900">{feed.title}</h3>
- <p className="mt-1 text-sm text-gray-500">
- <a href={feed.url} target="_blank" rel="noreferrer">
- {feed.url}
- </a>
+ <div className="group rounded-xl border border-stone-200 bg-white p-5 transition-all duration-200 hover:border-stone-300 hover:shadow-sm">
+ <div className="flex items-start justify-between gap-4">
+ <div className="min-w-0 flex-1">
+ <h3 className="truncate text-base font-semibold text-stone-900">
+ {feed.title}
+ </h3>
+ <a
+ href={feed.url}
+ target="_blank"
+ rel="noreferrer"
+ className="mt-1 block truncate text-sm text-stone-400 transition-colors hover:text-sky-600"
+ >
+ {feed.url}
+ </a>
+ <p className="mt-2 text-xs text-stone-400">
+ Last fetched: {formatDateTime(new Date(feed.fetchedAt))}
</p>
- <div className="mt-2 flex items-center gap-4 text-sm">
- <span className="text-gray-400">
- Last fetched: {formatDateTime(new Date(feed.fetchedAt))}
- </span>
- </div>
</div>
- <div className="flex items-center gap-2">
+ <div className="flex items-center gap-1">
<button
type="button"
onClick={() => handleMarkAllRead(feed.id)}
- className="rounded p-2 text-gray-600 hover:bg-gray-100 hover:text-gray-900"
+ className="rounded-lg p-2 text-stone-400 transition-all duration-150 hover:bg-stone-100 hover:text-stone-600"
title="Mark all as read"
>
<FontAwesomeIcon icon={faCheck} />
@@ -68,7 +71,7 @@ export function FeedItem({ feed, onFeedUnsubscribed }: Props) {
<button
type="button"
onClick={() => handleMarkAllUnread(feed.id)}
- className="rounded p-2 text-gray-600 hover:bg-gray-100 hover:text-gray-900"
+ className="rounded-lg p-2 text-stone-400 transition-all duration-150 hover:bg-stone-100 hover:text-stone-600"
title="Mark all as unread"
>
<FontAwesomeIcon icon={faCircle} />
@@ -76,7 +79,7 @@ export function FeedItem({ feed, onFeedUnsubscribed }: Props) {
<button
type="button"
onClick={() => handleUnsubscribeFeed(feed.id)}
- className="rounded p-2 text-red-600 hover:bg-red-50 hover:text-red-700"
+ className="rounded-lg p-2 text-stone-400 transition-all duration-150 hover:bg-red-50 hover:text-red-600"
title="Unsubscribe from feed"
>
<FontAwesomeIcon icon={faTrash} />
diff --git a/frontend/src/components/FeedList.tsx b/frontend/src/components/FeedList.tsx
index 6081293..24bcfc7 100644
--- a/frontend/src/components/FeedList.tsx
+++ b/frontend/src/components/FeedList.tsx
@@ -15,17 +15,29 @@ export function FeedList({ onFeedUnsubscribed }: Props) {
});
if (fetching) {
- return <div className="p-4">Loading feeds...</div>;
+ return (
+ <div className="py-8 text-center">
+ <p className="text-sm text-stone-400">Loading feeds...</p>
+ </div>
+ );
}
if (error) {
- return <div className="p-4 text-red-600">Error: {error.message}</div>;
+ return (
+ <div className="rounded-lg bg-red-50 p-4 text-sm text-red-600">
+ Error: {error.message}
+ </div>
+ );
}
if (!data?.feeds || data.feeds.length === 0) {
- return <div className="p-4 text-gray-500">No feeds added yet.</div>;
+ return (
+ <div className="py-8 text-center">
+ <p className="text-sm text-stone-400">No feeds added yet.</p>
+ </div>
+ );
}
return (
- <div className="space-y-4 p-4">
+ <div className="space-y-3">
{data.feeds.map((feed) => (
<FeedItem
key={feed.id}
diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx
index 09a0eb4..5f30de5 100644
--- a/frontend/src/components/Layout.tsx
+++ b/frontend/src/components/Layout.tsx
@@ -7,9 +7,9 @@ interface Props {
export function Layout({ children }: Props) {
return (
- <div className="min-h-screen bg-gray-50">
+ <div className="min-h-screen bg-stone-50">
<Navigation />
- <main className="container mx-auto px-4 py-8">{children}</main>
+ <main className="mx-auto max-w-5xl px-6 py-10">{children}</main>
</div>
);
}
diff --git a/frontend/src/components/MenuItem.tsx b/frontend/src/components/MenuItem.tsx
index 29e397c..42202c9 100644
--- a/frontend/src/components/MenuItem.tsx
+++ b/frontend/src/components/MenuItem.tsx
@@ -15,10 +15,10 @@ export function MenuItem({ path, label, icon }: Props) {
return (
<Link
href={path}
- className={`flex items-center sm:space-x-2 px-3 py-2 rounded-md text-sm font-medium transition-colors ${
+ className={`flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium transition-all duration-200 ${
isActive
- ? "bg-blue-100 text-blue-700"
- : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"
+ ? "bg-sky-50 text-sky-700"
+ : "text-stone-500 hover:text-stone-900 hover:bg-stone-100"
}`}
>
<FontAwesomeIcon icon={icon} />
diff --git a/frontend/src/components/Navigation.tsx b/frontend/src/components/Navigation.tsx
index c4af36a..1f99cd6 100644
--- a/frontend/src/components/Navigation.tsx
+++ b/frontend/src/components/Navigation.tsx
@@ -17,13 +17,16 @@ export function Navigation() {
};
return (
- <nav className="bg-white shadow-sm border-b border-gray-200">
- <div className="container mx-auto px-4">
- <div className="flex items-center justify-between h-16">
- <Link href="/" className="text-xl font-bold text-gray-900">
+ <nav className="sticky top-0 z-50 bg-white/80 backdrop-blur-sm border-b border-stone-200/60">
+ <div className="mx-auto max-w-5xl px-6">
+ <div className="flex items-center justify-between h-14">
+ <Link
+ href="/"
+ className="text-lg font-semibold tracking-tight text-stone-900"
+ >
feedaka
</Link>
- <div className="flex items-center space-x-6">
+ <div className="flex items-center gap-1">
<MenuItem path="/unread" label="Unread" icon={faBookOpen} />
<MenuItem path="/read" label="Read" icon={faCircleCheck} />
<MenuItem path="/settings" label="Settings" icon={faGear} />
@@ -31,11 +34,13 @@ export function Navigation() {
<button
type="button"
onClick={handleLogout}
- className="flex items-center space-x-2 text-gray-600 hover:text-gray-900"
+ className="flex items-center gap-2 px-3 py-1.5 rounded-lg text-stone-500 hover:text-stone-900 hover:bg-stone-100 transition-all duration-200"
title="Logout"
>
<FontAwesomeIcon icon={faRightFromBracket} />
- <span className="hidden sm:inline">Logout</span>
+ <span className="hidden sm:inline text-sm font-medium">
+ Logout
+ </span>
</button>
)}
</div>
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 (
- <div className="flex justify-center items-center min-h-screen bg-gray-100">
- <div className="bg-white p-8 rounded-lg shadow w-full max-w-md">
- <h1 className="mb-6 text-center text-2xl font-bold">Feedaka Login</h1>
- <form onSubmit={handleSubmit}>
- <div className="mb-4">
- <label htmlFor="username" className="block mb-2">
- Username
- </label>
- <input
- id="username"
- type="text"
- value={username}
- onChange={(e) => setUsername(e.target.value)}
- required
- className="w-full p-2 border border-gray-300 rounded disabled:opacity-70 disabled:cursor-not-allowed"
- disabled={isLoading}
- />
- </div>
- <div className="mb-4">
- <label htmlFor="password" className="block mb-2">
- Password
- </label>
- <input
- id="password"
- type="password"
- value={password}
- onChange={(e) => setPassword(e.target.value)}
- required
- className="w-full p-2 border border-gray-300 rounded disabled:opacity-70 disabled:cursor-not-allowed"
- disabled={isLoading}
- />
- </div>
- {error && (
- <div className="text-red-600 mb-4 p-2 bg-red-50 rounded">
- {error}
+ <div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-stone-100 to-stone-50 px-4">
+ <div className="w-full max-w-sm">
+ <div className="mb-8 text-center">
+ <h1 className="text-2xl font-semibold tracking-tight text-stone-900">
+ feedaka
+ </h1>
+ <p className="mt-2 text-sm text-stone-500">Sign in to your account</p>
+ </div>
+ <div className="rounded-xl border border-stone-200 bg-white p-6 shadow-sm">
+ <form onSubmit={handleSubmit} className="space-y-5">
+ <div>
+ <label
+ htmlFor="username"
+ className="mb-1.5 block text-sm font-medium text-stone-700"
+ >
+ Username
+ </label>
+ <input
+ id="username"
+ type="text"
+ value={username}
+ onChange={(e) => 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}
+ />
</div>
- )}
- <button
- type="submit"
- disabled={isLoading}
- className="w-full p-3 bg-blue-500 text-white rounded cursor-pointer hover:bg-blue-600 disabled:opacity-70 disabled:cursor-not-allowed"
- >
- {isLoading ? "Logging in..." : "Login"}
- </button>
- </form>
+ <div>
+ <label
+ htmlFor="password"
+ className="mb-1.5 block text-sm font-medium text-stone-700"
+ >
+ Password
+ </label>
+ <input
+ id="password"
+ type="password"
+ value={password}
+ onChange={(e) => 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}
+ />
+ </div>
+ {error && (
+ <div className="rounded-lg bg-red-50 p-3 text-sm text-red-600">
+ {error}
+ </div>
+ )}
+ <button
+ type="submit"
+ disabled={isLoading}
+ className="w-full rounded-lg bg-sky-600 px-4 py-2.5 text-sm font-medium text-white transition-all duration-200 hover:bg-sky-700 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-70"
+ >
+ {isLoading ? "Signing in..." : "Sign in"}
+ </button>
+ </form>
+ </div>
</div>
</div>
);
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 (
<div className="flex min-h-96 flex-col items-center justify-center">
<div className="text-center">
- <h1 className="text-6xl font-bold text-gray-900">404</h1>
- <h2 className="mt-4 text-2xl font-semibold text-gray-700">
+ <h1 className="text-6xl font-semibold tracking-tight text-stone-900">
+ 404
+ </h1>
+ <h2 className="mt-4 text-xl font-medium text-stone-700">
Page Not Found
</h2>
- <p className="mt-2 text-gray-500">
+ <p className="mt-2 text-sm text-stone-400">
The page you're looking for doesn't exist.
</p>
+ <Link
+ href="/"
+ className="mt-6 inline-block rounded-lg bg-sky-600 px-5 py-2.5 text-sm font-medium text-white transition-all duration-200 hover:bg-sky-700"
+ >
+ Go back home
+ </Link>
</div>
</div>
);
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 <div className="p-4">Loading read articles...</div>;
+ return (
+ <div className="py-8 text-center">
+ <p className="text-sm text-stone-400">Loading read articles...</p>
+ </div>
+ );
}
if (error) {
- return <div className="p-4 text-red-600">Error: {error.message}</div>;
+ return (
+ <div className="rounded-lg bg-red-50 p-4 text-sm text-red-600">
+ Error: {error.message}
+ </div>
+ );
}
return (
<div>
- <div className="border-b border-gray-200 bg-white px-4 py-3">
- <h1 className="text-xl font-semibold text-gray-900">Read Articles</h1>
+ <div className="mb-6">
+ <h1 className="text-xl font-semibold text-stone-900">Read</h1>
{data?.readArticles && (
- <p className="text-sm text-gray-500">
+ <p className="mt-1 text-sm text-stone-400">
{data.readArticles.length} article
{data.readArticles.length !== 1 ? "s" : ""}
</p>
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 (
- <div className="mx-auto max-w-4xl">
- <h1 className="mb-6 text-2xl font-bold text-gray-900">Feed Settings</h1>
-
- {/* Subscribe to New Feed Section */}
- <div className="mb-8">
- <h2 className="mb-4 text-xl font-semibold text-gray-800">
- Subscribe to New Feed
- </h2>
+ <div className="mx-auto max-w-3xl space-y-10">
+ <section>
<AddFeedForm onFeedAdded={handleFeedAdded} />
- </div>
+ </section>
- {/* Manage Feeds Section */}
- <div className="mb-8">
- <h2 className="mb-4 text-xl font-semibold text-gray-800">
- Manage Feeds
+ <section>
+ <h2 className="mb-4 text-sm font-semibold uppercase tracking-wide text-stone-900">
+ Your Feeds
</h2>
<FeedList onFeedUnsubscribed={handleFeedUnsubscribed} />
- </div>
+ </section>
</div>
);
}
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 <div className="p-4">Loading unread articles...</div>;
+ return (
+ <div className="py-8 text-center">
+ <p className="text-sm text-stone-400">Loading unread articles...</p>
+ </div>
+ );
}
if (error) {
- return <div className="p-4 text-red-600">Error: {error.message}</div>;
+ return (
+ <div className="rounded-lg bg-red-50 p-4 text-sm text-red-600">
+ Error: {error.message}
+ </div>
+ );
}
return (
<div>
- <div className="border-b border-gray-200 bg-white px-4 py-3">
- <h1 className="text-xl font-semibold text-gray-900">Unread Articles</h1>
+ <div className="mb-6">
+ <h1 className="text-xl font-semibold text-stone-900">Unread</h1>
{data?.unreadArticles && (
- <p className="text-sm text-gray-500">
+ <p className="mt-1 text-sm text-stone-400">
{data.unreadArticles.length} article
- {data.unreadArticles.length !== 1 ? "s" : ""}
+ {data.unreadArticles.length !== 1 ? "s" : ""} to read
</p>
)}
</div>