Replace ProminentButtonModifier with standard button rows and styles - #554
Closed
bradleymackey wants to merge 2 commits into
Closed
bradleymackey wants to merge 2 commits into
bradleymackey wants to merge 2 commits into
Conversation
Removes six view files and two `Color` members that reimplement SwiftUI
APIs the app already uses elsewhere. Every one has zero call sites
outside its own file, so this is deletion only — no behaviour change.
| Deleted | Superseded by |
| --- | --- |
| `SearchTextField.swift` | `.searchable`, already used at `VaultItemFeedView.swift:80` |
| `TextArea.swift` | `TextEditor`, already used in three places |
| `TextEditingView.swift` + `TextViewViewController.swift` | `TextEditor` |
| `OTPCodeLabels.swift` | the inline `labelsStack` copies that replaced it |
| `View+Center.swift` | `.frame(maxWidth:)` |
| `Color.contrastingForegroundColor` / `.contrastingBackgroudColor` | unreferenced |
`TextEditingView` carried a doc comment explaining it existed to dodge
"bugs we've experienced with raw SwiftUI text editors". That workaround
was never in service — `SecureNoteDetailView` and `BackupCreatePDFView`
both use a plain `TextEditor` — so nothing regresses by removing it.
`HorizontallyCenter` (`HStack { Spacer(); content; Spacer() }`) is
replaced at its nine call sites by `.frame(maxWidth: .infinity)`, which
centres its child at its ideal size the same way. Six of those sites are
buttons whose pill chrome is applied by an inner modifier, so the button
keeps its intrinsic size rather than stretching.
## Verification
Local, iPhone 18 Pro Max / iOS 27.0:
- `xcodebuild build-for-testing` — `TEST BUILD SUCCEEDED`, no warnings
(`-warnings-as-errors` is on package-wide)
- Full suite, `-parallel-testing-enabled NO` — `TEST EXECUTE SUCCEEDED`,
2834 passed, 0 failures (1417 tests across 12 bundles, run under both
the Default and TSAN configurations)
- **Zero snapshots re-recorded.** That is the check that matters here: if
any of these types had still been reachable, an image would have moved.
- `make format` + `make lint` — clean
⚠️ Automatic CI is still disabled (#548), so this is local verification only.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Applies the button convention from #551 to the screens outside `Views/Backup`, and deletes the modifier that made the old treatment possible. ## The problem `ProminentButtonModifier` reimplemented `.borderedProminent` by hand: `.font(.headline)` + a forced `.foregroundStyle(.white)` + `padding(16/12)` + `.background(color)` + `RoundedRectangle(cornerRadius: 12)`. It applied `.buttonStyle(.borderless)` *inside* its own body, so the wrapped button never received a pressed state, and the hardcoded white label had no contrast guarantee against the tint it was placed on. Six of its nine call sites also placed the button inside a `Section` footer as a padded, centred pill — the exact pattern #551 called out and removed from the backup screens. ## What changed | Screen | Change | | --- | --- | | `SettingsDangerView` | "Delete All Data" becomes a red row in its own section; the error message becomes that section's footer | | `VaultTagDetailView` | "Delete Tag" becomes a red row | | `VaultItemDetailView` | "Unlock" and "Dismiss" move out of section footers into their own rows | | `EncryptedItemDetailView` | "Decrypt" moves out of the footer into its own section | | `VaultDetailEncryptionEditView` | "Encrypt" and "Remove Encryption" become plain rows | | `OTPCodeDetailView` | "Delete" moves out of the editing-actions footer into its own red section | | `SecureNoteDetailView` | "Delete" likewise | | `VaultAutofillConfigurationView` | "Continue" is a genuine standalone CTA outside any `Form`, so it becomes `.buttonStyle(.borderedProminent)` + `.controlSize(.large)` | Destructive emphasis is preserved the way #551 preserved it: a red `FormRow` glyph tile plus red row text, matching `BackupRestoreView`'s Import & Override. Primary actions use the accent colour the same way `BackupKeyDecryptorView` does. The `ProgressView` in each `loading:` branch loses its `.tint(.white)`, which only existed because the spinner sat on a filled pill. Toolbar *Cancel* buttons keep their explicit `.foregroundStyle(.red)`. `Button(role: .cancel)` does not render red in a toolbar, so dropping the tint would have been a visual regression rather than a standardization. ## Liquid Glass does not survive snapshot rendering "Continue" was first written as `.buttonStyle(.glassProminent)`. That rendered `VaultAutofillConfigurationView` as a **completely blank image** — not just the button, the whole hierarchy — dropping the reference from 151KB to 62KB. Liquid Glass samples a backdrop through the render server, and the `.image` snapshot strategy rasterises off-screen, so the effect resolves to nothing and takes the rest of the frame with it. `Snapshotting.image(drawHierarchyInKeyWindow:)` exists as a possible escape hatch but changes the rendering path for every test, so it is not something to adopt as a side effect of this PR. `.borderedProminent` is used instead. On iOS 26 the system already draws it as a capsule, which is the shape the hand-rolled 12pt rounded rectangle was imitating. ## Verification Local, iPhone 18 Pro Max / iOS 27.0: - `xcodebuild build-for-testing` — `TEST BUILD SUCCEEDED` - Affected suites pass: `OTPCodeDetailViewSnapshotTests`, `SecureNoteDetailViewSnapshotTests`, `VaultAutofillConfigurationViewSnapshotTests` - 43 snapshots re-recorded (18 OTP detail, 24 secure note, 1 autofill) and visually reviewed - `make format` + `make lint` — clean `SettingsDangerView`, `VaultTagDetailView`, `EncryptedItemDetailView` and `VaultDetailEncryptionEditView` have view-model tests but no snapshot coverage, so there was nothing to re-record for them.⚠️ Automatic CI is still disabled (#548), so this is local verification only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Applies the button convention from #551 to the screens outside
Views/Backup, and deletes the modifier that made the old treatmentpossible.
The problem
ProminentButtonModifierreimplemented.borderedProminentby hand:.font(.headline)+ a forced.foregroundStyle(.white)+padding(16/12).background(color)+RoundedRectangle(cornerRadius: 12). It applied.buttonStyle(.borderless)inside its own body, so the wrapped buttonnever received a pressed state, and the hardcoded white label had no
contrast guarantee against the tint it was placed on.
Six of its nine call sites also placed the button inside a
Sectionfooter as a padded, centred pill — the exact pattern #551 called out and
removed from the backup screens.
What changed
SettingsDangerViewVaultTagDetailViewVaultItemDetailViewEncryptedItemDetailViewVaultDetailEncryptionEditViewOTPCodeDetailViewSecureNoteDetailViewVaultAutofillConfigurationViewForm, so it becomes.buttonStyle(.borderedProminent)+.controlSize(.large)Destructive emphasis is preserved the way #551 preserved it: a red
FormRowglyph tile plus red row text, matchingBackupRestoreView'sImport & Override. Primary actions use the accent colour the same way
BackupKeyDecryptorViewdoes.The
ProgressViewin eachloading:branch loses its.tint(.white),which only existed because the spinner sat on a filled pill.
Toolbar Cancel buttons keep their explicit
.foregroundStyle(.red).Button(role: .cancel)does not render red in a toolbar, so dropping thetint would have been a visual regression rather than a standardization.
Liquid Glass does not survive snapshot rendering
"Continue" was first written as
.buttonStyle(.glassProminent). Thatrendered
VaultAutofillConfigurationViewas a completely blank image— not just the button, the whole hierarchy — dropping the reference from
151KB to 62KB.
Liquid Glass samples a backdrop through the render server, and the
.imagesnapshot strategy rasterises off-screen, so the effect resolvesto nothing and takes the rest of the frame with it.
Snapshotting.image(drawHierarchyInKeyWindow:)exists as a possibleescape hatch but changes the rendering path for every test, so it is not
something to adopt as a side effect of this PR.
.borderedProminentis used instead. On iOS 26 the system already drawsit as a capsule, which is the shape the hand-rolled 12pt rounded
rectangle was imitating.
Verification
Local, iPhone 18 Pro Max / iOS 27.0:
xcodebuild build-for-testing—TEST BUILD SUCCEEDEDOTPCodeDetailViewSnapshotTests,SecureNoteDetailViewSnapshotTests,VaultAutofillConfigurationViewSnapshotTestsand visually reviewed
make format+make lint— cleanSettingsDangerView,VaultTagDetailView,EncryptedItemDetailViewandVaultDetailEncryptionEditViewhave view-model tests but no snapshotcoverage, so there was nothing to re-record for them.
🤖 Generated with Claude Code