rebuild theme system and restyle ui
flatten themes to 3 colors without categories, rewrite css in vanilla layers, simplify onboarding flow, tighten greeting punctuation, adjust viewport meta
This commit is contained in:
@@ -15,8 +15,8 @@ export function DailyLimitsCard({ limits, check, onOpenSettings }: DailyLimitsCa
|
||||
<section className="limits-card glass-panel p-5 sm:p-6">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<p className="text-sm font-medium uppercase tracking-[0.18em] text-cyan-100">Daily limits</p>
|
||||
<p className="mt-2 max-w-xl text-sm leading-6 text-slate-400">
|
||||
<p className="section-kicker">Daily limits</p>
|
||||
<p className="section-meta mt-2 max-w-xl leading-6">
|
||||
Set how many cans you want per day, when to stop, and a spend cap. Limits are optional and stored on your
|
||||
account.
|
||||
</p>
|
||||
@@ -37,7 +37,7 @@ export function DailyLimitsCard({ limits, check, onOpenSettings }: DailyLimitsCa
|
||||
return (
|
||||
<section className="limits-card glass-panel p-5 sm:p-6">
|
||||
<div className="mb-4 flex flex-wrap items-center justify-between gap-2">
|
||||
<p className="text-sm font-medium uppercase tracking-[0.18em] text-cyan-100">Daily limits</p>
|
||||
<p className="section-kicker">Daily limits</p>
|
||||
<button className="list-button !min-h-9 !px-3 !py-1.5 text-xs" type="button" onClick={onOpenSettings}>
|
||||
<Settings2 size={14} aria-hidden="true" />
|
||||
Edit
|
||||
|
||||
@@ -56,7 +56,7 @@ export function LimitsSettingsForm({ limits, check, saving, onSave }: LimitsSett
|
||||
<form className="grid gap-4" onSubmit={submit}>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<label className="grid gap-2 text-sm">
|
||||
<span className="font-medium text-slate-300">Cans per day</span>
|
||||
<span className="font-medium text-slate-700">Cans per day</span>
|
||||
<input
|
||||
className="field-input"
|
||||
type="number"
|
||||
@@ -70,7 +70,7 @@ export function LimitsSettingsForm({ limits, check, saving, onSave }: LimitsSett
|
||||
</label>
|
||||
|
||||
<label className="grid gap-2 text-sm">
|
||||
<span className="font-medium text-slate-300">Spend per day (£)</span>
|
||||
<span className="font-medium text-slate-700">Spend per day (£)</span>
|
||||
<input
|
||||
className="field-input"
|
||||
type="number"
|
||||
@@ -85,7 +85,7 @@ export function LimitsSettingsForm({ limits, check, saving, onSave }: LimitsSett
|
||||
</div>
|
||||
|
||||
<label className="grid gap-2 text-sm sm:max-w-xs">
|
||||
<span className="font-medium text-slate-300">Stop drinking by</span>
|
||||
<span className="font-medium text-slate-700">Stop drinking by</span>
|
||||
<input
|
||||
className="field-input"
|
||||
type="time"
|
||||
@@ -96,7 +96,7 @@ export function LimitsSettingsForm({ limits, check, saving, onSave }: LimitsSett
|
||||
</label>
|
||||
|
||||
{previewParts.length ? (
|
||||
<p className="rounded-lg border border-white/10 bg-white/[0.05] px-3 py-2 text-sm text-slate-300">
|
||||
<p className="rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-700">
|
||||
Today so far: {previewParts.join(" · ")}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { ArrowRight, Check, ChevronLeft } from "lucide-react";
|
||||
import { APP_THEMES, THEME_CATEGORIES, type ThemeCategory } from "../data/themes";
|
||||
import { APP_THEMES } from "../data/themes";
|
||||
import { currency } from "../lib/metrics";
|
||||
import type { UserLimits } from "../types";
|
||||
|
||||
@@ -33,12 +33,6 @@ export function OnboardingScreen({
|
||||
const [dailySpendLimit, setDailySpendLimit] = useState<number | "none">(3.5);
|
||||
const [stopTime, setStopTime] = useState<string | "none">("18:00");
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [activeCategory, setActiveCategory] = useState<ThemeCategory>("flavour");
|
||||
|
||||
const visibleThemes = useMemo(() => {
|
||||
return APP_THEMES.filter((theme) => theme.category === activeCategory);
|
||||
}, [activeCategory]);
|
||||
|
||||
const activeTheme = useMemo(() => {
|
||||
return APP_THEMES.find((theme) => theme.id === activeThemeId) ?? APP_THEMES[0];
|
||||
}, [activeThemeId]);
|
||||
@@ -56,7 +50,7 @@ export function OnboardingScreen({
|
||||
await onSave(limits, activeThemeId);
|
||||
onClose();
|
||||
} catch (err) {
|
||||
console.error("Failed to save onboarding preferences", err);
|
||||
console.error("setup save failed", err);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -117,7 +111,7 @@ export function OnboardingScreen({
|
||||
className="pointer-events-none absolute inset-0 opacity-60"
|
||||
style={{
|
||||
background:
|
||||
"radial-gradient(circle at 76% 20%, color-mix(in srgb, var(--primary-container) 62%, transparent) 0 22%, transparent 44%), radial-gradient(circle at 12% 84%, color-mix(in srgb, var(--tertiary-container) 48%, transparent) 0 18%, transparent 42%)",
|
||||
"linear-gradient(180deg, color-mix(in srgb, var(--primary-container) 24%, transparent), transparent 36%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -127,22 +121,22 @@ export function OnboardingScreen({
|
||||
<div className="h-full rounded-full bg-[var(--primary)] transition-all duration-500" style={{ width: progress }} />
|
||||
</div>
|
||||
<p className="text-xs font-normal uppercase tracking-[0.18em] text-[var(--muted)]">
|
||||
Question {step} of {STEP_COUNT}
|
||||
step {step} of {STEP_COUNT}
|
||||
</p>
|
||||
</div>
|
||||
<p className="hidden text-xs font-normal text-[var(--muted)] sm:block">Red Bull Intake Tracker</p>
|
||||
<p className="hidden text-xs font-normal text-[var(--muted)] sm:block">Red Bull tracker</p>
|
||||
</header>
|
||||
|
||||
<main className="relative z-10 mx-auto flex w-full max-w-3xl flex-1 flex-col justify-center py-10 sm:py-16">
|
||||
{step === 1 && (
|
||||
<section className="grid gap-9">
|
||||
<div className="grid gap-5">
|
||||
<p className="text-sm font-normal text-[var(--primary)]">Energy setup</p>
|
||||
<p className="text-sm font-normal text-[var(--primary)]">setup</p>
|
||||
<h1 className="max-w-2xl text-5xl font-normal leading-[0.95] tracking-[-0.055em] sm:text-7xl">
|
||||
Hey {userName || "there"}. Set your baseline.
|
||||
</h1>
|
||||
<p className="max-w-xl text-lg font-normal leading-8 text-[var(--muted)]">
|
||||
Six quick screens. Pick a theme, then set light guardrails for cans, spend, and late caffeine.
|
||||
Pick a theme, then set optional limits for cans, spend, and time.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
@@ -160,35 +154,14 @@ export function OnboardingScreen({
|
||||
{step === 2 && (
|
||||
<section className="grid gap-8">
|
||||
<div className="grid gap-4">
|
||||
<p className="text-sm font-normal text-[var(--primary)]">1. Visual style</p>
|
||||
<p className="text-sm font-normal text-[var(--primary)]">theme</p>
|
||||
<h2 className="max-w-2xl text-4xl font-normal leading-tight tracking-[-0.04em] sm:text-6xl">
|
||||
Choose the mood you want to see every day.
|
||||
Choose the app color.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{THEME_CATEGORIES.map((cat) => {
|
||||
const isActive = activeCategory === cat.id;
|
||||
return (
|
||||
<button
|
||||
key={cat.id}
|
||||
type="button"
|
||||
onClick={() => setActiveCategory(cat.id)}
|
||||
className="rounded-full border px-4 py-2 text-sm font-normal transition"
|
||||
style={{
|
||||
background: isActive ? "var(--primary-container)" : "var(--surface-container-lowest)",
|
||||
borderColor: isActive ? "var(--primary)" : "var(--outline-variant)",
|
||||
color: isActive ? "var(--on-primary-container)" : "var(--muted)",
|
||||
}}
|
||||
>
|
||||
{cat.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="grid max-h-[48vh] gap-2 overflow-y-auto pr-1 sm:grid-cols-2">
|
||||
{visibleThemes.map((theme) => {
|
||||
{APP_THEMES.map((theme) => {
|
||||
const isActive = activeThemeId === theme.id;
|
||||
return (
|
||||
<button
|
||||
@@ -227,12 +200,12 @@ export function OnboardingScreen({
|
||||
{step === 3 && (
|
||||
<section className="grid gap-9">
|
||||
<div className="grid gap-4">
|
||||
<p className="text-sm font-normal text-[var(--primary)]">2. Daily cans</p>
|
||||
<p className="text-sm font-normal text-[var(--primary)]">daily cans</p>
|
||||
<h2 className="max-w-2xl text-4xl font-normal leading-tight tracking-[-0.04em] sm:text-6xl">
|
||||
What is your daily can ceiling?
|
||||
</h2>
|
||||
<p className="max-w-lg text-base leading-7 text-[var(--muted)]">
|
||||
App warns before logging past this number. You can change it later.
|
||||
The app warns before saving an entry over this number. You can change it later.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -303,12 +276,12 @@ export function OnboardingScreen({
|
||||
{step === 4 && (
|
||||
<section className="grid gap-9">
|
||||
<div className="grid gap-4">
|
||||
<p className="text-sm font-normal text-[var(--primary)]">3. Daily spend</p>
|
||||
<p className="text-sm font-normal text-[var(--primary)]">daily spend</p>
|
||||
<h2 className="max-w-2xl text-4xl font-normal leading-tight tracking-[-0.04em] sm:text-6xl">
|
||||
Set a daily spend line.
|
||||
</h2>
|
||||
<p className="max-w-lg text-base leading-7 text-[var(--muted)]">
|
||||
Useful for catching small purchases before they stack up.
|
||||
Useful if you want a spending line for the day.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -379,12 +352,12 @@ export function OnboardingScreen({
|
||||
{step === 5 && (
|
||||
<section className="grid gap-8">
|
||||
<div className="grid gap-4">
|
||||
<p className="text-sm font-normal text-[var(--primary)]">4. Caffeine curfew</p>
|
||||
<p className="text-sm font-normal text-[var(--primary)]">time limit</p>
|
||||
<h2 className="max-w-2xl text-4xl font-normal leading-tight tracking-[-0.04em] sm:text-6xl">
|
||||
When should late caffeine stop?
|
||||
When should the app warn you?
|
||||
</h2>
|
||||
<p className="max-w-lg text-base leading-7 text-[var(--muted)]">
|
||||
Choose when the app should warn you that sleep may take the hit.
|
||||
Pick a time. The app will warn when an entry is later than this.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -427,7 +400,7 @@ export function OnboardingScreen({
|
||||
{step === 6 && (
|
||||
<section className="grid gap-8">
|
||||
<div className="grid gap-4">
|
||||
<p className="text-sm font-normal text-[var(--primary)]">Ready</p>
|
||||
<p className="text-sm font-normal text-[var(--primary)]">done</p>
|
||||
<h2 className="max-w-2xl text-4xl font-normal leading-tight tracking-[-0.04em] sm:text-6xl">
|
||||
This is your tracking profile.
|
||||
</h2>
|
||||
@@ -487,7 +460,7 @@ export function OnboardingScreen({
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
<p className="text-xs font-normal text-[var(--muted)]">Minimal setup. Editable later.</p>
|
||||
<p className="text-xs font-normal text-[var(--muted)]">you can edit this later.</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user