diff --git a/crates/web/assets/app.css b/crates/web/assets/app.css index eff0f5f..205f3db 100644 --- a/crates/web/assets/app.css +++ b/crates/web/assets/app.css @@ -2,7 +2,14 @@ body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; height: 10 .app-shell { display: flex; height: 100vh; } .content-header { display: flex; align-items: center; gap: 0.75rem; padding: 1.5rem 1.5rem 0.5rem; } .content-header h2 { margin: 0; } -.article-scroll-area { flex: 1; min-height: 0; padding: 0 1.5rem 1.5rem; } +/* Target the primitive's own stable data attribute, not a class we pass in: + ScrollArea sets its own `class` after spreading our attributes, so a + caller-supplied class never reaches the DOM (confirmed by inspecting the + live element — it only ever carries "dx-scroll-area-auto-hide"). Without + this, the scroll div has no bounded height, so it just grows to fit every + article (nothing to scroll) while its ancestor's overflow: hidden clips + everything past the viewport instead. */ +main > [data-scroll-direction] { flex: 1; min-height: 0; padding: 0 1.5rem 1.5rem; } .article-list { list-style: none; padding: 0; margin: 0; } .article-row { border-bottom: 1px solid #ddd; padding: 1rem 0; display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; } .article-row-header { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.5rem; } diff --git a/crates/web/src/app/mod.rs b/crates/web/src/app/mod.rs index ec6c9b0..2467f81 100644 --- a/crates/web/src/app/mod.rs +++ b/crates/web/src/app/mod.rs @@ -85,37 +85,36 @@ pub fn App() -> Element { } } SidebarInset { - main { - div { class: "content-header", - SidebarTrigger {} - h2 { - { - let heading = match selected_feed.read().as_ref() { - None => "All articles".to_string(), - Some(id) => feeds - .read() - .as_ref() - .and_then(|r| r.as_ref().ok()) - .and_then(|feeds| feeds.iter().find(|f| &f.id == id)) - .map(|f| f.title.clone()) - .unwrap_or_else(|| "Feed".to_string()), - }; - rsx! { "{heading}" } - } + div { class: "content-header", + SidebarTrigger {} + h2 { + { + let heading = match selected_feed.read().as_ref() { + None => "All articles".to_string(), + Some(id) => feeds + .read() + .as_ref() + .and_then(|r| r.as_ref().ok()) + .and_then(|feeds| feeds.iter().find(|f| &f.id == id)) + .map(|f| f.title.clone()) + .unwrap_or_else(|| "Feed".to_string()), + }; + rsx! { "{heading}" } } } - ScrollArea { class: "article-scroll-area", - match articles.read().as_ref() { - Some(Ok(articles)) => rsx! { - ul { class: "article-list", - for article in articles.iter() { - ArticleRow { article: article.clone() } - } + } + ScrollArea { + direction: dioxus_primitives::scroll_area::ScrollDirection::Vertical, + match articles.read().as_ref() { + Some(Ok(articles)) => rsx! { + ul { class: "article-list", + for article in articles.iter() { + ArticleRow { article: article.clone() } } - }, - Some(Err(err)) => rsx! { p { class: "error", "Failed to load articles: {err}" } }, - None => rsx! { p { "Loading..." } }, - } + } + }, + Some(Err(err)) => rsx! { p { class: "error", "Failed to load articles: {err}" } }, + None => rsx! { p { "Loading..." } }, } } }