commit 9ba7ed427d539732a18b1e541b6bc448352416ce
parent ead3ccc62a182b566252506552269555be31814a
Author: triesap <tyson@radroots.org>
Date: Tue, 27 Jan 2026 16:44:30 +0000
ui: normalize app layout and ids
Diffstat:
6 files changed, 294 insertions(+), 207 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -2704,6 +2704,7 @@ name = "ui-primitives-leptos"
version = "0.1.0"
dependencies = [
"leptos",
+ "send_wrapper",
"ui-primitives-core",
"wasm-bindgen",
"web-sys",
diff --git a/app/app.css b/app/app.css
@@ -62,8 +62,10 @@
background: var(--bg-app);
color: var(--text-primary);
height: 100%;
+ width: 100%;
overscroll-behavior: none;
touch-action: manipulation;
+ overflow: hidden;
}
body {
@@ -71,7 +73,9 @@
margin: 0;
background: var(--bg-app);
height: 100%;
+ width: 100%;
overscroll-behavior: none;
+ overflow: hidden;
}
* {
@@ -97,23 +101,53 @@
@layer base {
#app-root {
min-height: 100dvh;
- height: 100%;
+ height: 100dvh;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ overscroll-behavior: none;
}
#app-shell {
- min-height: 100dvh;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ flex: 1 1 auto;
+ min-height: 0;
+ overflow: hidden;
+ overscroll-behavior: none;
+ }
+}
+
+@layer components {
+ .app-page {
+ position: relative;
+ width: 100%;
display: flex;
flex-direction: column;
+ flex: 1 1 auto;
+ min-height: 0;
+ overscroll-behavior: none;
+ }
+
+ .app-page-fixed {
+ overflow: hidden;
}
- [data-app-scroll] {
+ .app-page-scroll {
overflow-y: auto;
+ overflow-x: hidden;
-webkit-overflow-scrolling: touch;
- overscroll-behavior: contain;
+ overscroll-behavior: none;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+ }
+
+ .app-page-scroll::-webkit-scrollbar {
+ display: none;
}
-}
-@layer components {
.app-view {
will-change: transform, opacity;
}
diff --git a/app/src/app.rs b/app/src/app.rs
@@ -183,6 +183,7 @@ fn SplashPage() -> impl IntoView {
view! {
<main
id="app-splash"
+ class="app-page app-page-fixed"
style="min-height:100dvh;background:white;display:flex;align-items:center;justify-content:center;"
>
</main>
@@ -192,8 +193,14 @@ fn SplashPage() -> impl IntoView {
#[component]
fn LogoCircle() -> impl IntoView {
view! {
- <div class="relative flex flex-col h-[196px] w-full justify-center items-center">
- <div class="relative flex flex-row h-36 w-36 justify-center items-center bg-ly2 rounded-full">
+ <div
+ id="app-logo-circle"
+ class="relative flex flex-col h-[196px] w-full justify-center items-center"
+ >
+ <div
+ id="app-logo-mark"
+ class="relative flex flex-row h-36 w-36 justify-center items-center bg-ly2 rounded-full"
+ >
<p class="font-sans font-[900] text-6xl text-ly0-gl -tracking-[0.4rem] -translate-x-[6px]">
"\u{00BB}`,"
</p>
@@ -296,8 +303,7 @@ fn SetupPage() -> impl IntoView {
view! {
<main
id="app-setup"
- data-app-scroll
- class="relative min-h-[100dvh] h-[100dvh] w-full flex flex-col"
+ class="app-page app-page-fixed relative w-full flex flex-col"
>
{move || match setup_step.get() {
RadrootsAppSetupStep::Intro => {
@@ -305,38 +311,57 @@ fn SetupPage() -> impl IntoView {
view! {
<section
id="app-setup-intro"
- class="app-view app-view-enter relative flex flex-col h-[100dvh] w-full justify-start items-center"
+ class="app-view app-view-enter relative flex flex-col h-[100dvh] w-full justify-start items-center"
>
- <div class="flex flex-col h-full w-full justify-start items-center">
- <div class="relative flex flex-col h-full w-full justify-center items-center">
- <div class="flex flex-row w-full justify-start items-center -translate-y-16">
+ <div
+ id="app-setup-intro-body"
+ class="flex flex-col h-full w-full justify-start items-center"
+ >
+ <div
+ id="app-setup-intro-stage"
+ class="relative flex flex-col h-full w-full justify-center items-center"
+ >
+ <header
+ id="app-setup-intro-header"
+ class="flex flex-row w-full justify-start items-center -translate-y-16"
+ >
<button
type="button"
+ id="app-setup-intro-logo-button"
class="flex flex-row w-full justify-center items-center"
on:click=move |_| navigate_home("/", Default::default())
>
<LogoCircle />
</button>
- </div>
- <div class="absolute bottom-0 left-0 flex flex-col h-[20rem] w-full px-10 gap-2 justify-start items-center">
- <div class="flex flex-row w-full justify-start items-center">
- <p class="font-sans font-[400] text-sm uppercase text-ly0-gl-label">
- "Configure"
+ </header>
+ <footer
+ id="app-setup-intro-footer"
+ class="absolute bottom-0 left-0 flex flex-col h-[20rem] w-full px-10 gap-2 justify-start items-center"
+ >
+ <p
+ id="app-setup-intro-kicker"
+ class="w-full text-left font-sans font-[400] text-sm uppercase text-ly0-gl-label"
+ >
+ "Configure"
+ </p>
+ <div
+ id="app-setup-intro-copy"
+ class="flex flex-col w-full gap-2 justify-start items-center"
+ >
+ <p
+ id="app-setup-intro-line-welcome"
+ class="w-full text-left font-mono font-[400] text-[1.1rem] text-ly0-gl"
+ >
+ "Welcome to Radroots!"
+ </p>
+ <p
+ id="app-setup-intro-line-body"
+ class="w-full text-left font-mono font-[400] text-[1.1rem] text-ly0-gl"
+ >
+ "Your device will be configured by the setup wizard."
</p>
</div>
- <div class="flex flex-col w-full gap-2 justify-start items-center">
- <div class="flex flex-row w-full justify-start items-center">
- <p class="font-mono font-[400] text-[1.1rem] text-ly0-gl">
- "Welcome to Radroots!"
- </p>
- </div>
- <div class="flex flex-row w-full justify-start items-center">
- <p class="font-mono font-[400] text-[1.1rem] text-ly0-gl">
- "Your device will be configured by the setup wizard."
- </p>
- </div>
- </div>
- </div>
+ </footer>
</div>
</div>
</section>
@@ -348,7 +373,7 @@ fn SetupPage() -> impl IntoView {
id="app-setup-key-choice"
class="app-view app-view-enter flex flex-col w-full gap-4 px-6 pt-10 pb-16"
>
- <header class="flex flex-col gap-2">
+ <header id="app-setup-key-choice-header" class="flex flex-col gap-2">
<p class="font-sans text-sm uppercase tracking-[0.14em] text-ly0-gl-label">
"Setup"
</p>
@@ -359,11 +384,16 @@ fn SetupPage() -> impl IntoView {
"Select how you want to add your Nostr key."
</p>
</header>
- <RadrootsAppUiListView basis=key_choice_list.clone() />
+ <div id="app-setup-key-choice-list" class="w-full">
+ <RadrootsAppUiListView basis=key_choice_list.clone() />
+ </div>
</section>
}.into_any(),
}}
- <div class="z-10 absolute bottom-10 left-0 flex flex-col w-full justify-center items-center">
+ <footer
+ id="app-setup-actions"
+ class="z-10 absolute bottom-4 left-0 flex flex-col w-full justify-center items-center"
+ >
{move || {
let step = setup_step.get();
let continue_action = RadrootsAppUiButtonLayoutAction {
@@ -381,11 +411,11 @@ fn SetupPage() -> impl IntoView {
view! {
<RadrootsAppUiButtonLayoutPair
continue_action=continue_action
- back=Some(back_action)
+ back=back_action
/>
}
}}
- </div>
+ </footer>
</main>
}
}
@@ -486,84 +516,92 @@ fn HomePage() -> impl IntoView {
}
};
view! {
- <main id="app-home" data-app-scroll>
- <div>"app"</div>
- <div style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
- <span
- style=move || format!(
- "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
- status_color()
- )
- ></span>
- <span>{move || init_state.get().stage.as_str()}</span>
- </div>
- <div style="margin-top: 12px; display: flex; align-items: center; gap: 8px;">
- <button
- on:click=move |_| {
- let config = backends.with_untracked(|value| value.as_ref().map(|backends| backends.config.clone()));
- reset_status.set(Some("resetting".to_string()));
- health_report.set(RadrootsAppHealthReport::empty());
- active_key.set(None);
- notifications_status.set(None);
- setup_required.set(Some(true));
- spawn_local(async move {
- let Some(config) = config else {
- reset_status.set(Some("reset_missing_backends".to_string()));
- return;
- };
- let datastore = radroots_app_core::datastore::RadrootsClientWebDatastore::new(
- Some(config.datastore.idb_config),
- );
- let keystore = radroots_app_core::keystore::RadrootsClientWebKeystoreNostr::new(
- Some(config.keystore.nostr_store),
- );
- match app_init_reset(
- Some(&datastore),
- Some(&config.datastore.key_maps),
- Some(&keystore),
- )
- .await
- {
- Ok(()) => {
- let log_datastore = logs_datastore();
- if let Err(err) = log_datastore.reset().await {
- let reset_err = RadrootsAppInitError::Datastore(err);
- let _ = app_log_error_emit(&reset_err);
- reset_status.set(Some(reset_err.to_string()));
- return;
+ <main id="app-home" class="app-page app-page-scroll">
+ <header id="app-home-header">
+ <h1 id="app-home-title">"app"</h1>
+ </header>
+ <section id="app-home-status" aria-label="Status">
+ <div id="app-home-status-row" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
+ <span
+ style=move || format!(
+ "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
+ status_color()
+ )
+ ></span>
+ <span>{move || init_state.get().stage.as_str()}</span>
+ </div>
+ </section>
+ <section id="app-home-reset" aria-label="Reset">
+ <div id="app-home-reset-row" style="margin-top: 12px; display: flex; align-items: center; gap: 8px;">
+ <button
+ on:click=move |_| {
+ let config = backends.with_untracked(|value| value.as_ref().map(|backends| backends.config.clone()));
+ reset_status.set(Some("resetting".to_string()));
+ health_report.set(RadrootsAppHealthReport::empty());
+ active_key.set(None);
+ notifications_status.set(None);
+ setup_required.set(Some(true));
+ spawn_local(async move {
+ let Some(config) = config else {
+ reset_status.set(Some("reset_missing_backends".to_string()));
+ return;
+ };
+ let datastore = radroots_app_core::datastore::RadrootsClientWebDatastore::new(
+ Some(config.datastore.idb_config),
+ );
+ let keystore = radroots_app_core::keystore::RadrootsClientWebKeystoreNostr::new(
+ Some(config.keystore.nostr_store),
+ );
+ match app_init_reset(
+ Some(&datastore),
+ Some(&config.datastore.key_maps),
+ Some(&keystore),
+ )
+ .await
+ {
+ Ok(()) => {
+ let log_datastore = logs_datastore();
+ if let Err(err) = log_datastore.reset().await {
+ let reset_err = RadrootsAppInitError::Datastore(err);
+ let _ = app_log_error_emit(&reset_err);
+ reset_status.set(Some(reset_err.to_string()));
+ return;
+ }
+ reset_status.set(Some("reset_done".to_string()));
+ spawn_health_checks(
+ config,
+ true,
+ health_report,
+ health_running,
+ active_key,
+ notifications_status,
+ );
+ }
+ Err(err) => {
+ let log_datastore = logs_datastore();
+ let _ = app_log_error_store(
+ &log_datastore,
+ &config.datastore.key_maps,
+ &err,
+ )
+ .await;
+ reset_status.set(Some(err.to_string()));
}
- reset_status.set(Some("reset_done".to_string()));
- spawn_health_checks(
- config,
- true,
- health_report,
- health_running,
- active_key,
- notifications_status,
- );
- }
- Err(err) => {
- let log_datastore = logs_datastore();
- let _ = app_log_error_store(
- &log_datastore,
- &config.datastore.key_maps,
- &err,
- )
- .await;
- reset_status.set(Some(err.to_string()));
}
- }
- });
- }
- disabled=reset_disabled
- >
- "reset"
- </button>
- <span>{reset_label}</span>
- </div>
- <div style="margin-top: 16px;">
- <div style="font-weight: 600;">"notifications"</div>
- <div style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
+ });
+ }
+ disabled=reset_disabled
+ >
+ "reset"
+ </button>
+ <span>{reset_label}</span>
+ </div>
+ </section>
+ <section id="app-home-notifications" aria-label="Notifications" style="margin-top: 16px;">
+ <header id="app-home-notifications-header">
+ <h2 id="app-home-notifications-title" style="font-weight: 600;">"notifications"</h2>
+ </header>
+ <div id="app-home-notifications-actions" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
<button
on:click=move |_| {
let config = backends.with_untracked(|value| value.as_ref().map(|backends| backends.config.clone()));
@@ -616,10 +654,12 @@ fn HomePage() -> impl IntoView {
</button>
<span>{notifications_label}</span>
</div>
- </div>
- <div style="margin-top: 16px;">
- <div style="font-weight: 600;">"health checks"</div>
- <div style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
+ </section>
+ <section id="app-home-health" aria-label="Health checks" style="margin-top: 16px;">
+ <header id="app-home-health-header">
+ <h2 id="app-home-health-title" style="font-weight: 600;">"health checks"</h2>
+ </header>
+ <div id="app-home-health-actions" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
<button
on:click=move |_| {
let config = backends.with_untracked(|value| value.as_ref().map(|backends| backends.config.clone()));
@@ -643,8 +683,8 @@ fn HomePage() -> impl IntoView {
{move || if health_running.get() { "checking" } else { "run checks" }}
</button>
</div>
- <div style="margin-top: 8px; display: grid; gap: 6px;">
- <div style="display: flex; align-items: center; gap: 8px;">
+ <ul id="app-home-health-list" style="margin-top: 8px; display: grid; gap: 6px;">
+ <li id="app-home-health-key-maps" style="display: flex; align-items: center; gap: 8px;">
<span
style=move || format!(
"display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
@@ -653,8 +693,8 @@ fn HomePage() -> impl IntoView {
></span>
<span>"key_maps"</span>
<span>{move || health_result_label(&health_report.get().key_maps)}</span>
- </div>
- <div style="display: flex; align-items: center; gap: 8px;">
+ </li>
+ <li id="app-home-health-bootstrap-state" style="display: flex; align-items: center; gap: 8px;">
<span
style=move || format!(
"display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
@@ -663,8 +703,8 @@ fn HomePage() -> impl IntoView {
></span>
<span>"bootstrap_state"</span>
<span>{move || health_result_label(&health_report.get().bootstrap_state)}</span>
- </div>
- <div style="display: flex; align-items: center; gap: 8px;">
+ </li>
+ <li id="app-home-health-active-key-state" style="display: flex; align-items: center; gap: 8px;">
<span
style=move || format!(
"display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
@@ -673,8 +713,8 @@ fn HomePage() -> impl IntoView {
></span>
<span>"state_active_key"</span>
<span>{move || health_result_label(&health_report.get().state_active_key)}</span>
- </div>
- <div style="display: flex; align-items: center; gap: 8px;">
+ </li>
+ <li id="app-home-health-notifications" style="display: flex; align-items: center; gap: 8px;">
<span
style=move || format!(
"display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
@@ -683,8 +723,8 @@ fn HomePage() -> impl IntoView {
></span>
<span>"notifications"</span>
<span>{move || health_result_label(&health_report.get().notifications)}</span>
- </div>
- <div style="display: flex; align-items: center; gap: 8px;">
+ </li>
+ <li id="app-home-health-tangle" style="display: flex; align-items: center; gap: 8px;">
<span
style=move || format!(
"display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
@@ -693,8 +733,8 @@ fn HomePage() -> impl IntoView {
></span>
<span>"tangle"</span>
<span>{move || health_result_label(&health_report.get().tangle)}</span>
- </div>
- <div style="display: flex; align-items: center; gap: 8px;">
+ </li>
+ <li id="app-home-health-datastore" style="display: flex; align-items: center; gap: 8px;">
<span
style=move || format!(
"display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
@@ -703,8 +743,8 @@ fn HomePage() -> impl IntoView {
></span>
<span>"datastore_roundtrip"</span>
<span>{move || health_result_label(&health_report.get().datastore_roundtrip)}</span>
- </div>
- <div style="display: flex; align-items: center; gap: 8px;">
+ </li>
+ <li id="app-home-health-keystore" style="display: flex; align-items: center; gap: 8px;">
<span
style=move || format!(
"display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
@@ -713,13 +753,13 @@ fn HomePage() -> impl IntoView {
></span>
<span>"keystore"</span>
<span>{move || health_result_label(&health_report.get().keystore)}</span>
- </div>
- <div style="display: flex; align-items: center; gap: 8px;">
+ </li>
+ <li id="app-home-health-active-key" style="display: flex; align-items: center; gap: 8px;">
<span>"active_key"</span>
<span>{move || active_key_label(active_key.get())}</span>
- </div>
- </div>
- </div>
+ </li>
+ </ul>
+ </section>
</main>
}
}
@@ -860,7 +900,13 @@ fn AppShell() -> impl IntoView {
<A href="/settings">"settings"</A>
<A href="/setup">"setup"</A>
</nav>
- <Routes fallback=|| view! { <div>"not_found"</div> }>
+ <Routes
+ fallback=|| view! {
+ <main id="app-not-found" class="app-page app-page-fixed">
+ <p id="app-not-found-label">"not_found"</p>
+ </main>
+ }
+ >
<Route path=path!("") view=HomePage />
<Route path=path!("logs") view=RadrootsAppLogsPage />
<Route path=path!("ui") view=RadrootsAppUiDemoPage />
diff --git a/app/src/logs.rs b/app/src/logs.rs
@@ -571,17 +571,17 @@ pub fn RadrootsAppLogsPage() -> impl IntoView {
total == 0 || page_index.get() + 1 >= total
};
view! {
- <main>
- <div style="display:flex;align-items:center;gap:12px;">
- <div style="font-size:18px;font-weight:600;">"logs"</div>
+ <main id="app-logs" class="app-page app-page-scroll">
+ <header id="app-logs-header" style="display:flex;align-items:center;gap:12px;">
+ <h1 id="app-logs-title" style="font-size:18px;font-weight:600;">"logs"</h1>
<button on:click=move |_| refresh()>"refresh"</button>
<button on:click=move |_| clear()>"clear"</button>
<button on:click=move |_| copy_dump() disabled=dump_action_disabled>"copy dump"</button>
<button on:click=move |_| download_dump() disabled=dump_action_disabled>"download dump"</button>
- <div style="font-size:12px;color:#6b7280;">{status_label}</div>
- <div style="font-size:12px;color:#6b7280;">{dump_action_label}</div>
- </div>
- <div style="margin-top:12px;display:flex;flex-wrap:wrap;gap:12px;align-items:center;">
+ <div id="app-logs-status" style="font-size:12px;color:#6b7280;">{status_label}</div>
+ <div id="app-logs-dump-status" style="font-size:12px;color:#6b7280;">{dump_action_label}</div>
+ </header>
+ <section id="app-logs-filters" aria-label="Filters" style="margin-top:12px;display:flex;flex-wrap:wrap;gap:12px;align-items:center;">
<input
type="text"
placeholder="search code/message/context"
@@ -637,7 +637,7 @@ pub fn RadrootsAppLogsPage() -> impl IntoView {
<option value="100">"100"</option>
<option value="250">"250"</option>
</select>
- <div style="font-size:12px;color:#6b7280;">
+ <div id="app-logs-filter-summary" style="font-size:12px;color:#6b7280;">
{move || {
let total = entries.get().len();
let visible = filtered_entries.get().len();
@@ -647,8 +647,8 @@ pub fn RadrootsAppLogsPage() -> impl IntoView {
format!("showing {visible} of {total} (limit {limit}) page {page}/{pages}")
}}
</div>
- </div>
- <div style="margin-top:8px;display:flex;align-items:center;gap:8px;">
+ </section>
+ <section id="app-logs-pagination" aria-label="Pagination" style="margin-top:8px;display:flex;align-items:center;gap:8px;">
<button
on:click=move |_| {
let next = page_index.get().saturating_sub(1);
@@ -673,12 +673,12 @@ pub fn RadrootsAppLogsPage() -> impl IntoView {
<button on:click=move |_| copy_support() disabled=support_disabled>
"copy instructions"
</button>
- <div style="font-size:12px;color:#6b7280;">{support_label}</div>
- </div>
- <div style="margin-top:12px;display:flex;flex-wrap:wrap;gap:16px;">
- <section style="flex:1 1 520px;min-width:280px;">
- <div style="font-weight:600;font-size:14px;">"entries"</div>
- <div style="margin-top:8px;border:1px solid #e5e7eb;border-radius:8px;height:60vh;overflow:auto;padding:10px;display:flex;flex-direction:column;gap:10px;">
+ <div id="app-logs-support-status" style="font-size:12px;color:#6b7280;">{support_label}</div>
+ </section>
+ <section id="app-logs-content" aria-label="Log content" style="margin-top:12px;display:flex;flex-wrap:wrap;gap:16px;">
+ <section id="app-logs-entries" style="flex:1 1 520px;min-width:280px;">
+ <h2 id="app-logs-entries-title" style="font-weight:600;font-size:14px;">"entries"</h2>
+ <div id="app-logs-entries-list" style="margin-top:8px;border:1px solid #e5e7eb;border-radius:8px;height:60vh;overflow:auto;padding:10px;display:flex;flex-direction:column;gap:10px;">
<For
each=move || paged_entries.get()
key=|entry| entry.id.clone()
@@ -722,21 +722,21 @@ pub fn RadrootsAppLogsPage() -> impl IntoView {
/>
</div>
</section>
- <section style="flex:1 1 320px;min-width:260px;">
- <div style="font-weight:600;font-size:14px;">"dump (jsonl)"</div>
+ <section id="app-logs-dump" style="flex:1 1 320px;min-width:260px;">
+ <h2 id="app-logs-dump-title" style="font-weight:600;font-size:14px;">"dump (jsonl)"</h2>
<textarea
readonly
prop:value=move || dump_text.get()
style="margin-top:8px;width:100%;height:60vh;border:1px solid #e5e7eb;border-radius:8px;padding:8px;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace;"
></textarea>
- <div style="margin-top:12px;font-weight:600;font-size:14px;">"support instructions"</div>
+ <h3 id="app-logs-support-title" style="margin-top:12px;font-weight:600;font-size:14px;">"support instructions"</h3>
<textarea
readonly
prop:value=move || support_instructions_text()
style="margin-top:8px;width:100%;height:140px;border:1px solid #e5e7eb;border-radius:8px;padding:8px;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace;"
></textarea>
</section>
- </div>
+ </section>
</main>
}
}
diff --git a/app/src/settings.rs b/app/src/settings.rs
@@ -185,12 +185,14 @@ pub fn RadrootsAppSettingsPage() -> impl IntoView {
styles: None,
};
view! {
- <main style="padding: 16px;">
- <div style="font: var(--type-title2); margin-bottom: 12px;">"settings"</div>
- <div style="display:flex;flex-direction:column;gap:16px;">
+ <main id="app-settings" class="app-page app-page-scroll" style="padding: 16px;">
+ <header id="app-settings-header" style="font: var(--type-title2); margin-bottom: 12px;">
+ <h1 id="app-settings-title">"settings"</h1>
+ </header>
+ <section id="app-settings-content" style="display:flex;flex-direction:column;gap:16px;">
<RadrootsAppUiListView basis=appearance_list />
<RadrootsAppUiListView basis=actions_list />
- </div>
+ </section>
</main>
}
}
diff --git a/app/src/ui_demo.rs b/app/src/ui_demo.rs
@@ -181,62 +181,66 @@ pub fn RadrootsAppUiDemoPage() -> impl IntoView {
}),
};
view! {
- <main style="padding: 16px;">
- <div style="font: var(--type-title2); margin-bottom: 12px;">"UI Demo"</div>
- <RadrootsAppUiListView basis=list />
+ <main id="app-ui-demo" class="app-page app-page-scroll" style="padding: 16px;">
+ <header id="app-ui-demo-header" style="font: var(--type-title2); margin-bottom: 12px;">
+ <h1 id="app-ui-demo-title">"UI Demo"</h1>
+ </header>
+ <section id="app-ui-demo-content">
+ <RadrootsAppUiListView basis=list />
- <RadrootsAppUiSheetRoot
- open=Some(sheet_open_read)
- default_open=false
- modal=None
- on_open_change=Some(sheet_open_set)
- >
- <RadrootsAppUiSheetTrigger
- disabled=false
- class=Some("ui-card".to_string())
- id=None
- style=Some("padding:12px 16px; width: 100%; text-align: left;".to_string())
+ <RadrootsAppUiSheetRoot
+ open=Some(sheet_open_read)
+ default_open=false
+ modal=None
+ on_open_change=Some(sheet_open_set)
>
- "Open Sheet"
- </RadrootsAppUiSheetTrigger>
- <RadrootsAppUiSheetPortal>
- <RadrootsAppUiSheetOverlay
- close_on_click=None
- class=None
+ <RadrootsAppUiSheetTrigger
+ disabled=false
+ class=Some("ui-card".to_string())
id=None
- style=None
- />
- <RadrootsAppUiSheetContent
- disable_outside_pointer_events=false
- show_handle=true
- class=None
- id=None
- style=None
+ style=Some("padding:12px 16px; width: 100%; text-align: left;".to_string())
>
- <RadrootsAppUiSheetTitle
+ "Open Sheet"
+ </RadrootsAppUiSheetTrigger>
+ <RadrootsAppUiSheetPortal>
+ <RadrootsAppUiSheetOverlay
+ close_on_click=None
class=None
id=None
style=None
- >
- "Sheet Preview"
- </RadrootsAppUiSheetTitle>
- <RadrootsAppUiSheetDescription
+ />
+ <RadrootsAppUiSheetContent
+ disable_outside_pointer_events=false
+ show_handle=true
class=None
id=None
- style=Some("margin-top: 6px;".to_string())
- >
- "This is a placeholder sheet for iOS styling."
- </RadrootsAppUiSheetDescription>
- <RadrootsAppUiSheetClose
- class=Some("ui-card".to_string())
- id=None
- style=Some("margin-top: 16px; padding: 10px 14px;".to_string())
+ style=None
>
- "Close"
- </RadrootsAppUiSheetClose>
- </RadrootsAppUiSheetContent>
- </RadrootsAppUiSheetPortal>
- </RadrootsAppUiSheetRoot>
+ <RadrootsAppUiSheetTitle
+ class=None
+ id=None
+ style=None
+ >
+ "Sheet Preview"
+ </RadrootsAppUiSheetTitle>
+ <RadrootsAppUiSheetDescription
+ class=None
+ id=None
+ style=Some("margin-top: 6px;".to_string())
+ >
+ "This is a placeholder sheet for iOS styling."
+ </RadrootsAppUiSheetDescription>
+ <RadrootsAppUiSheetClose
+ class=Some("ui-card".to_string())
+ id=None
+ style=Some("margin-top: 16px; padding: 10px 14px;".to_string())
+ >
+ "Close"
+ </RadrootsAppUiSheetClose>
+ </RadrootsAppUiSheetContent>
+ </RadrootsAppUiSheetPortal>
+ </RadrootsAppUiSheetRoot>
+ </section>
</main>
}
}