* 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>
* Make categories global
This solves us A LOT of cash flow and budgeting problems.
* Update schema.rb
* Update auto_categorizer.rb
* Update income_statement.rb
* FIX budget sub-categories
* FIX sub-categories and tests
* Add 2 step migration
* Fix property subtype not persisting on edit
* Add regression test for property subtype persistence
This change introduces model specs and factories to cover
property subtype persistence on update.
FactoryBot setup and test dependencies were adjusted to
support the new specs.
* Add regression test for property subtype persistence
* remove unused FactoryBot factories and test
* remove FactoryBot in Gemfile.lock
* Fix no-op regression test for property subtype update
* Delete no-op property_test
* add pimary_residence in properties fixtures
* add capybara system test for property subtype persistence
* fix spelling and indent
* rename test to "can persist property subtype"
Signed-off-by: HugoleDino <135261771+HugoleDino@users.noreply.github.com>
---------
Signed-off-by: HugoleDino <135261771+HugoleDino@users.noreply.github.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
* 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
* Add nice formatting for subtypes on account list
* Fix rubocop linting errors
* Implement better mapping
* Fix rubocop linting
* Add short and long versions of subtypes
* Simplify subtype reference
Co-authored-by: Zach Gollwitzer <zach.gollwitzer@gmail.com>
Signed-off-by: Alex Hatzenbuhler <hatz@hey.com>
* Simplify reference logic, add a small test
* Fix test
* Fix tests
---------
Signed-off-by: Alex Hatzenbuhler <hatz@hey.com>
Co-authored-by: Zach Gollwitzer <zach.gollwitzer@gmail.com>