Simplify application copy and improve landing page spacing
This commit is contained in:
@@ -255,7 +255,7 @@ describe("homepage", () => {
|
||||
|
||||
test("signed-out visitors can try progress without calling private habit APIs", async () => {
|
||||
await render("/");
|
||||
expect(container.querySelector("h1")?.textContent).toBe("Small steps.Lasting rhythm.");
|
||||
expect(container.querySelector("h1")?.textContent).toBe("Track your habits");
|
||||
expect(fetchMock.mock.calls.map(call => call[0])).toEqual(["/api/me"]);
|
||||
await act(async () => buttonNamed("Increase glasses of water").click());
|
||||
await settleDeferred();
|
||||
@@ -278,7 +278,7 @@ describe("homepage", () => {
|
||||
let resolve!: (response: Response) => void;
|
||||
fetchMock.mockImplementation(Object.assign(() => new Promise<Response>(done => { resolve = done; }), { preconnect: fetch.preconnect }));
|
||||
await render("/");
|
||||
expect(container.textContent).toContain("Getting things ready");
|
||||
expect(container.textContent).toContain("Loading account");
|
||||
expect(container.querySelector("#welcome-title")).toBeNull();
|
||||
await act(async () => resolve(new Response(null, { status: 401 })));
|
||||
expect(container.querySelector("#welcome-title")).not.toBeNull();
|
||||
@@ -298,7 +298,7 @@ describe("homepage", () => {
|
||||
const f = connectAccount();
|
||||
await render("/");
|
||||
expect(container.textContent).toContain("Welcome back, alice.");
|
||||
expect(container.textContent).toContain("Start with one habit.");
|
||||
expect(container.textContent).toContain("No habits yet.");
|
||||
await act(async () => buttonNamed("Add a habit +").click());
|
||||
await settleDeferred();
|
||||
const input = container.querySelector<HTMLInputElement>("form input")!;
|
||||
|
||||
@@ -60,7 +60,7 @@ export function ArchivedHabits({ date, revision, onChanged, onExpired, onClose }
|
||||
<div ref={archiveFocus} tabIndex={-1} aria-label="Archive status"><FormMessage>{notice}</FormMessage></div>
|
||||
<FormMessage error>{error}</FormMessage>
|
||||
{error && <Button variant="secondary" onClick={() => setAttempt(value => value + 1)}>Retry archive</Button>}
|
||||
{loading ? <p className="ds-supporting-copy" role="status">Loading archive…</p> : !habits.length && <div className="ds-empty-panel"><Archive size={24} aria-hidden="true" /><div><p className="type-ui-heading">No archived habits.</p><p className="ds-supporting-copy">When you put a habit aside, its history will be waiting here.</p></div></div>}
|
||||
{loading ? <p className="ds-supporting-copy" role="status">Loading archive…</p> : !habits.length && <div className="ds-empty-panel"><Archive size={24} aria-hidden="true" /><div><p className="type-ui-heading">No archived habits.</p></div></div>}
|
||||
<div className="ds-record-list">
|
||||
{habits.map(habit => <article key={habit.habitId} className="ds-record" aria-labelledby={`archive-title-${habit.habitId}`}>
|
||||
<header className="ds-record-heading">
|
||||
|
||||
@@ -40,7 +40,7 @@ export function CheckInEditor({ habitId, date, disabled, onSaved, onExpired, onB
|
||||
} finally { saving.current = false; setBusy(false); onBusyChange?.(false); }
|
||||
}
|
||||
return <div className="ds-form-stack ds-check-in-controls" aria-label={`Check-in for ${date}`}>
|
||||
<header className="ds-check-in-heading"><p className="ds-eyebrow">EDITING CHECK-IN</p><p className="type-ui-heading">{formatTrackingDate(date)}</p></header>
|
||||
<header className="ds-check-in-heading"><p className="type-ui-heading">{formatTrackingDate(date)}</p></header>
|
||||
<FormMessage error>{error}</FormMessage>
|
||||
{!day ? error ? <Button onClick={() => setAttempt(value => value + 1)}>Retry check-in</Button> : <p role="status">Loading check-in…</p>
|
||||
: day.future ? <p>Future check-ins cannot be edited.</p>
|
||||
|
||||
@@ -114,9 +114,6 @@ export function CreateHabit({
|
||||
}}
|
||||
>
|
||||
<div className="home-create-heading">
|
||||
<p className="ds-eyebrow">
|
||||
{editing ? "SHAPE YOUR RHYTHM" : "A LITTLE ROOM FOR SOMETHING GOOD"}
|
||||
</p>
|
||||
<Button
|
||||
variant="text"
|
||||
aria-label={editing ? "Close habit editor" : "Close new habit"}
|
||||
@@ -127,21 +124,9 @@ export function CreateHabit({
|
||||
</Button>
|
||||
</div>
|
||||
<h2 id="create-title">
|
||||
{editing ? (
|
||||
<>
|
||||
Edit your <em>habit.</em>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Make it <em>yours.</em>
|
||||
</>
|
||||
)}
|
||||
{editing ? "Edit habit" : "Create habit"}
|
||||
</h2>
|
||||
<p className="ds-muted">
|
||||
{editing
|
||||
? "Changes start today. Earlier targets and schedules stay as they were."
|
||||
: "Start small. You don’t need a perfect plan."}
|
||||
</p>
|
||||
{editing && <p className="type-small">Changes apply from today. Earlier history is kept.</p>}
|
||||
<form onSubmit={submit}>
|
||||
<fieldset disabled={busy} className="home-form-fields">
|
||||
<div className="ds-field">
|
||||
|
||||
@@ -47,7 +47,6 @@ export function DeleteHabit({
|
||||
if (!saving.current) onClose();
|
||||
}}
|
||||
>
|
||||
<p className="ds-eyebrow">MAKE A LITTLE ROOM</p>
|
||||
<h2 id="delete-title">
|
||||
Delete this <em>habit?</em>
|
||||
</h2>
|
||||
|
||||
@@ -9,13 +9,12 @@ export function InstallApp() {
|
||||
if (!enabled || installed) return null;
|
||||
return <>
|
||||
<Button variant="text" onClick={() => { if (installPrompt) void installApp(); else setHelp(true); }}>Install app</Button>
|
||||
{help && <Modal id="install-app" size="compact" title="Keep Mina Habits close." onClose={() => setHelp(false)}>
|
||||
{help && <Modal id="install-app" size="compact" title="Install app" onClose={() => setHelp(false)}>
|
||||
<div className="ds-form-stack">
|
||||
<p>Open Mina Habits from your home screen or dock.</p>
|
||||
<p><strong>iPhone or iPad:</strong> open the Share menu, then choose Add to Home Screen.</p>
|
||||
<p><strong>Mac Safari:</strong> choose File → Add to Dock.</p>
|
||||
<p><strong>Chrome or Edge:</strong> use the install icon in the address bar, or Install app in the browser menu.</p>
|
||||
<p className="ds-supporting-copy">Installation options depend on your browser. Your habits still need a connection to load and save.</p>
|
||||
<p className="ds-supporting-copy">An internet connection is required to load and save habits.</p>
|
||||
<div className="ds-actions"><Button onClick={() => setHelp(false)}>Got it</Button></div>
|
||||
</div>
|
||||
</Modal>}
|
||||
|
||||
@@ -91,7 +91,6 @@ export function ShareProgress({ user, today, revision, onClose }: { user: Public
|
||||
<div className={`ds-share-preview${thumbnail ? " ds-share-preview--thumbnail" : ""}`} aria-busy={!ready && !error}>
|
||||
{error ? <div className="ds-share-placeholder" role="alert"><ImageIcon size={28} aria-hidden="true" /><p>{error}</p>{ids.length > 0 && <Button variant="secondary" onClick={() => setRenderAttempt(n => n + 1)}>Retry preview</Button>}</div> : ready ? <img src={ready.url} alt={ready.alt} /> : <div className="ds-share-placeholder" role="status"><ImageIcon size={28} aria-hidden="true" /><p>Creating your card…</p></div>}
|
||||
</div>
|
||||
<p className="ds-share-caption">Your preview matches the shared image.</p>
|
||||
{ready?.avatarMissing && <p role="status" className="ds-share-caption">Your avatar couldn’t load. A neutral profile icon is used.</p>}
|
||||
</section>
|
||||
<div className="ds-share-controls">
|
||||
@@ -112,13 +111,13 @@ export function ShareProgress({ user, today, revision, onClose }: { user: Public
|
||||
</fieldset>
|
||||
</details>
|
||||
<div className="ds-share-destination"><span className="ds-share-destination-icon"><Hash size={20} aria-hidden="true" /></span><div><span className="ds-share-hint">Discord destination</span>
|
||||
{connectionError ? <div role="alert"><p>{connectionError}</p><Button variant="text" disabled={busy} onClick={() => setConnectionAttempt(n => n + 1)}>Retry connection</Button></div> : connection?.connected ? <><a href={connection.channelUrl} target="_blank" rel="noreferrer">{connection.name}</a><p className="ds-share-hint">Posted by the Mina Habits bot.</p></> : <p className="ds-share-hint" role="status">{connection?.message ?? "Loading channel…"}</p>}
|
||||
{connectionError ? <div role="alert"><p>{connectionError}</p><Button variant="text" disabled={busy} onClick={() => setConnectionAttempt(n => n + 1)}>Retry connection</Button></div> : connection?.connected ? <><a href={connection.channelUrl} target="_blank" rel="noreferrer">{connection.name}</a></> : <p className="ds-share-hint" role="status">{connection?.message ?? "Loading channel…"}</p>}
|
||||
</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<footer className="ds-share-footer">
|
||||
<div className="ds-share-footer-copy" aria-live="polite">
|
||||
{sendError ? <p role="alert" className="ds-share-error">{sendError}</p> : sent ? <p>{sent.status === "sent" ? <>Shared successfully. {sent.messageUrl && <a href={sent.messageUrl} target="_blank" rel="noreferrer">View in Discord ↗</a>}</> : <>Delivery unconfirmed. Check {connection?.channelUrl ? <a href={connection.channelUrl} target="_blank" rel="noreferrer">your channel</a> : "your channel"}; this attempt won’t be resent.</>}</p> : <p>{sending ? "Sending your card to Discord…" : error ? "Check your card settings." : ready ? "Ready to share." : "Preparing preview…"}<span>{sending ? "This may take a moment." : "Only the details in your preview are shared."}</span></p>}
|
||||
{sendError ? <p role="alert" className="ds-share-error">{sendError}</p> : sent ? <p>{sent.status === "sent" ? <>Shared successfully. {sent.messageUrl && <a href={sent.messageUrl} target="_blank" rel="noreferrer">View in Discord ↗</a>}</> : <>Delivery unconfirmed. Check {connection?.channelUrl ? <a href={connection.channelUrl} target="_blank" rel="noreferrer">your channel</a> : "your channel"}; this attempt won’t be resent.</>}</p> : <p>{sending ? "Sending your card to Discord…" : error ? "Check your card settings." : ready ? "Ready to share." : "Preparing preview…"}</p>}
|
||||
</div>
|
||||
<div className="ds-share-actions">
|
||||
{ready ? <a className="ds-button ds-button--secondary" href={ready.url} download={`mina-habits-progress-${from}-${to}.png`}><Download size={17} aria-hidden="true" />Download</a> : <Button variant="secondary" disabled><Download size={17} aria-hidden="true" />Download</Button>}
|
||||
|
||||
@@ -8,33 +8,30 @@ export function ContainerShowcase() {
|
||||
|
||||
return (
|
||||
<section className="ds-section ds-container-showcase" id="containers" aria-labelledby="containers-title">
|
||||
<SectionHeading number="CONTAINERS & LAYOUT" id="containers-title" title={<>A little structure. <em>Room to breathe.</em></>}>
|
||||
Group related content with a quiet surface. Keep open layouts for long lists and calendars.
|
||||
</SectionHeading>
|
||||
<SectionHeading id="containers-title" title="Containers & layout" />
|
||||
|
||||
<div className="ds-spec-heading ds-spec-heading--spaced">
|
||||
<h3>Two quiet surfaces</h3>
|
||||
<h3>Card surfaces</h3>
|
||||
<span className="ds-code">Card · soft / outlined</span>
|
||||
</div>
|
||||
<CardGrid>
|
||||
<Card headingLevel={4} eyebrow="SOFT SURFACE" heading="A gentle grouping.">
|
||||
<Card headingLevel={4} heading="Soft surface">
|
||||
<p>A neutral fill gathers related content without adding another divider. Use it for summaries, guidance, and a small set of actions.</p>
|
||||
</Card>
|
||||
<Card headingLevel={4} variant="outlined" eyebrow="OUTLINED SURFACE" heading="A clear boundary.">
|
||||
<Card headingLevel={4} variant="outlined" heading="Outlined surface">
|
||||
<p>A fine border defines a standalone group on white. Useful when a form or a focused task needs its own space.</p>
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
<div className="ds-spec-heading ds-spec-heading--spaced">
|
||||
<h3>A day, arranged simply</h3>
|
||||
<h3>Bento layout</h3>
|
||||
<span className="ds-code">CardGrid · bento</span>
|
||||
</div>
|
||||
<CardGrid layout="bento" aria-label="Interactive bento layout example">
|
||||
<Card
|
||||
headingLevel={4}
|
||||
span="featured"
|
||||
eyebrow="TODAY / YOUR OWN PACE"
|
||||
heading={<>Small things, <em>adding up.</em></>}
|
||||
heading="Today"
|
||||
>
|
||||
<div className="ds-card-summary" role="status">
|
||||
<p className="type-display">{Number(water === 8) + Number(read)}<span className="type-title ds-muted"> / 2</span></p>
|
||||
@@ -45,11 +42,11 @@ export function ContainerShowcase() {
|
||||
<Counter label="bento glasses of water" value={water} target={8} onChange={setWater} />
|
||||
<p className="type-small" role="status">{water === 8 ? "Complete for today" : `${8 - water} glasses to go`}</p>
|
||||
</Card>
|
||||
<Card headingLevel={4} eyebrow="A FEW PAGES" heading="Read a little" variant="outlined">
|
||||
<Card headingLevel={4} heading="Read a little" variant="outlined">
|
||||
<Checkbox label="Reading done" checked={read} onChange={(event) => setRead(event.target.checked)} />
|
||||
</Card>
|
||||
<Card headingLevel={4} span="wide" eyebrow="A GENTLE REMINDER" heading="There’s no catching up.">
|
||||
<p>Come back to today. Your next small step is enough.</p>
|
||||
<Card headingLevel={4} span="wide" heading="Demo">
|
||||
<p>Changes to these examples are not saved.</p>
|
||||
</Card>
|
||||
</CardGrid>
|
||||
<div className="ds-container-guidance">
|
||||
|
||||
@@ -407,18 +407,7 @@ export function EditingWorkbench({
|
||||
</div>
|
||||
<div className="ds-edit-layout">
|
||||
<aside className="ds-edit-context">
|
||||
<span className="ds-eyebrow">
|
||||
{tab === "Backfill progress"
|
||||
? "A DATE, NOT A RESET"
|
||||
: "MAKE IT YOURS"}
|
||||
</span>
|
||||
<h3>
|
||||
{tab === "Habit settings"
|
||||
? "Shape your rhythm."
|
||||
: tab === "Tasks"
|
||||
? "Small steps, clearly defined."
|
||||
: "A missed log is not a missed day."}
|
||||
</h3>
|
||||
<h3>{tab}</h3>
|
||||
<p>
|
||||
{tab === "Habit settings"
|
||||
? "Adjust a name, target, or schedule. Changes start today; your history stays as it was."
|
||||
|
||||
@@ -39,7 +39,6 @@ export function PageLayout({
|
||||
{children}
|
||||
<footer className="ds-footer">
|
||||
<span className="ds-wordmark">Mina Habits.</span>
|
||||
<span>A little, every day.</span>
|
||||
<InstallApp />
|
||||
<Button
|
||||
variant="text"
|
||||
|
||||
@@ -68,7 +68,6 @@ export function WelcomePanel({
|
||||
<h1 id="dashboard-title" className="type-display">
|
||||
Welcome back, <em>{name}.</em>
|
||||
</h1>
|
||||
<p className="ds-welcome-subtitle">Make time for today.</p>
|
||||
{children}
|
||||
</div>
|
||||
<div className="ds-welcome-calendar">
|
||||
@@ -82,7 +81,7 @@ export function WelcomePanel({
|
||||
<span className="ds-date-weekday">
|
||||
{day.toLocaleDateString("en", { weekday: "long", timeZone: "UTC" })}
|
||||
</span>
|
||||
<span className="ds-date-caption ds-eyebrow">TODAY, AT YOUR OWN PACE</span>
|
||||
<span className="ds-date-caption ds-eyebrow">TODAY</span>
|
||||
</time>
|
||||
) : (
|
||||
<p className="ds-muted" role="status">Your day is loading…</p>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="./assets/favicon.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="./assets/apple-touch-icon.png" />
|
||||
<title>Mina Habits</title>
|
||||
<meta name="description" content="A quiet place to track your habits and see your progress. Explore interactive habit calendars and connect with Discord with Mina Habits." />
|
||||
<meta name="description" content="Track habits, check in, and view your progress." />
|
||||
<script type="module" src="./frontend.tsx" async></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -36,7 +36,7 @@ export function DesignSystem() {
|
||||
<DesignSystemTabs tabs={[
|
||||
{ id: "foundations", label: "Foundations", content: (
|
||||
<section className="ds-section ds-split-section" id="foundations">
|
||||
<SectionHeading number="FOUNDATIONS" title="The essentials." />
|
||||
<SectionHeading title="Foundations" />
|
||||
<div className="ds-section-content">
|
||||
<div className="ds-spec-heading">
|
||||
<h3>Typography</h3>
|
||||
@@ -44,14 +44,14 @@ export function DesignSystem() {
|
||||
</div>
|
||||
<div className="ds-type-specimen">
|
||||
<span className="ds-type-display">
|
||||
Small steps.
|
||||
Regular type.
|
||||
<br />
|
||||
<em>Lasting rhythm.</em>
|
||||
<em>Italic type.</em>
|
||||
</span>
|
||||
<p>Instrument Serif · Regular & italic · 72/81px display, 48/54px on small screens</p>
|
||||
</div>
|
||||
<div className="ds-body-specimen">
|
||||
<span>Room to breathe. A clear next step.</span>
|
||||
<span>Body text and controls.</span>
|
||||
<p>System sans · 16/24px body & controls · 14/21px supporting text</p>
|
||||
</div>
|
||||
<ul className="ds-type-scale" aria-label="Shared typography scale">
|
||||
@@ -131,7 +131,7 @@ export function DesignSystem() {
|
||||
) },
|
||||
{ id: "components", label: "Components", content: (
|
||||
<section className="ds-section ds-split-section" id="components">
|
||||
<SectionHeading number="COMPONENTS" title="Only what matters." />
|
||||
<SectionHeading title="Components" />
|
||||
<div className="ds-section-content">
|
||||
<div className="ds-spec-heading">
|
||||
<h3>Actions</h3>
|
||||
@@ -180,7 +180,7 @@ export function DesignSystem() {
|
||||
</div>
|
||||
<div className="ds-component-row">
|
||||
<div>
|
||||
<h3>A little more</h3>
|
||||
<h3>Counter</h3>
|
||||
<span className="ds-code">Counter · editable total</span>
|
||||
</div>
|
||||
<Counter
|
||||
@@ -245,7 +245,7 @@ export function DesignSystem() {
|
||||
<section className="ds-section ds-split-section" id="calendar-states">
|
||||
<SectionHeading
|
||||
number="PROGRESS LANGUAGE"
|
||||
title="Every shade counts."
|
||||
title="Calendar states"
|
||||
/>
|
||||
<div className="ds-section-content">
|
||||
<div className="ds-spec-heading">
|
||||
|
||||
@@ -11,12 +11,10 @@ import { DiscordSignInButton } from "../components/DiscordSignInButton";
|
||||
import { PageLayout } from "../components/design-system/PageLayout";
|
||||
import {
|
||||
Button,
|
||||
ButtonLink,
|
||||
Checkbox,
|
||||
Counter,
|
||||
SectionHeading,
|
||||
} from "../components/design-system/primitives";
|
||||
import { Card, CardGrid } from "../components/design-system/Card";
|
||||
import { HabitChart } from "../components/design-system/HabitChart";
|
||||
import { HABIT_COLORS } from "../components/design-system/calendar-model";
|
||||
const HabitForm = deferred(() => import("../components/HabitForm").then(module => module.HabitForm));
|
||||
@@ -56,17 +54,17 @@ export function Home() {
|
||||
)}
|
||||
{loading ? (
|
||||
<section className="ds-section" aria-busy="true">
|
||||
<p role="status">Getting things ready…</p>
|
||||
<p role="status">Loading account…</p>
|
||||
</section>
|
||||
) : accountError ? (
|
||||
<section className="ds-section">
|
||||
<h1 className="type-section">{disconnected ? "A moment to reconnect." : "Let’s try that again."}</h1>
|
||||
<Card headingLevel={2} heading={disconnected ? "Your habits will be here." : "Account unavailable"}>
|
||||
<p>{disconnected ? "We can’t connect to Mina Habits. Check your internet connection to load your account and check in. We’ll try again automatically." : "Your account couldn’t be loaded."}</p>
|
||||
<h1 className="type-section">{disconnected ? "Connection unavailable" : "Account unavailable"}</h1>
|
||||
<div className="ds-form-stack">
|
||||
<p>{disconnected ? "Check your connection. We’ll retry automatically." : "Your account couldn’t be loaded."}</p>
|
||||
<div className="ds-actions">
|
||||
<Button onClick={retry}>Try again</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
) : user ? (
|
||||
<CalendarCacheProvider key={`${user.id}:${user.timezone}`}><Dashboard user={user} onExpired={retry} /></CalendarCacheProvider>
|
||||
@@ -83,39 +81,17 @@ function Welcome({ onSignIn }: { onSignIn: () => void }) {
|
||||
const [read, setRead] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<section className="ds-section ds-split-section" aria-labelledby="welcome-title">
|
||||
<div className="ds-section-heading">
|
||||
<p className="ds-eyebrow">A LITTLE, EVERY DAY</p>
|
||||
<h1 id="welcome-title" className="type-section">
|
||||
Small steps.
|
||||
<br />
|
||||
<em>Lasting rhythm.</em>
|
||||
</h1>
|
||||
</div>
|
||||
<Card headingLevel={2} heading="Make room for what matters.">
|
||||
<p>
|
||||
Check in, count a little more, or work through a few tasks. See your progress grow, one
|
||||
day at a time.
|
||||
</p>
|
||||
<section className="ds-section ds-landing-intro" aria-labelledby="welcome-title">
|
||||
<h1 id="welcome-title" className="type-section">Track your habits</h1>
|
||||
<p className="type-small">Sign in to save your habits and progress.</p>
|
||||
<div className="ds-actions">
|
||||
<DiscordSignInButton disabled={!online} onClick={onSignIn} />
|
||||
<ButtonLink href="#try-it">Try it below ↓</ButtonLink>
|
||||
</div>
|
||||
<p className="type-small">Sign in with your Discord account to save your habits.</p>
|
||||
</Card>
|
||||
</section>
|
||||
<section className="ds-section" id="try-it" aria-labelledby="try-it-title">
|
||||
<div className="ds-section-top">
|
||||
<SectionHeading
|
||||
number="TRY A CHECK-IN"
|
||||
id="try-it-title"
|
||||
title={
|
||||
<>
|
||||
A little progress. <em>Made visible.</em>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<span className="ds-demo-note">Example · September 4, 2026 · not saved</span>
|
||||
<SectionHeading id="try-it-title" title="Try a check-in" />
|
||||
<span className="ds-demo-note">Demo · changes are not saved</span>
|
||||
</div>
|
||||
<div className="ds-habit-chart-grid">
|
||||
<HabitChart
|
||||
@@ -146,22 +122,6 @@ function Welcome({ onSignIn }: { onSignIn: () => void }) {
|
||||
</HabitChart>
|
||||
</div>
|
||||
</section>
|
||||
<section className="ds-section" aria-label="Your own rhythm">
|
||||
<CardGrid>
|
||||
<Card heading="Your habits. Your pace." eyebrow="MAKE IT FIT">
|
||||
<p>
|
||||
Pick a check-in, a count target, or a task list. Repeat daily, on selected weekdays,
|
||||
or at your own interval.
|
||||
</p>
|
||||
</Card>
|
||||
<Card heading="See the days add up." eyebrow="KEEP PERSPECTIVE" variant="outlined">
|
||||
<p>
|
||||
Explore your calendar to see the progress behind each square. Days off stay distinct
|
||||
from missed days.
|
||||
</p>
|
||||
</Card>
|
||||
</CardGrid>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -374,16 +334,13 @@ function Dashboard({ user, onExpired }: { user: PublicUser; onExpired: () => voi
|
||||
</span>
|
||||
<span>
|
||||
habits complete today
|
||||
{today.completed === today.due && (
|
||||
<span className="ds-welcome-complete">A little, all done.</span>
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<p>
|
||||
{today.habits.length
|
||||
? "Nothing scheduled today. Enjoy a little breathing room."
|
||||
: "Start with one habit. Your first small step starts here."}
|
||||
? "Nothing scheduled today."
|
||||
: "No habits yet."}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -457,18 +414,14 @@ function Dashboard({ user, onExpired }: { user: PublicUser; onExpired: () => voi
|
||||
<div className="ds-section-top">
|
||||
<SectionHeading
|
||||
id="habits-title"
|
||||
title={
|
||||
<>
|
||||
Today, <em>at your pace.</em>
|
||||
</>
|
||||
}
|
||||
title="Habits"
|
||||
/>
|
||||
<div className="ds-action-row ds-habit-filters" role="group" aria-label="Show habits">
|
||||
<Button variant={habitView === "today" ? "primary" : "text"} aria-pressed={habitView === "today"} onClick={() => setHabitView("today")}>Today · {today.due}</Button>
|
||||
<Button variant={habitView === "all" ? "primary" : "text"} aria-pressed={habitView === "all"} onClick={() => setHabitView("all")}>All habits · {today.habits.length}</Button>
|
||||
</div>
|
||||
</div>
|
||||
{habitView === "today" && today.habits.length > 0 && today.due === 0 && <p className="ds-supporting-copy">You’re all set for today. Choose All habits to view or edit your routines.</p>}
|
||||
{habitView === "today" && today.habits.length > 0 && today.due === 0 && <p className="ds-supporting-copy">Choose All habits to view or edit your routines.</p>}
|
||||
<div className="ds-habit-chart-grid ds-habit-list">
|
||||
{today.habits.filter(habit => habitView === "all" || habit.due).map((habit) => (
|
||||
<SavedHabit
|
||||
|
||||
@@ -304,6 +304,13 @@
|
||||
padding: 42px 0 56px;
|
||||
border-top: 1px solid var(--ds-rule);
|
||||
}
|
||||
.ds-landing-intro > p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.ds-landing-intro > .ds-actions {
|
||||
margin-top: 24px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.ds-section-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
Reference in New Issue
Block a user