Wizard polish: step headlines, gated Next, real summary, portal action bar
Mobile only (<=639px); desktop markup untouched. Bug fix: the Back/Next bar was fixed-positioned inside the screen entrance animation's transformed wrapper, so it painted under the content and jumped to the viewport bottom about a second in. It is now portalled to <body> and only fades in, pinned to the bottom from the first frame. The other fixed/sticky elements (tab bar, drawer, sheets, install sheet) were checked and live outside the animated wrappers. - The step name is now the large headline with a one-line description; the repeated page title is dropped on phones. Thin progress bar + 1/3. - Step 1: formats/limits hint, one big dashed Add photos or files area (plus icon), Camera and Paste text instead as secondaries, and a 'nothing added yet' placeholder that becomes the thumbnail/chip list. - Next is disabled until a photo, file or pasted text exists; step 3 can never be reached empty. - Step 1 shows Cancel (back to the Library) instead of a dead Back. - Step 3 is a real summary: thumbnail/chip strip plus Files, Difficulty, Activities and Folder rows, each with an Edit link to its step; Freeze is the single primary action. - Step 2 got a visible Difficulty label and a helper line under every group, defaults still preselected. - Bottom chrome: flat full-width Back/Next bar above the safe area, and the tab bar hides inside both wizards. The lower-right orb is hidden on phones so it can never glow behind the bar. - Make my homework matches: headline, hint, big add area, placeholder, Cancel, gated Next, flat bar. Verified: new wizard suite 80/80 (bar position sampled at 5 frames during the entrance from tab bar/drawer/empty state; 360 + 390, dark + light, EN + CS, reduced motion on/off; gating, summary Edit links, hidden tab bar and orb), declutter 116/116, regression 74/74, mobile 52/52, animation 8/8, i18n 25/25. Lint + build clean. SW freezmo-v22.
This commit is contained in:
parent
374752c59e
commit
e66141e9a0
9 changed files with 338 additions and 132 deletions
39
components/WizardActionBar.tsx
Normal file
39
components/WizardActionBar.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import { useEffect, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
/**
|
||||
* The wizard's Back / Next bar.
|
||||
*
|
||||
* Portalled to <body> on purpose: the page content animates its entrance with
|
||||
* a transform, and a transform on an ancestor turns it into the containing
|
||||
* block for `position: fixed` — the bar would briefly render inside the
|
||||
* content and then jump to the viewport bottom. Rendering it outside every
|
||||
* animated wrapper (and only fading it in, never transforming it) keeps it
|
||||
* pinned to the bottom edge from the first frame.
|
||||
*
|
||||
* Flat, full-width and above the safe area. The wizard pages hide the tab
|
||||
* bar, so this is the only bottom chrome while the flow is open.
|
||||
*/
|
||||
export function WizardActionBar({ children }: { children: React.ReactNode }) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const reduceMotion = useReducedMotion();
|
||||
|
||||
useEffect(() => setMounted(true), []);
|
||||
if (!mounted) return null;
|
||||
|
||||
return createPortal(
|
||||
<motion.div
|
||||
initial={reduceMotion ? false : { opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.2, ease: "easeOut" }}
|
||||
className="fixed inset-x-0 bottom-0 z-40 border-t border-[var(--hairline)] bg-[color-mix(in_oklab,var(--page)_92%,transparent)] px-4 pb-safe pt-2 backdrop-blur-lg sm:hidden"
|
||||
data-wizard-bar
|
||||
>
|
||||
<div className="mx-auto flex w-full max-w-3xl items-center gap-2">{children}</div>
|
||||
</motion.div>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue