ux(goals/show): catch-up alert anchors all three numbers + scoped scrollbar on contributions list

Alert previous pass led with delta ("Behind by $750/mo") but the user
still had to reconcile that with the $1,000/mo CTA — the relationship
between current pace, gap, and required rate was implicit.

Make every number visible in the sentence:
- Title: "Save $1,000/mo to stay on track" — leads with the action +
  required rate. Reduces decision load: the headline is what to do.
- Body: "You're saving $250/mo today — $750/mo short of the pace to
  finish by September 11, 2026." — current pace + gap + deadline.

User can now mentally verify: $250 + $750 = $1,000. The catch-up
amount in title + body + CTA is no longer disconnected from the
current pace number; the body is the bridge.

Adds `scrollbar` utility (defined in app/assets/tailwind/application.css
as 4px gray-300 thumb) to the contributions list container. Browser-
default scrollbar was rendering as a thick dark bar in light mode on
some OSes; the in-house utility renders a thin gray thumb consistently
across themes.
This commit is contained in:
Guillem Arias
2026-05-11 21:05:03 +02:00
parent b47e3478b7
commit 270ea2630d
2 changed files with 8 additions and 7 deletions

View File

@@ -137,12 +137,13 @@
<% catch_up_money = Money.new(@goal.monthly_target_amount, @goal.currency) %>
<% catch_up_delta = @goal.monthly_target_amount.to_d - @stats[:avg_monthly].to_d %>
<% catch_up_delta_money = Money.new(catch_up_delta, @goal.currency) %>
<%= render DS::Alert.new(variant: "warning", title: t("goals.show.catch_up.title", delta: catch_up_delta_money.format)) do %>
<% catch_up_avg_money = Money.new(@stats[:avg_monthly], @goal.currency) %>
<%= render DS::Alert.new(variant: "warning", title: t("goals.show.catch_up.title", amount: catch_up_money.format)) do %>
<p class="text-secondary">
<% if @goal.target_date %>
<%= t("goals.show.catch_up.body_with_date", amount: catch_up_money.format, date: I18n.l(@goal.target_date, format: :long)) %>
<%= t("goals.show.catch_up.body_with_date", avg: catch_up_avg_money.format, delta: catch_up_delta_money.format, date: I18n.l(@goal.target_date, format: :long)) %>
<% else %>
<%= t("goals.show.catch_up.body", amount: catch_up_money.format) %>
<%= t("goals.show.catch_up.body", avg: catch_up_avg_money.format, delta: catch_up_delta_money.format) %>
<% end %>
</p>
<div class="mt-2">
@@ -306,7 +307,7 @@
<h2 class="text-sm font-medium text-primary"><%= t(".contributions_heading") %></h2>
<span class="ml-2 text-xs text-subdued tabular-nums"><%= @contributions.size %></span>
</div>
<div class="max-h-[420px] overflow-y-auto overflow-x-hidden">
<div class="max-h-[420px] overflow-y-auto overflow-x-hidden scrollbar">
<%= render "contributions_list", contributions: @contributions %>
</div>
</div>

View File

@@ -123,9 +123,9 @@ en:
on_track: At your current pace, you'll reach this goal around <strong class="text-primary">%{date}</strong>.
aria_label: "Projection chart for %{name}"
catch_up:
title: "Behind by %{delta}/mo"
body_with_date: "Save %{amount}/mo to stay on track for %{date}."
body: "Save %{amount}/mo to stay on track."
title: "Save %{amount}/mo to stay on track"
body_with_date: "You're saving %{avg}/mo today — %{delta}/mo short of the pace to finish by %{date}."
body: "You're saving %{avg}/mo today — %{delta}/mo short of the required pace."
cta: "Add %{amount}"
confirm_complete_title: Mark this goal complete?
confirm_complete_body: It leaves the Ongoing list. You can still archive or restore it later.