From f82c31392d5d402727f324e4b589de6abe93aea3 Mon Sep 17 00:00:00 2001 From: Timo Boomers Date: Fri, 18 Jul 2025 13:43:31 +0200 Subject: [PATCH] Added mobile navigation dock --- assets/css/main.css | 89 ++++++++++++++++++++++++++++++++++++++++++ src/icons.rs | 33 ++++++++++++++++ src/layouts/desktop.rs | 33 ++++++++++++++-- 3 files changed, 152 insertions(+), 3 deletions(-) diff --git a/assets/css/main.css b/assets/css/main.css index 9a99a4f..8f84b5e 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -375,6 +375,74 @@ } } } + .dock { + position: fixed; + right: calc(0.25rem * 0); + bottom: calc(0.25rem * 0); + left: calc(0.25rem * 0); + z-index: 1; + display: flex; + width: 100%; + flex-direction: row; + align-items: center; + justify-content: space-around; + background-color: var(--color-base-100); + padding: calc(0.25rem * 2); + color: currentColor; + border-top: 0.5px solid var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + border-top: 0.5px solid color-mix(in oklab, var(--color-base-content) 5%, #0000); + } + height: 4rem; + height: calc(4rem + env(safe-area-inset-bottom)); + padding-bottom: env(safe-area-inset-bottom); + > * { + position: relative; + margin-bottom: calc(0.25rem * 2); + display: flex; + height: 100%; + max-width: calc(0.25rem * 32); + flex-shrink: 1; + flex-basis: 100%; + cursor: pointer; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 1px; + border-radius: var(--radius-box); + background-color: transparent; + transition: opacity 0.2s ease-out; + @media (hover: hover) { + &:hover { + opacity: 80%; + } + } + &[aria-disabled="true"], &[disabled] { + &, &:hover { + pointer-events: none; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + opacity: 100%; + } + } + .dock-label { + font-size: 0.6875rem; + } + &:after { + content: ""; + position: absolute; + height: calc(0.25rem * 1); + width: calc(0.25rem * 6); + border-radius: calc(infinity * 1px); + background-color: transparent; + bottom: 0.2rem; + border-top: 3px solid transparent; + transition: background-color 0.1s ease-out, text-color 0.1s ease-out, width 0.1s ease-out; + } + } + } .btn { :where(&) { width: unset; @@ -966,6 +1034,9 @@ .mt-5 { margin-top: calc(var(--spacing) * 5); } + .mt-auto { + margin-top: auto; + } .mr-1 { margin-right: calc(var(--spacing) * 1); } @@ -1025,6 +1096,14 @@ width: var(--size); height: var(--size); } + .size-5 { + width: calc(var(--spacing) * 5); + height: calc(var(--spacing) * 5); + } + .size-\[1\.5em\] { + width: 1.5em; + height: 1.5em; + } .size-\[1\.6em\] { width: 1.6em; height: 1.6em; @@ -1218,6 +1297,11 @@ } } } + .max-md\:hidden { + @media (width < 48rem) { + display: none; + } + } .sm\:modal-middle { @media (width >= 40rem) { place-items: center; @@ -1235,6 +1319,11 @@ } } } + .md\:hidden { + @media (width >= 48rem) { + display: none; + } + } } h2 { font-size: var(--text-xl); diff --git a/src/icons.rs b/src/icons.rs index 59729fc..02505f8 100644 --- a/src/icons.rs +++ b/src/icons.rs @@ -12,3 +12,36 @@ pub fn arrow_left() -> Markup { } } } + +pub fn house() -> Markup { + html! { + svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 256 256" { + path + fill="currentColor" + d="m219.31 108.68l-80-80a16 16 0 0 0-22.62 0l-80 80A15.87 15.87 0 0 0 32 120v96a8 8 0 0 0 8 8h64a8 8 0 0 0 8-8v-56h32v56a8 8 0 0 0 8 8h64a8 8 0 0 0 8-8v-96a15.87 15.87 0 0 0-4.69-11.32M208 208h-48v-56a8 8 0 0 0-8-8h-48a8 8 0 0 0-8 8v56H48v-88l80-80l80 80Z" + {} + } + } +} + +pub fn barbell() -> Markup { + html! { + svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 256 256" { + path + fill="currentColor" + d="M248 120h-8V88a16 16 0 0 0-16-16h-16v-8a16 16 0 0 0-16-16h-24a16 16 0 0 0-16 16v56h-48V64a16 16 0 0 0-16-16H64a16 16 0 0 0-16 16v8H32a16 16 0 0 0-16 16v32H8a8 8 0 0 0 0 16h8v32a16 16 0 0 0 16 16h16v8a16 16 0 0 0 16 16h24a16 16 0 0 0 16-16v-56h48v56a16 16 0 0 0 16 16h24a16 16 0 0 0 16-16v-8h16a16 16 0 0 0 16-16v-32h8a8 8 0 0 0 0-16M32 168V88h16v80Zm56 24H64V64h24zm104 0h-24V64h24zm32-24h-16V88h16Z" + {} + } + } +} + +pub fn person_simple_run() -> Markup { + html! { + svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 256 256" { + path + fill="currentColor" + d="M152 88a32 32 0 1 0-32-32a32 32 0 0 0 32 32m0-48a16 16 0 1 1-16 16a16 16 0 0 1 16-16m67.31 100.68c-.61.28-7.49 3.28-19.67 3.28c-13.85 0-34.55-3.88-60.69-20a169.3 169.3 0 0 1-15.41 32.34a104.3 104.3 0 0 1 31.31 15.81C173.92 186.65 184 207.35 184 232a8 8 0 0 1-16 0c0-41.7-34.69-56.71-54.14-61.85c-.55.7-1.12 1.41-1.69 2.1c-19.64 23.8-44.25 36.18-71.63 36.18a92 92 0 0 1-9.34-.43a8 8 0 0 1 1.6-16c25.92 2.58 48.47-7.49 67-30c12.49-15.14 21-33.61 25.25-47c-38.92-22.65-63.78-3.37-64.05-3.16a8 8 0 1 1-10-12.48c1.5-1.2 37.22-29 89.51 6.57c45.47 30.91 71.93 20.31 72.18 20.19a8 8 0 1 1 6.63 14.56Z" + {} + } + } +} diff --git a/src/layouts/desktop.rs b/src/layouts/desktop.rs index 8df6ffc..ba645b4 100644 --- a/src/layouts/desktop.rs +++ b/src/layouts/desktop.rs @@ -7,11 +7,16 @@ use super::empty; pub fn desktop_minimal(content: Markup, name: &str) -> Markup { let content = html! { div class="w-full h-screen flex" { - div class="w-56" { + div class="max-md:hidden w-56" { (sidebar()) } - div class="w-full h-full overflow-y-auto" { - (content) + div class="w-full h-full flex flex-col" { + div class="overflow-y-auto" { + (content) + } + div class="md:hidden mt-auto" { + (dock()) + } } } }; @@ -40,12 +45,15 @@ fn sidebar() -> Markup { h2 { "Timo's Workouts" } } a href="/" { + div class="size-5" { (icons::house()) } "Overview" } a href="/workouts" { + div class="size-5" { (icons::barbell()) } "Workouts" } a href="/exercises" { + div class="size-5" { (icons::person_simple_run()) } "Exercises" } } @@ -53,6 +61,25 @@ fn sidebar() -> Markup { } } +fn dock() -> Markup { + html! { + div class="dock" { + a href="/" { + div class="size-[1.5em]" { (icons::house()) } + span class="dock-label" { "Overview" } + } + a href="/workouts" { + div class="size-[1.5em]" { (icons::barbell()) } + span class="dock-label" { "Workouts" } + } + a href="/exercises" { + div class="size-[1.5em]" { (icons::person_simple_run()) } + span class="dock-label" { "Exercises" } + } + } + } +} + fn header(title: &str, back_button: bool) -> Markup { html! { div class="flex items-center mb-5" {