commit d09bbf957567a49a33a36d468a4c05d2c65b30b4
parent e2404b61557a9a8d358c4d5a1f15a04ec6aeb1cb
Author: triesap <triesap@radroots.dev>
Date: Thu, 22 Jan 2026 22:45:14 +0000
app: refresh setup intro view
- add logo circle component for setup intro
- restructure intro layout into full-height sections
- wire intro tap to home navigation
- add app-view enter animation utilities
Diffstat:
4 files changed, 200 insertions(+), 30 deletions(-)
diff --git a/app/app.css b/app/app.css
@@ -4,6 +4,7 @@
@import "../crates/ui-tokens/assets/themes/layout.css";
@import "../crates/ui-tokens/assets/themes/screens.css";
@import "../crates/ui-tokens/assets/themes/theme_os.css";
+@import "../crates/ui-tokens/assets/themes/theme_os_daisyui.css";
@import "./stylesheets/daisyui.css";
@import "./stylesheets/apps-base.css";
@import "./stylesheets/apps-ui.css";
@@ -15,6 +16,10 @@
@source "./src/**/*.rs";
@source "../crates/**/*.rs";
+@plugin "daisyui" {
+ themes: os_light, os_dark;
+}
+
@theme {
--font-sans: "SF Pro Rounded", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-sansd: "SF Pro Display", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
@@ -114,6 +119,20 @@
}
@layer components {
+ .app-view {
+ will-change: transform, opacity;
+ }
+
+ .app-view-enter {
+ animation: app-view-enter 360ms var(--ease-ios) both;
+ }
+
+ @media (prefers-reduced-motion: reduce) {
+ .app-view-enter {
+ animation: none;
+ }
+ }
+
.ui-surface {
background: var(--bg-elevated);
color: var(--text-primary);
@@ -240,3 +259,18 @@
opacity: 0;
}
}
+
+@keyframes app-view-enter {
+ 0% {
+ opacity: 0;
+ transform: translateY(10px) scale(0.98);
+ }
+ 60% {
+ opacity: 1;
+ transform: translateY(-2px) scale(1.005);
+ }
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
diff --git a/app/package.json b/app/package.json
@@ -2,6 +2,6 @@
"name": "radroots-app",
"private": true,
"devDependencies": {
- "daisyui": "^4.12.0"
+ "daisyui": "^5.5.14"
}
-}
+}
+\ No newline at end of file
diff --git a/app/src/app.rs b/app/src/app.rs
@@ -187,6 +187,22 @@ 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">
+ <p class="font-sans font-[900] text-6xl text-ly0-gl -tracking-[0.4rem] -translate-x-[6px]">
+ "\u{00BB}`,"
+ </p>
+ <p class="font-sans font-[900] text-6xl text-ly0-gl translate-x-[8px]">
+ "-"
+ </p>
+ </div>
+ </div>
+ }
+}
+
+#[component]
fn SetupPage() -> impl IntoView {
let context = app_context();
let fallback_setup_required = RwSignal::new_local(None::<bool>);
@@ -196,6 +212,7 @@ fn SetupPage() -> impl IntoView {
.unwrap_or(fallback_setup_required);
let navigate = use_navigate();
let navigate_guard = navigate.clone();
+ let navigate_home = navigate.clone();
let setup_step = RwSignal::new_local(app_setup_step_default());
let key_choice_list = RadrootsAppUiList {
id: Some("setup-key-choice".to_string()),
@@ -263,36 +280,71 @@ fn SetupPage() -> impl IntoView {
});
};
view! {
- <main id="app-setup" data-app-scroll class="min-h-[100dvh] w-full px-6 pt-10 pb-16">
+ <main
+ id="app-setup"
+ data-app-scroll
+ class="min-h-[100dvh] h-[100dvh] w-full flex flex-col"
+ >
{move || match setup_step.get() {
- RadrootsAppSetupStep::Intro => view! {
- <section id="app-setup-intro" class="flex flex-col w-full gap-6">
- <header class="flex flex-col gap-3">
- <p class="font-sans text-sm uppercase tracking-[0.14em] text-ly0-gl-label">
- "Radroots"
- </p>
- <h1 class="font-sans font-[600] text-3xl text-ly0-gl">
- "Welcome to Radroots"
- </h1>
- <p class="font-sans text-line_d_e text-ly0-gl-label">
- "Set up your key and preferences to get started."
- </p>
- </header>
- <div class="flex flex-col gap-3">
- <button
- type="button"
- class="button-submit rounded-touch px-6 py-3"
- on:click=advance_step
- >
- <span class="font-sans font-[600] text-ly2-gl">
- "Get started"
- </span>
- </button>
- </div>
- </section>
- }.into_any(),
+ RadrootsAppSetupStep::Intro => {
+ let navigate_home = navigate_home.clone();
+ view! {
+ <section
+ id="app-setup-intro"
+ 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">
+ <button
+ type="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"
+ </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>
+ </div>
+ </div>
+ <div class="z-10 absolute bottom-10 left-0 flex flex-col w-full justify-center items-center">
+ <button
+ type="button"
+ class="button-submit rounded-touch px-6 py-3"
+ on:click=advance_step
+ >
+ <span class="font-sans font-[600] text-ly2-gl">
+ "Get started"
+ </span>
+ </button>
+ </div>
+ </section>
+ }
+ .into_any()
+ },
RadrootsAppSetupStep::KeyChoice => view! {
- <section id="app-setup-key-choice" class="flex flex-col w-full gap-4">
+ <section
+ 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">
<p class="font-sans text-sm uppercase tracking-[0.14em] text-ly0-gl-label">
"Setup"
diff --git a/crates/ui-tokens/assets/themes/theme_os_daisyui.css b/crates/ui-tokens/assets/themes/theme_os_daisyui.css
@@ -0,0 +1,83 @@
+@plugin "daisyui/theme" {
+ name: "os_dark";
+ default: false;
+ prefersdark: true;
+ color-scheme: dark;
+ --color-ly0: hsl(0, 0%, 7%);
+ --color-ly0-w: hsl(0, 0%, 7%);
+ --color-ly0-a: hsl(240, 2%, 23%);
+ --color-ly0-edge: hsl(274, 4%, 11%);
+ --color-ly0-blur: hsl(0, 0%, 12%);
+ --color-ly0-gl: hsl(230, 3%, 56%);
+ --color-ly0-gl-a: hsl(230, 3%, 51%);
+ --color-ly0-gl-pl: hsl(30, 1%, 99%);
+ --color-ly0-gl-hl: hsl(210, 100%, 52%);
+ --color-ly0-gl-hl-a: hsl(210, 91%, 21%);
+ --color-ly0-gl-shade: hsl(240, 3%, 57%);
+ --color-ly0-gl-label: hsl(240, 3%, 55%);
+ --color-ly1: hsl(240, 4%, 11%);
+ --color-ly1-a: hsl(240, 2%, 23%);
+ --color-ly1-edge: hsl(240, 3%, 19%);
+ --color-ly1-err: hsl(0, 0%, 0%);
+ --color-ly1-focus: hsl(240, 4%, 20%);
+ --color-ly1-gl: hsl(30, 100%, 100%);
+ --color-ly1-gl-a: hsl(30, 1%, 90%);
+ --color-ly1-gl-d: hsl(240, 1%, 82%);
+ --color-ly1-gl-pl: hsl(30, 1%, 99%);
+ --color-ly1-gl-hl: hsl(210, 100%, 52%);
+ --color-ly1-gl-hl-a: hsl(210, 100%, 62%);
+ --color-ly1-gl-shade: hsl(230, 4%, 61%);
+ --color-ly1-gl-label: hsl(30, 1%, 99%);
+ --color-ly2: hsl(240, 2%, 18%);
+ --color-ly2-a: hsl(240, 3%, 15%);
+ --color-ly2-edge: hsl(240, 2%, 23%);
+ --color-ly2-gl: hsl(240, 3%, 73%);
+ --color-ly2-gl-a: hsl(230, 4%, 51%);
+ --color-ly2-gl-d: hsl(240, 3%, 63%);
+ --color-ly2-gl-pl: hsl(240, 2%, 40%);
+ --color-ly2-gl-hl: hsl(210, 100%, 52%);
+ --color-ly2-gl-hl-a: hsl(210, 100%, 42%);
+ --color-ly2-gl-shade: hsl(230, 4%, 61%);
+}
+
+@plugin "daisyui/theme" {
+ name: "os_light";
+ default: false;
+ prefersdark: false;
+ color-scheme: light;
+ --color-ly0: hsl(240, 24%, 96%);
+ --color-ly0-w: hsl(248, 17%, 98%);
+ --color-ly0-a: hsl(240, 6%, 83%);
+ --color-ly0-edge: hsl(0, 0%, 87%);
+ --color-ly0-blur: hsl(179, 7%, 96%);
+ --color-ly0-gl: hsl(240, 2%, 55%);
+ --color-ly0-gl-a: hsl(240, 2%, 60%);
+ --color-ly0-gl-pl: hsl(240, 2%, 78%);
+ --color-ly0-gl-hl: hsl(219, 92%, 59%);
+ --color-ly0-gl-hl-a: hsl(211, 100%, 40%);
+ --color-ly0-gl-shade: hsl(230, 3%, 54%);
+ --color-ly0-gl-label: hsl(240, 2%, 53%);
+ --color-ly1: hsl(0, 0%, 100%);
+ --color-ly1-a: hsl(240, 6%, 83%);
+ --color-ly1-edge: hsl(274, 4%, 90%);
+ --color-ly1-err: hsl(0, 0%, 0%);
+ --color-ly1-focus: hsl(240, 15%, 94%);
+ --color-ly1-gl: hsl(0, 0%, 10%);
+ --color-ly1-gl-a: hsl(0, 0%, 10%);
+ --color-ly1-gl-d: hsl(0, 0%, 20%);
+ --color-ly1-gl-pl: hsl(240, 2%, 78%);
+ --color-ly1-gl-hl: hsl(211, 100%, 50%);
+ --color-ly1-gl-hl-a: hsl(211, 100%, 40%);
+ --color-ly1-gl-shade: hsl(240, 2%, 55%);
+ --color-ly1-gl-label: hsl(240, 2%, 53%);
+ --color-ly2: hsl(240, 5%, 90%);
+ --color-ly2-a: hsl(240, 5%, 95%);
+ --color-ly2-edge: hsl(242, 2%, 88%);
+ --color-ly2-gl: hsl(240, 2%, 55%);
+ --color-ly2-gl-a: hsl(240, 2%, 45%);
+ --color-ly2-gl-d: hsl(240, 2%, 65%);
+ --color-ly2-gl-pl: hsl(240, 2%, 78%);
+ --color-ly2-gl-hl: hsl(211, 100%, 50%);
+ --color-ly2-gl-hl-a: hsl(211, 100%, 40%);
+ --color-ly2-gl-shade: hsl(240, 2%, 55%);
+}