diff --git a/app/components/DS/pill.rb b/app/components/DS/pill.rb
index 6e6aae924..899c97345 100644
--- a/app/components/DS/pill.rb
+++ b/app/components/DS/pill.rb
@@ -104,24 +104,26 @@ class DS::Pill < DesignSystemComponent
def container_classes
base = [
"inline-flex items-center align-middle font-medium whitespace-nowrap shrink-0",
- "border rounded-md",
- "leading-none"
+ "border leading-none"
]
if marker
- # Marker mode (Beta / Canary / NEW): uppercase, sub-12px text,
- # wider tracking. text-[10/11px] stays as arbitrary values — the
- # pill is intentionally sub-12px (Sure's smallest scale token is
- # text-xs / 12px) so it reads as a marker, not a label. Padding /
- # gap / tracking snap to Tailwind's scale to satisfy the
- # design-system "no arbitrary values" rule.
- base << "uppercase"
+ # Marker mode (Beta / Canary / NEW): rounded-md (slight chip
+ # shape), uppercase, sub-12px text, wider tracking.
+ # text-[10/11px] stays as arbitrary values — the pill is
+ # intentionally sub-12px (Sure's smallest scale token is text-xs
+ # / 12px) so it reads as a marker, not a label. Padding / gap /
+ # tracking snap to Tailwind's scale to satisfy the design-system
+ # "no arbitrary values" rule.
+ base << "rounded-md uppercase"
base << (size == :md ? "px-2 py-0.5 text-[11px] tracking-wide gap-1" : "px-1.5 py-0.5 text-[10px] tracking-wider gap-1")
else
# Badge mode (Pending / Active / Past due / category tag):
- # normal case, snaps to the design-system text scale
- # (`text-xs` / `text-sm`). Padding bumps slightly so the badge
- # reads as a status chip rather than a sub-12px marker.
+ # rounded-full pill shape (matches the existing convention used
+ # by `settings/providers/_status_pill`, `_maturity_badge`, and
+ # the inline transaction badges). Normal case, snaps to the
+ # design-system text scale (`text-xs` / `text-sm`).
+ base << "rounded-full"
base << (size == :md ? "px-2 py-0.5 text-sm gap-1.5" : "px-1.5 py-0.5 text-xs gap-1")
end
class_names(*base)
diff --git a/app/views/transactions/_header.html.erb b/app/views/transactions/_header.html.erb
index a5ca70b63..26fcacd94 100644
--- a/app/views/transactions/_header.html.erb
+++ b/app/views/transactions/_header.html.erb
@@ -22,10 +22,13 @@
<%= entry.date ? I18n.l(entry.date, format: :long) : "—" %>
<% if entry.transaction.pending? %>
- ">
- <%= icon "clock", size: "sm", color: "current" %>
- <%= t("transactions.transaction.pending") %>
-
+ <%= render DS::Pill.new(
+ label: t("transactions.transaction.pending"),
+ tone: :neutral,
+ marker: false,
+ icon: "clock",
+ title: t("transactions.transaction.pending_tooltip")
+ ) %>
<% end %>
diff --git a/app/views/transactions/_split_parent_row.html.erb b/app/views/transactions/_split_parent_row.html.erb
index 99bf6a086..f2e68eca6 100644
--- a/app/views/transactions/_split_parent_row.html.erb
+++ b/app/views/transactions/_split_parent_row.html.erb
@@ -36,10 +36,12 @@
-
- <%= icon "split", size: "sm", color: "current" %>
- <%= t("transactions.split_parent_row.split_label") %>
-
+ <%= render DS::Pill.new(
+ label: t("transactions.split_parent_row.split_label"),
+ tone: :neutral,
+ marker: false,
+ icon: "split"
+ ) %>
diff --git a/app/views/transactions/_transaction.html.erb b/app/views/transactions/_transaction.html.erb
index 659591bff..8bccd8b4e 100644
--- a/app/views/transactions/_transaction.html.erb
+++ b/app/views/transactions/_transaction.html.erb
@@ -98,33 +98,45 @@
<%# Pending indicator %>
<% if transaction.pending? %>
- ">
- <%= icon "clock", size: "sm", color: "current" %>
- <%= t("transactions.transaction.pending") %>
-
+ <%= render DS::Pill.new(
+ label: t("transactions.transaction.pending"),
+ tone: :neutral,
+ marker: false,
+ icon: "clock",
+ title: t("transactions.transaction.pending_tooltip")
+ ) %>
<% end %>
<%# Potential duplicate indicator - different styling for low vs medium confidence %>
<% if transaction.has_potential_duplicate? %>
<% if transaction.low_confidence_duplicate? %>
- ">
- <%= icon "help-circle", size: "sm", color: "current" %>
- <%= t("transactions.transaction.review_recommended") %>
-
+ <%= render DS::Pill.new(
+ label: t("transactions.transaction.review_recommended"),
+ tone: :neutral,
+ marker: false,
+ icon: "help-circle",
+ title: t("transactions.transaction.review_recommended_tooltip")
+ ) %>
<% else %>
- ">
- <%= icon "alert-triangle", size: "sm", color: "current" %>
- <%= t("transactions.transaction.possible_duplicate") %>
-
+ <%= render DS::Pill.new(
+ label: t("transactions.transaction.possible_duplicate"),
+ tone: :warning,
+ marker: false,
+ icon: "alert-triangle",
+ title: t("transactions.transaction.potential_duplicate_tooltip")
+ ) %>
<% end %>
<% end %>
<%# Split indicator %>
<% if @split_parent_entry_ids ? @split_parent_entry_ids.include?(entry.id) : entry.split_parent? %>
- ">
- <%= icon "split", size: "sm", color: "current" %>
- <%= t("transactions.transaction.split") %>
-
+ <%= render DS::Pill.new(
+ label: t("transactions.transaction.split"),
+ tone: :neutral,
+ marker: false,
+ icon: "split",
+ title: t("transactions.transaction.split_tooltip")
+ ) %>
<% end %>
<% if entry.split_child? && !in_split_group %>
">
diff --git a/test/components/DS/pill_test.rb b/test/components/DS/pill_test.rb
index f108593b4..aab1cc9ad 100644
--- a/test/components/DS/pill_test.rb
+++ b/test/components/DS/pill_test.rb
@@ -1,16 +1,19 @@
require "test_helper"
class DS::PillTest < ViewComponent::TestCase
- test "marker mode (default) renders uppercase sub-12px chrome" do
+ test "marker mode (default) renders uppercase sub-12px chrome with rounded-md" do
render_inline(DS::Pill.new(label: "Beta", tone: :violet))
pill = page.find("span", text: "Beta")
assert_includes pill[:class], "uppercase"
# Marker keeps sub-12px text via arbitrary value (intentional — see component docs).
assert_match(/text-\[1[01]px\]/, pill[:class])
+ # Marker uses rounded-md (chip shape).
+ assert_includes pill[:class], "rounded-md"
+ refute_includes pill[:class], "rounded-full"
end
- test "marker: false renders normal-case DS-scale chrome" do
+ test "marker: false renders normal-case DS-scale chrome with rounded-full" do
render_inline(DS::Pill.new(label: "Active", tone: :success, marker: false))
pill = page.find("span", text: "Active")
@@ -18,6 +21,9 @@ class DS::PillTest < ViewComponent::TestCase
# Badge mode snaps to text-xs / text-sm — no sub-12px arbitrary values.
assert_match(/text-(xs|sm)/, pill[:class])
refute_match(/text-\[1[01]px\]/, pill[:class])
+ # Badge uses rounded-full to match the existing _status_pill / _maturity_badge convention.
+ assert_includes pill[:class], "rounded-full"
+ refute_includes pill[:class], "rounded-md"
end
test "semantic tone aliases resolve to visual palette tones" do
@@ -51,9 +57,9 @@ class DS::PillTest < ViewComponent::TestCase
# Lucide icon helper renders the inline SVG; verifying we see at least one