Migrate remaining static Lucide icons to Solar bold-duotone

Context

PR #1015 (SUP-342) migrated the sidebar's animated icons to Solar bold-duotone (@iconify-json/solar via @iconify/tailwind4). The rest of the app still uses Lucide via the <.icon name={...} /> component.

We want everything on Solar bold-duotone for a single visual language. These icons don't need animations - just static SVG rendering with the existing <.icon> API preserved so we don't have to touch every call site.

Scope

~98 distinct Lucide icon names used across the codebase via <.icon name={...} />. Full list captured from a grep of lib/:

arrow_down, arrow_left, arrow_right, arrow_up, arrow_up_down, arrow_up_right,
badge_check, banknote, blocks, calculator, calendar, calendar_clock, calendar_days,
chart_bar, chart_line, chart_no_axes_column, check, check_check, check_circle,
chevron_down, chevron_left, chevron_right, chevrons_up_down, circle_alert,
circle_check, circle_help, circle_minus, circle_plus, circle_user_round, circle_x,
clipboard, clipboard_copy, clipboard_list, clock, cloud_download, coins, copy,
copy_minus, credit_card, crown, download, ellipsis_vertical, eye, eye_off,
file_text, folder_tree, gift, hand_coins, history, hourglass, inbox, info, landmark,
link, loader, lock, log_out, menu, message_square, monitor, moon, more_horizontal,
party_popper, pause, pen, pen_line, pencil, play, plug, plus, receipt, receipt_text,
refresh_cw, repeat, rotate_ccw, send, settings, shield_alert, shield_check, siren,
sliders_horizontal, smile, sparkles, square_pen, sun, tag, tags, terminal, ticket,
trending_up, triangle_alert, unlink, unplug, user, wallet, x, zap

Proposed approach

Path A: rewire <.icon> to render Solar SVGs at compile time.

  1. Create Visor.SolarIcons module, modeled after VisorWeb.Components.AnimatedIcons.
  2. The module reads assets/node_modules/@iconify-json/solar/icons.json at compile time via @external_resource + Jason.decode!/1.
  3. Maintain a @mappings :: %{atom() => String.t()} table mapping each Lucide name above to a Solar slug (e.g. :credit_card => "card", :circle_alert => "danger-circle"). Most map directly; ~15-20 need judgment.
  4. Metaprogramming generates one def icon_name(assigns) per mapping, embedding the Solar SVG body inline with currentColor fill.
  5. Update VisorWeb.CoreComponents.icon/1 to dispatch to Visor.SolarIcons instead of Lucideicons. Keep the same name/class/rest API so no call sites change.
  6. For Lucide names with no clean Solar match, pick the closest bold-duotone variant. Document choices in module attrs for design review.

Out of scope

  • Animations (those stay on the sidebar via AnimatedIcons).
  • Removing the lucide_icons hex dep (keep until we're sure nothing else uses it).
  • React Native / mobile icon migration.

Risks

  • Solar bold-duotone is heavier than Lucide stroke icons. Cards/badges/empty states may need padding or weight tweaks. Spot-check the top 10 most-used screens after migration (dashboard, transactions, categories, recurring, accounts, budgets, goals, settings, auth, onboarding).
  • Some Lucide names have no clean Solar 1:1. Possible candidates for design review:
  • :check_check (double check), :chevrons_up_down (double chevron), :rotate_ccw, :square_pen, :plug/:unplug, :cloud_download, :copy_minus, :folder_tree, :hourglass, :landmark, :monitor, :party_popper, :sliders_horizontal, :terminal.

Acceptance criteria

  • <.icon name={:foo}> renders a Solar bold-duotone SVG for every name in the list above.
  • mix compile and mix test pass.
  • Visual QA confirms no obvious regressions on the top screens.
  • PR description includes the full mapping table for designer sign-off.
  • Parent work: PR #1015 (SUP-342) - sidebar animated icons + Iconify Tailwind plugin scaffold.

Share update with 0 linked conversations as well

Upvoters
Status

Completed

Board
💡

Feature Request

Date

14 days ago

Subscribe to post

Get notified by email when there are changes.