* fix(design-system): DS::Button a11y audit
Closes#1738. Four concrete fixes surfaced by the savings-goals
audit + #1737 universal checklist:
1. Focus ring (WCAG 2.4.7). `base.css` had
`focus-visible:outline-gray-900` which is **1.07:1** against the
primary button's gray-900 background — invisible. Widen to
`outline-2 outline-offset-2`, place outline outside the button
via offset, and add a dark-mode `outline-white` so the ring is
always visible against the page chrome regardless of the button
surface.
2. Touch target (WCAG 2.5.5). Icon-only buttons at the default
`:md` size were `w-9 h-9` = 36×36, below the 44×44 enhanced
target. Bump `md.icon_container_classes` to `w-11 h-11` and
`lg.icon_container_classes` to `w-12 h-12` to keep the size
scale intact. `sm` stays at 32×32 (already passes WCAG 2.5.8
AA's 24×24 minimum; intentional compact-density variant).
3. Default button type. `content_tag(:button, ...)` inherits the
HTML default `type="submit"`, so a DS::Button rendered inside a
form steals Enter-key submission from the first text input
(reproducible in the form stepper). Default to `type="button"`
in the non-`href` branch; existing form submitters pass
`type: "submit"` explicitly and continue to work. The `button_to`
(href) branch keeps the submit default because button_to wraps
its own form.
4. Icon-only accessible name. Icon-only buttons render no text
node, so AT users hear "button" with no name. Derive a
humanized aria-label from the icon key (e.g. `icon: "more-horizontal"`
→ `aria-label="More horizontal"`); explicit
`aria: { label: }` on the caller still wins. Soft fallback —
callers should still pass meaningful labels for richer copy.
Plus: replace the stale `fg-white` icon class on the destructive
variant with `text-inverse` (the `fg-*` namespace was deprecated
in #1626 so `fg-white` resolved to nothing; the icon was using its
helper-default color rather than the white the design intended).
Out of scope:
- Menu avatar trigger (custom 36×36 button bypassing DS::Button) —
belongs to #1743 DS::Menu audit.
- DS::FilledIcon `lg` size container (decorative, not interactive)
— belongs to #1742.
* fix(design-system): force type=submit on StyledFormBuilder#submit
The DS::Button default-type-button change in the previous commit
broke every `form.submit "Log in"` callsite because
`StyledFormBuilder#submit` (app/helpers/styled_form_builder.rb)
renders a DS::Button under the hood with no explicit `type:`.
After the default flip, those submit buttons rendered as
`type="button"`, so submitting forms (login, password reset, every
form using `form.submit`) silently no-ops. CI surfaced this via
~30 system tests failing in the `sign_in` helper, which couldn't
get past the login page.
Pin `type: "submit"` on the DS::Button rendered by
`StyledFormBuilder#submit`. The 22 view-level `f.submit` /
`render DS::Button.new(type: :submit, ...)` callers already pass
type explicitly and are unaffected.
* fix(review): href-branch type-button bug + focus-ring tokens + profile Save submit
CodeRabbit P1+P2 review on #1840:
1. button.rb: `merged_opts.delete(:href)` always returned nil because
Buttonish#initialize strips :href from opts into @href, so the
`if href.blank?` guard was ALWAYS true. Every DS::Button rendered via
button_to (the href branch) got `type="button"` on the inner button,
breaking submission of those button_to-generated forms (e.g.
imports/_ready.html.erb publish button, imports/_failure.html.erb
try-again button). Drop the local `href = merged_opts.delete(:href)`
so the guard now reads the @href reader, leaving the href branch's
HTML default intact.
2. settings/profiles/show.html.erb: the Save button is rendered with
`render DS::Button.new(...)` inside `styled_form_with` (not via
form.submit), so the StyledFormBuilder#submit type-pin from
624e9794 doesn't cover it. Pass `type: :submit` explicitly so the
profile form submits again under the default-type-button policy.
3. base.css: replace raw `outline-gray-900` / `outline-white` with the
established alpha-ring focus pattern
(focus-visible:ring-alpha-black-300 + theme-dark:ring-alpha-white-300)
already used by app/components/settings/provider_card.html.erb and
sure-design-system/components.css. Keeps a11y focus ring while using
DS tokens.
* fix(review): add type: :submit to DS::Button submitters inside forms
CI test_system on #1840 surfaced 6 failures (confirm-dialog close,
property create/edit, transaction filter apply) caused by the same
gap that db563f3d started addressing: the default-type-button policy
on DS::Button means every \`render DS::Button.new(...)\` inside a
\`<form>\` (or \`styled_form_with\`) that relies on the HTML default to
submit is now an inert \`type="button"\`.
Audited every \`render DS::Button.new(\` callsite repo-wide for the
combination (no \`type:\`, no \`href:\`, inside a form context) and
pinned \`type: :submit\` explicitly on the 12 forms that need it:
- layouts/shared/_confirm_dialog.html.erb: Confirm button inside the
global \`<form method=\"dialog\">\` — fixes
test_should_allow_revoking_API_key_with_confirmation.
- properties/{new,edit,balances}.html.erb: Save/Next submitter inside
\`styled_form_with\` — fixes test_can_create_property_account,
test_can_persist_property_subtype.
- transactions/searches/_menu.html.erb: Apply inside the filter form —
fixes test_can_filter_uncategorized_transactions,
test_all_filters_work_and_empty_state_shows_if_no_match,
test_can_open_filters_and_apply_one_or_more.
- transactions/bulk_updates/new.html.erb: Save in bulk-edit drawer.
- account_sharings/show.html.erb: Save in account-sharing form.
- category/deletions/new.html.erb, tag/deletions/new.html.erb:
destructive + safe submit buttons in deletion dialog forms.
- family_merchants/merge.html.erb: Submit in merge form.
- subscriptions/upgrade.html.erb: contribute_and_support_sure submit.
- rules/_category_rule_cta.html.erb: Dismiss inside the
rule_prompts_disabled form.
Cancel/close DS::Button instances inside these same forms intentionally
keep the \`type=button\` default since they drive JS-only actions
(\`DS--dialog#close\`, \`DS--menu#close\`).
* fix(review): add type: :submit to 4 remaining form-context DS::Button callers
Second sweep for the same default-type-button regression that 24c517eb
fixed for 12 callsites. The latest CI run on this branch narrowed the
failures from 6 to 2 (the property wizard's Address step still failed
because that view was not in the first sweep). Audited via a wider
4000-char form-context window:
- app/views/properties/address.html.erb: Save inside
styled_form_with — fixes the remaining
test_can_create_property_account + test_can_persist_property_subtype
by letting Step 3 of the property wizard complete.
- app/views/onboardings/goals.html.erb: Submit inside form_with so
the onboarding goals step submits.
- app/views/account_sharings/show.html.erb (owner-side form): Save
button for the family-share permissions form (the non-owner Save
was already fixed in 24c517eb).
- app/views/transactions/_attachments.html.erb: Upload inside
styled_form_with — kept the JS-driven hook (attachment_upload_target)
but explicit type:submit covers the no-JS fallback.
* fix(review): pin type=submit on the Save currencies button
Codex P1 (third pass) caught one more in-form DS::Button I missed in
the earlier sweeps: \`app/views/settings/preferences/show.html.erb:185\`
renders the Save currencies submit deep inside a long
\`styled_form_with\` block. The form-context scan I used had a finite
look-back window which missed it because the matching
\`styled_form_with\` opener sits ~80 lines / 4k+ characters above the
button. Switched to a whole-file scan to confirm no further callsite
remains.
* Extract hardcoded strings to i18n
Replace numerous hardcoded English strings with I18n lookups (t / I18n.t) across controllers, views, helpers, and components, and convert model validation error messages to symbol keys. Added multiple locale files under config/locales for models and views. This centralizes user-facing notices/alerts, UI text, import/validation messages, and prepares the app for localization and easier translation maintenance.
* Update en.yml
* Update preview-cleanup.yml
* Revert "Update preview-cleanup.yml"
This reverts commit 1ba6d3c34c.
* test: align i18n assertions with translated messages
* Standardize balance error key and tweak locales
Replace SophtronAccount's :requires_balance error key with :no_balance and update related locale strings for sophtron, plaid, and simplefin accounts to use the new key and clearer copy. Also switch the QIF upload redirect notice to use a relative translation key (t('.qif_uploaded')), remove an unused SSO providers help line, and fix a trailing-newline/whitespace issue in the subscriptions locale. These changes standardize validation keys and improve translation consistency and messaging.
---------
Co-authored-by: KiloClaw <kiloclaw@openclaw.ai>
* feat(splits): add excluded attribute support for split children and improve rendering of split transactions
* address coderabbitai suggestions to improve code quality
* Fix split excluded coercion, DRY helpers, and clean up view partials
Fix boolean coercion bug where string "false" from form params was
truthy in Ruby, causing all split children to be marked excluded.
Use ActiveModel::Type::Boolean for explicit casting in Entry#split!.
Additional changes addressing code review feedback:
- Extract duplicated in_split_group logic from TransactionsController
and TransactionCategoriesController into TransactionsHelper
- Remove redundant local_assigns.fetch calls in partials that already
declare defaults via the Rails 7.1 locals: magic comment
- Simplify ternary in _transaction.html.erb to pass grouped directly
- Guard hidden_field_tag :grouped to only emit when value is "true"
- Add model tests for excluded on split children (boolean and string)
- Add controller test for excluded param through full HTTP stack
- Add test confirming excluded children are dropped from balance queries
* fix(splits): simplify excluded attribute boolean check
* refactor(splits): extract truthy values constant for excluded check
Extract the array of truthy values used for excluded attribute check
into a private constant to improve code maintainability and avoid
duplication of the magic array.
* refactor: simplify split grouping link generation and add test coverage for excluded split parameters
* Make category selection menus opaque for excluded transactions
* Allow keyboard navigation in category selection menu
* Fix category transparency on mobile
* Make checkbox opaque
* Remove text-secondary from amount container
* Submit form directly
* Handle aria labels
* Initial split transaction support
* Add support to unsplit and edit split
* Update show.html.erb
* FIX address reviews
* Improve UX
* Update show.html.erb
* Reviews
* Update edit.html.erb
* Add parent category to dialog
* Update en.yml
* Add UI indication to totals
* FIX ui update
* Add category select like rest of app
* Add split ui
* Add settings configuration for split transactions
- Adds a new settings section for appearance changes
- Also adds extra checks for delete and API calls
- Also adds checks for parent/child changes
* fixes
- split transactions dark mode fix
- add split transactions to context menu
* Update entry.rb
1. New validation split_child_date_matches_parent — prevents saving a split child with a date different from its parent. This is the root-cause fix that
protects all flows at once.
2. Bulk update guard — bulk_update! now strips :date from attributes when processing split children, preventing the validation from raising and silently
skipping the date change instead.
* N+1 fix for split_parent?
* Update entry.rb
Problem: In bulk_update!, when a split child has :date removed from attrs (line 432) and the remaining attrs is empty (e.g., the bulk update only
changed the date), entry.update! {} still ran as a no-op. But lock_saved_attributes! and mark_user_modified! at lines 443-444 executed unconditionally,
incorrectly marking untouched split children as user-modified and opting them out of future syncs.
Fix:
1. Added a changed flag to track whether any actual modification happened
2. Wrapped entry.update! in an if attrs.present? check so no-op updates are skipped
3. Gated lock_saved_attributes! and mark_user_modified! behind if changed, so they only run when the entry was actually modified (either via attribute
update or tag update)
* fixes
1. Indentation in show.html.erb Settings section — The split button block and delete block had extra indentation making them appear nested inside guard
blocks they weren't part of. Fixed to match actual nesting.
2. Skip @split_parents query when grouping is off — The controller now only loads split parent entries when show_split_grouped? is true, saving a query
with joins when the feature is disabled.
* feat: Add toggle on mobile to show/hide checkboxes in transaction page
* fix: Add multi-select toggle also in activities page. Make JS controller compatible also in this view.
* feat: Add category in mobile view
* feat: Add mobile layout for transaction categories
* feat: Add margin for pagination on mobile
* fix: Ensure category exists when displaying the name
* fix: Adjust mobile paddings
* fix: Display "uncategorized" label if no category is set
* fix: Expand transaction name/subtitle
* feat: Add merchant name on desktop view
* feat: Move merchant name before account name
* fix: Add class to hide merchant on mobile
* feat: Add merchant logo on mobile
* fix: add pointer-events-none to merchant image on mobile view
* feat: toggle header checkbox in transaction page when button is clicked
* Remove unnecessary CSS class
* Remove duplicate CSS class
* Remove wrong Enable Banking logo URL
* Update app/views/transactions/_transaction.html.erb
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com>
* Revert "Update app/views/transactions/_transaction.html.erb"
This reverts commit 9766c50a1d.
* Add translation for Loan Payment/Transfer
* Apply review comments
* Add accessible name for toggle based on review comments
* Use border instead of border-1 class
* Apply review comments
* Missing l10n key
---------
Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
* Create shared ruler view
* Use collection rendering/spacer templates for rules, and new shared_ruler
* Use shared ruler for all the places a ruler is used
* Use shared ruler for imports and balance sheet
* Fix brakeman by using a static partial with a defined collection
* Update balance sheet
* Fix category dark mode styles
* Fix sidebar account tab states
* Fix dashboard balance sheet group styles
* Fix budget dark mode styles
* Fix chart gradient split
* Fix prose styles in dark mode
* Add back chat nav id for tests
* Add lookbook + viewcomponent, organize design system file
* Build menu component
* Button updates
* More button fixes
* Replace all menus with new ViewComponent
* Checkpoint: fix tests, all buttons and menus converted
* Split into Link and Button components for clarity
* Button cleanup
* Simplify custom confirmation configuration in views
* Finalize button, link component API
* Add toggle field to custom form builder + Component
* Basic tabs component
* Custom tabs, convert all menu / tab instances in app
* Gem updates
* Centralized icon helper
* Update all icon usage to central helper
* Lint fixes
* Centralize all disclosure instances
* Dialog replacements
* Consolidation of all dialog styles
* Test fixes
* Fix app layout issues, move to component with slots
* Layout simplification
* Flakey test fix
* Fix dashboard mobile issues
* Finalize homepage
* Lint fixes
* Fix shadows and borders in dark mode
* Fix tests
* Remove stale class
* Fix filled icon logic
* Move transparent? to public interface
* fix: improve dark mode readability across the app
* fix: improve dark mode support for asset percentage text
* fix: apply suggested patch for theme-related improvements
* chore: apply PR feedback – remove dark:, align with design tokens, update form builder
* chore: revert background token and restore original style for visual consistency
* chore: remove unnecessary class attributes from form fields using builder
* refactor: move number_field and date_field into metaprogramming block
* refactor: replace bg-divider-adaptive divs with <hr> and border-secondary
* fix: apply requested changes and linting fixes
* Add geist font
* Design system css file
* Add cursor ui/ux rules
* Add shadows and shadow borders
* Replace primitives with tokens for common text and backgrounds
* Organize css
* Update switch and checkbox class names
* Add back global color variables
* Transfer data model migration
* Transfers and payment modeling and UI improvements
* Fix CI
* Transfer matching flow
* Better UI for transfers
* Auto transfer matching, approve, reject flow
* Mark transfers created from form as confirmed
* Account filtering
* Excluded rejected transfers from calculations
* Calculation tweaks with transfer exclusions
* Clean up migration
* Consolidate modal form structure into partial + helper
* Scaffold out trade transaction form
* Normalize translations
* Add buy and sell trade form with tests
* Move entryable lists to dedicated controllers
* Delegate entry group contents rendering
* More cleanup
* Extract transaction and valuation update logic from entries controller
* Delegate edit and show actions to entryables
* Trade builder
* Update paths for transaction updates
* Make forms more composable, opt-in to form builder
* Remove unused method
* Simpler money input controls
* Add in new form styling to imports
* Lint fixes
* Small tweak of multi select styles
* Initial entryable models
* Update transfer and tests
* Update transaction controllers and tests
* Update sync process to use new entries model
* Get dashboard working again
* Update transfers, imports, and accounts to use Account::Entry
* Update system tests
* Consolidate transaction management into entries controller
* Add permitted partial key helper
* Move account transactions list to entries controller
* Delegate transaction entries search
* Move transfer relation to entry
* Update bulk transaction management flows to use entries
* Remove test code
* Test fix attempt
* Update demo data script
* Consolidate remaining transaction partials to entries
* Consolidate valuations controller to entries controller
* Lint fix
* Remove unused files, additional cleanup
* Add back valuation creation
* Make migrations fully reversible
* Stale routes cleanup
* Migrations reversible fix
* Move types to entryable concern
* Fix search when no entries found
* Remove more unused code
* Move Transfer to Account namespace
* Fix partial resolution due to namespacing plurality
* Make category and tag controllers consistent with namespacing convention
* Update stale partial reference