From 490edf9e9f2911231df6c76ef4afeb3b1fb763d2 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Sat, 14 Feb 2026 14:49:09 -0800 Subject: task: improve mobile responsiveness of React UI\n\n- Added media queries to App.css to handle mobile sidebar layout (overlay with backdrop)\n- Implemented auto-hiding sidebar on mobile when links are clicked\n- Reduced padding and adjusted max-widths for smaller screens in App.css, FeedItem.css, and Settings.css\n- Added window resize listener to Dashboard to manage sidebar visibility based on width\n- Verified all existing tests pass\n\nFixes NK-g818qn --- frontend/src/App.css | 65 ++++++++++++++++++++++++++++++++++-- frontend/src/App.tsx | 33 +++++++++++++++--- frontend/src/components/FeedItem.css | 22 ++++++++++++ frontend/src/components/FeedList.tsx | 18 +++++++--- frontend/src/components/Settings.css | 15 +++++++++ 5 files changed, 141 insertions(+), 12 deletions(-) (limited to 'frontend/src') diff --git a/frontend/src/App.css b/frontend/src/App.css index 773e114..f47c179 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -58,16 +58,75 @@ body { top: 1rem; left: 1rem; z-index: 1000; - background: transparent; + background: var(--bg-color); + /* Added bg to be visible over content if needed */ border: none; font-size: 2rem; line-height: 1; cursor: pointer; - padding: 0; + padding: 0.2rem; color: var(--text-color); - /* Inherit didn't work well if parent is transparent */ + border-radius: 50%; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + display: flex; + align-items: center; + justify-content: center; } .fixed-toggle:hover { transform: scale(1.1); +} + +/* Mobile Responsiveness */ +@media (max-width: 768px) { + .dashboard-sidebar { + position: fixed; + top: 0; + left: 0; + bottom: 0; + z-index: 1100; + box-shadow: 2px 0 10px rgba(0, 0, 0, 0.2); + width: 14rem; + /* Slightly wider on mobile for better target area */ + } + + .dashboard-sidebar.hidden { + margin-left: -14rem; + } + + .dashboard-main { + padding: 1rem; + padding-top: 4rem; + /* Space for the toggle button */ + } + + .dashboard-main>* { + max-width: 100%; + } + + /* When sidebar is visible on mobile, we show a backdrop */ + .sidebar-backdrop { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.4); + z-index: 1050; + animation: fadeIn 0.3s ease; + } + + .dashboard.sidebar-visible::after { + display: none; + } +} + +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } } \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1812451..478444c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -44,24 +44,47 @@ interface DashboardProps { } function Dashboard({ theme, setTheme, fontTheme, setFontTheme }: DashboardProps) { - const [sidebarVisible, setSidebarVisible] = useState(true); + const [sidebarVisible, setSidebarVisible] = useState(window.innerWidth > 768); + + useEffect(() => { + const handleResize = () => { + if (window.innerWidth > 768) { + setSidebarVisible(true); + } else { + setSidebarVisible(false); + } + }; + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); return (
- {!sidebarVisible && ( + {(!sidebarVisible || window.innerWidth <= 768) && ( )} + {sidebarVisible && ( +
setSidebarVisible(false)} + /> + )}
diff --git a/frontend/src/components/FeedItem.css b/frontend/src/components/FeedItem.css index 4eca2b1..944b66a 100644 --- a/frontend/src/components/FeedItem.css +++ b/frontend/src/components/FeedItem.css @@ -123,4 +123,26 @@ .scrape-btn:hover { background: var(--sidebar-bg); +} + +@media (max-width: 768px) { + .feed-item { + margin-top: 2rem; + padding: 0.5rem; + } + + .item-title { + font-size: 1.4rem; + word-break: break-word; + } + + .item-header { + flex-direction: column; + gap: 0.5rem; + } + + .item-actions { + margin-left: 0; + margin-bottom: 0.5rem; + } } \ No newline at end of file diff --git a/frontend/src/components/FeedList.tsx b/frontend/src/components/FeedList.tsx index 4ce2d71..250ce7d 100644 --- a/frontend/src/components/FeedList.tsx +++ b/frontend/src/components/FeedList.tsx @@ -8,10 +8,12 @@ export default function FeedList({ theme, setTheme, setSidebarVisible, + isMobile, }: { theme: string; setTheme: (t: string) => void; setSidebarVisible: (visible: boolean) => void; + isMobile: boolean; }) { const [feeds, setFeeds] = useState([]); const [tags, setTags] = useState([]); @@ -39,6 +41,12 @@ export default function FeedList({ setFeedsExpanded(!feedsExpanded); }; + const handleLinkClick = () => { + if (isMobile) { + setSidebarVisible(false); + } + }; + useEffect(() => { Promise.all([ apiFetch('/api/feed/').then((res) => { @@ -89,17 +97,17 @@ export default function FeedList({
  • - + unread
  • - + all
  • - + starred
  • @@ -116,6 +124,7 @@ export default function FeedList({ {tag.title} @@ -138,6 +147,7 @@ export default function FeedList({ {feed.title || feed.url} @@ -150,7 +160,7 @@ export default function FeedList({
    • - + settings
    • diff --git a/frontend/src/components/Settings.css b/frontend/src/components/Settings.css index c8784a0..3738e61 100644 --- a/frontend/src/components/Settings.css +++ b/frontend/src/components/Settings.css @@ -110,8 +110,23 @@ } @media (max-width: 600px) { + .settings-page { + padding: 1rem; + } + + .add-feed-form { + flex-direction: column; + } + .import-export-section { flex-direction: column; + gap: 1rem; + } + + .settings-feed-item { + flex-direction: column; + align-items: flex-start; + gap: 1rem; } } -- cgit v1.2.3