Modals Are An Experience Antipattern

"An antipattern is just like a pattern, except that instead of a solution it gives something that looks superficially like a solution, but isn't one." ~ Linda Rising - The Patterns Handbook wikipedia.org/wiki/Anti-pattern

9 times out of 10 when designers/egineers employ a modal in an App. they are doing so inappropriately and inadvertently making the experience/interface worse for the person.

If you are unfamiliar with them, a good place to learn is: bootstrap.com/modal

bootstrap-modal

A modal overlays information on top of what is already displayed on the page. It hijacks the person's focus to what the dev wants them to see and requires the person to manually dismiss it before being allowed to resume what they were doing. This is an unwelcome interruption to the flow and a generally horrible experience.

Modals are a relic of the old web where ads hijacked people's screens with pop-ups:

pop-up-ads-90s

See: wikipedia.org/Pop-up_ad

The situation got completely out-of-hand and made browsing the web a horrible experience. All modern browsers block pop-ups by default now but designers/devs who don't respect the people using their site/app still reach for modals for hijacking attention.

modal-ad

Modals are almost never a good way of displaying information.

Modals in Phoenix?

Phoenix 1.7 added the new modal component.

We really wish the creators of Phoenix had not done it because it will inevitably be misused. Naive devs who mean well but haven't studied UX, will use a modal when a basic <div> would be considerably better.

It is used by default for inserting new content:

phoenix-modal-new-item

This is a horrible experience. The New Item modal overlays the Listing Items page instead of just showing a New Item page. This adds absolutely no value to the person inputing the item; it's just a distraction. This shows that the devs building Phoenix have not done very much UX/Usability testing because this would immediately confuse an older less tech-savvy person. They would ask is this page "Listing Items" or is it allowing me to create a "New Item"? And if they accidentally clicked/tapped on the "X" they would lose the text they inputted. Horrible.

Quick Example

After executing the mix phx.gen.auth command and then running the project with:

mix phx.server

The following 2 links are added to the header of home page: phoenix-homepage-with-register-login-links

The code that creates these links is: /lib/auth_web/components/layouts/root.html.heex#L15-L55

Clicking on the register link we navigate to: http://localhost:4000/people/register image

Here we can enter an Email and Password to and click on the Create an account button: phoenix-auth-register-account

This redirects us back to the homepage http://localhost:4000/ and we see the following modal: phoenix-auth-register-success-modal

The person viewing this screen has to manually dismiss the modal in order to see the content that is relevant to them:

phoenix-auth-email-in-header

Inspecting the DOM of the page in our browser: image

We see that the <div id="flash" has the following HTML:

<div id="flash" phx-mounted="[[&quot;show&quot;,{&quot;display&quot;:null,&quot;time&quot;:200,&quot;to&quot;:&quot;#flash&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-out&quot;,&quot;duration-300&quot;],[&quot;opacity-0&quot;,&quot;translate-y-4&quot;,&quot;sm:translate-y-0&quot;,&quot;sm:scale-95&quot;],[&quot;opacity-100&quot;,&quot;translate-y-0&quot;,&quot;sm:scale-100&quot;]]}]]" phx-click="[[&quot;push&quot;,{&quot;event&quot;:&quot;lv:clear-flash&quot;,&quot;value&quot;:{&quot;key&quot;:&quot;info&quot;}}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#flash&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;,&quot;translate-y-0&quot;,&quot;sm:scale-100&quot;],[&quot;opacity-0&quot;,&quot;translate-y-4&quot;,&quot;sm:translate-y-0&quot;,&quot;sm:scale-95&quot;]]}]]" role="alert" class="fixed hidden top-2 right-2 w-80 sm:w-96 z-50 rounded-lg p-3 shadow-md shadow-zinc-900/5 ring-1 bg-emerald-50 text-emerald-800 ring-emerald-500 fill-cyan-900" style="display: block;">
  <p class="flex items-center gap-1.5 text-[0.8125rem] font-semibold leading-6">
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="h-4 w-4" fill="currentColor" viewBox="0 0 20 20">
        <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd"></path>
    </svg>
    Success!
  </p>
  <p class="mt-2 text-[0.8125rem] leading-5">Person created successfully.</p>
  <button type="button" class="group absolute top-2 right-1 p-2" aria-label="close">
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="h-5 w-5 stroke-current opacity-40 group-hover:opacity-70" fill="currentColor" viewBox="0 0 24 24">
        <path fill-rule="evenodd" d="M5.47 5.47a.75.75 0 011.06 0L12 10.94l5.47-5.47a.75.75 0 111.06 1.06L13.06 12l5.47 5.47a.75.75 0 11-1.06 1.06L12 13.06l-5.47 5.47a.75.75 0 01-1.06-1.06L10.94 12 5.47 6.53a.75.75 0 010-1.06z" clip-rule="evenodd"></path>
    </svg>
  </button>
</div>

This is a lot of code just to inform the person that the successfully registered.

We know there's a much better way. We will be demonstrating it in the next few pages.

For now, we're just going to remove the line:

<.flash_group flash={@flash} />

From the file: /lib/auth_web/components/layouts/app.html.heex#L40

To remove all flash modals.

And just like magic, the tests that were failing previously, because the modal (noise) was covering the email address, now pass:

mix test 

Compiling 2 files (.ex)
The database for Auth.Repo has already been created

19:08:47.892 [info] Migrations already up
..........................................................
.........................................................
Finished in 0.5 seconds (0.3s async, 0.1s sync)
115 tests, 0 failures

With all tests passing, we can finally get on with building Auth!