From 9b70f0385c9a44bfc79cdcaa43610a88f13bc4ea Mon Sep 17 00:00:00 2001 From: Guillem Arias Date: Mon, 11 May 2026 12:28:34 +0200 Subject: [PATCH] fix(savings): refine hero spacing, goal/account card padding, sparkline negative range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sparkline (`savings-sparkline` controller): dropped the `Math.max(0, yMin)` clamp on the y-axis domain so negative balances (or any series that dips into negative territory) render fully instead of being cropped off the canvas. - Hero card: padding `p-6` → `p-7`, column ratio `[minmax(0,1fr)_minmax(0,1.6fr)]` so the chart breathes, min height bumped to 220px, sparkline container `h-full min-h-[200px]` so it fills the card vertically. Stats row now sits at the bottom of the text column via `mt-auto pt-6`; labels promoted to `text-xs`, values to `text-lg`. - Section vertical rhythm: outer `space-y-6` → `space-y-8`. - Goal card: padding `p-[18px]` → `p-6`. Internal gap from header row to amount line `mt-3.5` → `mt-5`. Account-row gap `mt-3` → `mt-4`. - Account card: padding `p-5` → `p-6`. - Status pill "Behind" dot: `bg-yellow-500` → `bg-yellow-600` for a warmer/ambery tone matching the Claude Design reference. - Goal card donut "behind" stroke: `var(--color-yellow-500)` → `var(--color-yellow-600)` to match the pill. --- .../savings/account_card_component.html.erb | 2 +- .../savings/goal_card_component.html.erb | 6 +++--- app/components/savings/goal_card_component.rb | 2 +- .../savings/status_pill_component.rb | 2 +- .../savings_sparkline_controller.js | 7 +++++-- app/views/savings_goals/index.html.erb | 20 +++++++++---------- 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/app/components/savings/account_card_component.html.erb b/app/components/savings/account_card_component.html.erb index ff62bcdc3..85927b39c 100644 --- a/app/components/savings/account_card_component.html.erb +++ b/app/components/savings/account_card_component.html.erb @@ -1,4 +1,4 @@ -
+
diff --git a/app/components/savings/goal_card_component.html.erb b/app/components/savings/goal_card_component.html.erb index e2409047a..e15f2f998 100644 --- a/app/components/savings/goal_card_component.html.erb +++ b/app/components/savings/goal_card_component.html.erb @@ -1,5 +1,5 @@ <%= link_to savings_goal_path(goal), - class: "group block bg-container rounded-xl shadow-border-xs hover:bg-surface-hover transition-colors p-[18px]", + class: "group block bg-container rounded-xl shadow-border-xs hover:bg-surface-hover transition-colors p-6", data: { savings_goals_filter_target: "card", goal_name: goal.name, @@ -40,14 +40,14 @@
-
+
<%= goal.current_balance_money.format %> / <%= goal.target_amount_money.format %>
-
+
<%= render Savings::AccountStackComponent.new(accounts: linked_accounts) %> <%= linked_accounts_count_label %> diff --git a/app/components/savings/goal_card_component.rb b/app/components/savings/goal_card_component.rb index fb3c52795..667397e3a 100644 --- a/app/components/savings/goal_card_component.rb +++ b/app/components/savings/goal_card_component.rb @@ -15,7 +15,7 @@ class Savings::GoalCardComponent < ApplicationComponent def ring_color case goal.status when :reached then "var(--color-green-600)" - when :behind then "var(--color-yellow-500)" + when :behind then "var(--color-yellow-600)" when :on_track then "var(--text-primary)" else "var(--text-subdued)" end diff --git a/app/components/savings/status_pill_component.rb b/app/components/savings/status_pill_component.rb index 8adbb6378..d200f3833 100644 --- a/app/components/savings/status_pill_component.rb +++ b/app/components/savings/status_pill_component.rb @@ -1,7 +1,7 @@ class Savings::StatusPillComponent < ApplicationComponent VARIANTS = { on_track: { classes: "bg-green-500/10 text-success", dot: "bg-green-600" }, - behind: { classes: "bg-yellow-500/10 text-warning", dot: "bg-yellow-500" }, + behind: { classes: "bg-yellow-500/10 text-warning", dot: "bg-yellow-600" }, reached: { classes: "bg-green-500/10 text-success", dot: "bg-green-600" }, no_target_date: { classes: "bg-surface-inset text-secondary", dot: "bg-gray-400" } }.freeze diff --git a/app/javascript/controllers/savings_sparkline_controller.js b/app/javascript/controllers/savings_sparkline_controller.js index e0ea79e14..2b0912a4b 100644 --- a/app/javascript/controllers/savings_sparkline_controller.js +++ b/app/javascript/controllers/savings_sparkline_controller.js @@ -42,10 +42,13 @@ export default class extends Controller { const yMin = Math.min(...series.map((d) => d.value)); const yMax = Math.max(...series.map((d) => d.value)); - const padding = (yMax - yMin) * 0.15 || yMax * 0.05 || 1; + // Don't clamp to 0 — savings totals can be negative under certain + // demo / edge conditions, and clamping pushes the line off-canvas. + const range = yMax - yMin; + const padding = range > 0 ? range * 0.15 : Math.abs(yMax) * 0.05 || 1; const y = d3 .scaleLinear() - .domain([Math.max(0, yMin - padding), yMax + padding]) + .domain([yMin - padding, yMax + padding]) .range([margin.top + innerHeight, margin.top]); const svg = d3 diff --git a/app/views/savings_goals/index.html.erb b/app/views/savings_goals/index.html.erb index 8ae292476..cb78e01a3 100644 --- a/app/views/savings_goals/index.html.erb +++ b/app/views/savings_goals/index.html.erb @@ -1,4 +1,4 @@ -
+

<%= t(".title") %>

<%= t(".subtitle") %>

@@ -8,7 +8,7 @@ <%= render "empty_state", linkable_account_count: @linkable_account_count %> <% else %> <%# Hero card %> -
+

<%= t(".hero.total_in_savings") %>

<%= @hero[:total_savings_money].format %>

@@ -19,24 +19,24 @@

<% end %> -
+
-

<%= t(".hero.accounts") %>

-

<%= @hero[:accounts_count] %>

+

<%= t(".hero.accounts") %>

+

<%= @hero[:accounts_count] %>

-

<%= t(".hero.active_goals") %>

-

<%= @hero[:active_goals_count] %>

+

<%= t(".hero.active_goals") %>

+

<%= @hero[:active_goals_count] %>

-

<%= t(".hero.saved_toward_goals") %>

-

<%= @hero[:saved_toward_goals_money].format %>

+

<%= t(".hero.saved_toward_goals") %>

+

<%= @hero[:saved_toward_goals_money].format %>

<% if @hero[:sparkline_series].size >= 2 %> -
<% end %>