diff --git a/components/contacts/__tests__/contact-form.test.tsx b/components/contacts/__tests__/contact-form.test.tsx
index e6c6570a0..ef207dbdc 100644
--- a/components/contacts/__tests__/contact-form.test.tsx
+++ b/components/contacts/__tests__/contact-form.test.tsx
@@ -50,6 +50,22 @@ describe('ContactForm', () => {
expect(onSave).not.toHaveBeenCalled();
});
+ // The browser's own "Please include an '@'" bubble sat over the app's
+ // inline message: the form leaves checking to the app.
+ it('says an address is wrong itself, with no browser bubble in the way', async () => {
+ const onSave = vi.fn();
+ render();
+ const form = screen.getByText('save').closest('form')!;
+ expect(form.noValidate).toBe(true);
+
+ fireEvent.change(screen.getByDisplayValue('alice@example.com'), { target: { value: 'alice@' } });
+ // A click on Save, as the user does: the browser checks a form that
+ // has no noValidate before it lets the submit through.
+ fireEvent.click(screen.getByRole('button', { name: 'save' }));
+ expect(await screen.findByText('email_invalid')).toBeInTheDocument();
+ expect(onSave).not.toHaveBeenCalled();
+ });
+
it('adds email entry when add button is clicked', () => {
render();
const emailInputsBefore = screen.getAllByPlaceholderText('email_placeholder');
diff --git a/components/contacts/contact-form.tsx b/components/contacts/contact-form.tsx
index b2ebe7b04..ebfe802a0 100644
--- a/components/contacts/contact-form.tsx
+++ b/components/contacts/contact-form.tsx
@@ -693,7 +693,9 @@ export function ContactForm({ contact, addressBooks, allKeywords, defaultAddress
const previewEmail = emails.find(e => e.address.trim())?.address.trim() || "";
return (
-