Skip to content

feat(ux): apply tokens to 5 deferred screens + CompletionBloom#17

Open
Outtsett wants to merge 8 commits intodesign/research-grounded-systemfrom
design/ux-upgrade-screens
Open

feat(ux): apply tokens to 5 deferred screens + CompletionBloom#17
Outtsett wants to merge 8 commits intodesign/research-grounded-systemfrom
design/ux-upgrade-screens

Conversation

@Outtsett
Copy link
Copy Markdown
Owner

@Outtsett Outtsett commented May 4, 2026

Summary

Continues the design system from PR #16 by applying context.tokens to the 5 deferred screens (Onboarding, AddHabit, Analytics, Premium, Settings), plus a new radial-bloom micro-interaction on habit complete.

Stacked on PR #16 (design/research-grounded-system). Merge #16 first; this rebases cleanly onto main after.

Per-screen highlights

Screen Notable change
Onboarding Per-page accent color (sky for welcome/notifications, forest for identity/health). 140dp tinted halo behind hero icon — visible Lichtenfeld brief-glimpse priming on the identity page. Page-dot pill stretches on active.
AddHabit Identity field wrapped in a forest-tinted card with vote icon (the soul of the app). Swatches drawn from research-grounded ramps. Animated 36->44dp swatch select with shadow bloom. Mono digits on time + skip-tolerance. Slider active color = success.
Analytics Three mono-digit hero stat tiles (success-rate / attempts / high-risk count). Heatmap wrapped in surfaceVariant card; cells bumped 14->16dp. IF-THEN sheet gets a forest-tinted header card with mono percentage + drag handle. Amber high-risk windows card with mono % per row. Sky upsell with bolt icon, quotes Gollwitzer d=0.65.
Premium Hero $6.99 in 56pt JetBrains Mono with sky->forest gradient bg. Sticky bottom CTA so the FilledButton is always reachable. Per-benefit accent (primary/warning/success/danger by domain) with 40dp icon tile. Forest "Premium unlocked" banner when entitled.
Settings Section headers uppercase + letter-spacing 1.2 + lead icon (palette/bell/heart/brain/badge/storage/info). Theme rows render a real 44x28dp two-color preview pill of the actual ThemeData. IF-THEN adherence % shows traffic-light color (success >= 70, warning >= 40, danger). Subscription status as token-tinted chip. Crimson-bordered danger zone separates 'Wipe all data'. About section uses mono digits.

New: CompletionBloomOverlay

lib/widgets/completion_bloom.dart — one-shot OverlayEntry that paints a radial gradient at the tapped card's global center on habit complete. 400ms easeOutCubic, radius 12->332dp, alpha 0.30->0. Color = tokens.identityDot (forest.500 light / forest.400 dark) so it matches the identity-vote dot already on the voted line. Self-cleaning entry, IgnorePointer guards rapid double-taps.

Token system polish

context.tokens now falls back to SemanticColors.light when the SemanticColorsExt theme extension isn't registered — keeps widget tests that pump a bare MaterialApp (without our AppTheme.light()) from blowing up.

Test plan

  • flutter analyze --fatal-infos — clean
  • flutter test62 pass / 2 skip (was 62/2 on PR feat(design): research-grounded token system + flagship HomeScreen #16; one existing test updated to address fields by index since the AddHabit identity field's labelText moved to a heading row)
  • flutter build apk --debug — Built app-debug.apk
  • Manual: complete a habit on HomeScreen, confirm forest bloom radiates from card center
  • Manual: open AddHabit, confirm forest-tinted identity card, animated swatches
  • Manual: open Analytics with seeded skip data, confirm hero stat tiles + amber high-risk card
  • Manual: open Premium, confirm $6.99 mono hero + sticky bottom CTA
  • Manual: open Settings, confirm theme swatch previews + crimson danger zone

Files

Status Path
modified lib/screens/onboarding_screen.dart
modified lib/screens/add_habit_screen.dart
modified lib/screens/analytics_screen.dart
modified lib/screens/premium_screen.dart
modified lib/screens/settings_screen.dart
modified lib/screens/home_screen.dart (CompletionBloom integration)
new lib/widgets/completion_bloom.dart
modified lib/design/tokens.dart (graceful fallback)
modified test/add_habit_screen_test.dart (resilient field finder)

🤖 Generated with Claude Code

Outtsett and others added 8 commits May 4, 2026 13:23
- Per-page accent: sky for welcome/notifications, forest for identity/health
- Tinted halo (140dp circle) behind hero icon — visible Lichtenfeld 2012
  brief-glimpse priming on the identity page
- AnimatedPageDot: active dot stretches to 24dp pill (was 12dp circle)
- All paddings tokenized via Spacing.s*
- Page transition curve unified with Motion.bloomCurve

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Identity field wrapped in forest-tinted card with vote icon (Clear 2018
  identity-based change + Lichtenfeld 2012 priming at decision point)
- Swatches drawn from research-grounded design ramps (sky/forest/amber/
  crimson/slate.700) plus 3 extended hues (violet/pink/teal.700) — all
  audited at 4.5:1+ legibility
- Animated swatch selection (36->44dp on select with shadow bloom)
- Animated icon tile selection (forest-tinted bg + primary border)
- Mono digits on time-of-day display + skip-tolerance count + slider
  active color = success (green at the safety-margin decision)
- 2-min version helper text quotes Atomic Habits explicitly
- All paddings tokenized via Spacing.s*

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…et polish

- Three mono-digit stat tiles above heatmap: success-rate (forest),
  attempts (sky primary), high-risk count (amber when >0)
- Heatmap wrapped in surfaceVariant card; cells bumped 14->16dp for
  hero-element treatment
- IF-THEN bottom sheet:
  * forest-tinted header card with psychology icon (Lichtenfeld
    priming for the proposal-authoring task)
  * mono digits on skip-risk % and attempts count
  * 'Save plan' FilledButton.icon with checkmark
  * top drag handle for sheet affordance
- High-risk windows card: amber-tinted bg + border, mono % digits,
  amber dot per-row, chevron + InkWell tap target
- Upsell card: sky-tinted with bolt icon, quotes Gollwitzer d=0.65
- No-data state: insights icon halo + tokens.primary 0.5 alpha,
  helper copy in muted color

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…enefits

- Hero card: sky->forest gradient bg (Mehta-Zhu approach + Lichtenfeld
  completion ecosystem); '$6.99' rendered as Plus Jakarta + JetBrains
  Mono in 56pt mono with tabular figures; tagline + 'no subscription'
  iconified inline
- Sticky bottom CTA section with surface border so the FilledButton
  is always reachable without scrolling (the conversion moment must
  not be hidden by a scroll)
- 'Unlock for $6.99' button uses inline Text.rich so the price stays
  mono while the verb stays Plus Jakarta Sans
- Per-benefit accent color routes by domain: primary (sky) for habits/
  themes, warning (amber) for analytics, success (forest) for IF-THEN/
  health, danger (crimson) for ads-block, muted slate for local-first
- 40dp icon tile with 12% alpha accent bg replaces flat icon — gives
  the list visual rhythm and pairs with the streak-chip token semantics
- 'Premium unlocked' banner forest-tinted with check icon when entitled

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… zone

- Section headers: uppercase + letter-spacing 1.2 + lead icon (palette,
  bell, heart, brain, premium-badge, storage, info), token primary accent
- Theme rows: 44x28dp two-color preview pill (surface + primary +
  secondary) using the actual ThemeData a user would get on commit;
  selected check icon (forest); locked rows show price in mono digits
- IF-THEN rows: adherence % rendered mono with traffic-light color
  (success >= 70, warning >= 40, danger below)
- Subscription status: chip with success bg when premium, surfaceVariant
  when free, lifetime price uses bolt icon prefix
- Data section: danger zone wrapped in crimson-bordered box with subtle
  bg tint — visually separated from normal export action
- About: version + source rendered in JetBrains Mono with tabular figures

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New widget lib/widgets/completion_bloom.dart: one-shot OverlayEntry
  with RadialGradient painted at the tapped card's global center.
  Animation: 400ms easeOutCubic, radius 12->332dp, alpha 0.30->0
  (Lichtenfeld 2012 brief-glimpse priming via tokens.identityDot
  forest.500). Self-cleaning entry, IgnorePointer guards against
  swallowing rapid double-taps.
- HomeScreen: wrap _HabitCard with Builder to scope a cardContext,
  capture the RenderBox center before the await on
  HabitProvider.toggleCompletion, then fire the bloom only when the
  toggle resolves nowCompleted=true (no bloom on un-toggle).
- Bloom color = tokens.identityDot (forest.500 light / forest.400 dark)
  so it matches the identity-vote dot already shown on the voted line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* context.tokens now returns SemanticColors.light when the theme
  extension isn't present (e.g. widget tests that pump a bare
  MaterialApp without our AppTheme.light()). Production code still
  reads the registered values; widget tests no longer have to wire
  the theme just to use a single token.
* add_habit_screen_test: identity field's labelText was replaced
  with a hintText when the forest-tinted identity card landed; test
  now addresses the three TextFormFields by index (0=name, 1=identity,
  2=two-minute) instead of label text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 4, 2026 20:38
Outtsett added a commit that referenced this pull request May 4, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Applies the research-grounded design token system (context.tokens, Spacing, AppTypography) across the previously deferred UI surfaces (Onboarding, AddHabit, Analytics, Premium, Settings) and introduces a completion “radial bloom” micro-interaction triggered from the Home habit card.

Changes:

  • Updated multiple screens to use design tokens for spacing, color, and typography (including new section headers, stat tiles, paywall layout, and onboarding accents).
  • Added CompletionBloom overlay and integrated it into HomeScreen on habit completion.
  • Made context.tokens more resilient by adding a fallback when the theme extension is missing; adjusted widget test finders for AddHabit.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
lib/screens/onboarding_screen.dart Applies tokenized spacing/motion and per-page accent/glow styling for onboarding pages.
lib/screens/add_habit_screen.dart Tokenizes spacing/colors/typography; introduces identity “card” and animated swatch/icon selection UI.
lib/screens/analytics_screen.dart Adds tokenized summary stat tiles, rewraps heatmap, and upgrades IF-THEN bottom sheet styling/actions.
lib/screens/premium_screen.dart Reworks paywall layout with sticky CTA and tokenized benefit tiles/hero pricing typography.
lib/screens/settings_screen.dart Tokenizes section chrome and adds theme preview swatches + richer subscription/data/about presentation.
lib/screens/home_screen.dart Hooks completion bloom into habit completion interaction (captures tap center + shows overlay).
lib/widgets/completion_bloom.dart New overlay-driven radial bloom animation painter for completion micro-interaction.
lib/design/tokens.dart Adds context.tokens fallback behavior when SemanticColorsExt is not registered.
test/add_habit_screen_test.dart Updates form field targeting approach to accommodate identity field UI changes.

Comment thread lib/design/tokens.dart
Comment on lines 303 to +305
SemanticColors get tokens =>
Theme.of(this).extension<SemanticColorsExt>()!.colors;
Theme.of(this).extension<SemanticColorsExt>()?.colors ??
SemanticColors.light;
Comment on lines +192 to +196
final renderBox =
cardContext.findRenderObject() as RenderBox?;
final globalCenter = renderBox?.localToGlobal(
renderBox.size.center(Offset.zero),
);
Comment on lines +382 to +388
decoration: const InputDecoration(
hintText: 'a runner, a writer, a meditator…',
helperText:
'each completion casts a vote for this identity',
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
Comment on lines +232 to +286
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: Spacing.s4,
vertical: Spacing.s3,
),
child: Row(
children: [
_ThemeSwatch(preview: preview, isLocked: isLocked),
const SizedBox(width: Spacing.s4),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: isLocked ? lockedColor : tokens.onSurface,
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w400,
),
),
if (isLocked)
Text.rich(
TextSpan(
style: Theme.of(
context,
).textTheme.bodySmall?.copyWith(color: lockedColor),
children: [
const TextSpan(text: 'Premium · '),
TextSpan(
text: '\$6.99',
style: AppTypography.mono(
color: lockedColor,
fontSize: 12,
fontWeight: FontWeight.w700,
),
),
const TextSpan(text: ' lifetime'),
],
),
),
],
),
),
if (isLocked)
Icon(Icons.lock_outline, size: 18, color: lockedColor)
else if (isSelected)
Icon(Icons.check_circle, size: 22, color: tokens.success),
],
),
),
);
Comment on lines +143 to +150
// The first TextFormField is "Habit name", second is the identity
// field (now wrapped in a forest-tinted card with a heading row, so
// its labelText was replaced with a hintText — the test addresses
// the field by index instead of label), third is "2-minute version".
final fields = find.byType(TextFormField);
expect(fields, findsAtLeastNWidgets(3));
await tester.enterText(fields.at(0), 'Read');
await tester.enterText(fields.at(1), ' reader ');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants