How I managed to customize AWS Amplify login screens

Custom Login/Sign Up styling for Amplify Vue & Cognito

Bryce Howitson
5 min readFeb 11, 2019

I’m an interface designer who does a lot of CSS and some Javascript for the apps I design. I’ve been struggling (read, bashing my head against a wall) to customize the visual style/layout of AWS Amplify Vue’s Cognito authentication flow for several months across multiple projects.

Now, I completely understand why the development community loves Amplify as it makes interacting with AWS SDKs much easier. Amplify also produces something accessible with an interface that works pretty well out of the box. Unfortunately, as a designer, I find the default styling horrific not to mention that it doesn’t even remotely match the brands for which I’m creating apps.

So I rolled up my sleeves and started punching. I’m happy to report that I’ve finally beat Amplify’s defaults into submission, well mostly.

Note, my project was built in Vue.js but the same process should work for Angular until Amplify components (and for other similar integrations) are updated for both frameworks.

Default on the left and my customized version on the right. Apparently, Amplify is still fighting with my existing App CSS even before I take on customizations.

Where I landed:

This isn’t a perfect solution, but I estimate about 80% success in my ability to change Amplify’s pre-baked interface to match my designs.

What can be changed: Colors, layout, and any content that wraps around the login/signup fields. Basically the stuff you can do with CSS and containers.

What can’t be changed: Form field order, label text, placeholder text, validation, or error messages as they’re part of the rendered HTML.

Note: Some of this solution applies to AWS-Amplify-Vue specifically but the styling portions should work for React, Angular, etc.

Problems I encountered

The major problem when it comes to modifying layout is CSS selectors. The Amplify Vue package contains modularized CSS from Webpack. This means classes/ids look something like this: class="form__input__xFAr73" Each time the library is recompiled with Webpack the string at the end of the class changes. So it's a bad idea to write CSS to override these classes because they could change unexpectedly. CSS Modules force class names to be highly specific (the intent is to keep included CSS from fighting with your app CSS). That’s usually a very good idea, but it also makes it more difficult to override the styling that comes pre-made with Amplify.

Second, I wanted to add branding content around the individual forms. Obviously, we’re doing this without modifying the Amplify templates to ensure their functionality remains, so it was difficult to achieve much branding when the Amplify views represented a full route (think individual screen not part of another screen).

Finally, I would like to modify what form fields are called and change where/how validation messages appear. As far as I can tell, you are able to modify some of the sign-up form rendering via configuration but the other screens are off-limits. Unfortunately, I wasn’t able to achieve this last bit entirely, but the pre-built Amplify forms are actually pretty good.

Steps to customize Amplify Vue’s Cognito authentication flow

1. Create a new component

Create a new view/component to contain Amplify components and bus (render decisions like Vue Router). This allows custom content like branding to appear in the context of the login or sign up.

  • To make sure I had everything, I basically copied all the component code from /node-modules/aws-amplify-vue/src/components/authenticator/Authenticator.vue into my new component
  • You’ll need to change the imports to reference the packageimport { components, AmplifyEventBus, AmplifyPlugin } from ‘aws-amplify-vue’;
  • Now you can add additional content inside the template to show for EVERY login screen or use v-if to show content if specific components are rendered. For example, if you want something to only show on the sign-in screen you could do this: <h2 v-if="displayMap.showSignIn">Please Login</h2>
  • Don’t forget to update the Router to your new component.

2. Improve styling by adding IDs & Classes to your component

Wrap Amplify components with an ID to increase CSS Specificity. In practical terms, it means it's easier to override existing styling without relying on !important to win.

  • I gave the first div inside template an id of “auth”
  • Since we might want to change styles based on the screen I also added logic to change the class based on which content is displaying. Notice how this relies on Amplify’s map the same way showing components does.

3. Override styling with defaults

Reset Amplify styling back to browser defaults. You can skip this step but it means finding every attribute that differs from your design and needs an override. This is really pretty easy with some newish CSS but be warned it doesn’t work across all browsers ever.

At first, I tried css all:initial on everything but it turned out to be a pretty nuclear option. Things like div’s didn’t retain block-level status and even my CSS Reset was overridden. So… a final solution was to manually reset most attributes of div and span and then a much harder reset on input, button, and anchor tags. I also decided to hide any Labels as I couldn’t change the text on most fields.

4. Decide what to change

Once you know what elements are called, use pattern matching in CSS selectors to target prefixes in class names, thus avoiding changes when Amplify updates and run’s Webpack. I opened up the web inspector to see what I needed to style.

Notice, how messy all that stuff is. I was able to style most of my app with only a few of the selectors and :nth-child() Your mileage may vary. Most of what I wanted to change were the Form__formField___2DWht divs. I struggled with the best way to reference those elements without worrying if they changed. I finally remembered that you can pattern match selectors in CSS so I referenced the above like this: [class^=”Form__formField”] {attribute: value} This is called “Substring Matching” and proved to be super useful in this case. Here’s a good explanation of how they work.

Conclusion

Once I had a wrapper with an easily accessible ID, classes denoting the current content, and access to the CSS Modularized elements, the styling was pretty straight forward. Hopefully, this write-up saves someone else all the headaches I experienced.

If you like this, please give it applause or share it with others. If you take issue with something I’ve said, leave a comment or response so we can discuss.

By day, I’m a product strategist consulting on all things digital and a Design Sprint Master. By night I’m a Google Expert, a startup mentor and writing a book about getting started in UX. Follow me on Twitter @howitson.

--

--

Bryce Howitson

I’m a designer, I help teams create great customer experiences in the digital space. Google Expert (UI/UX/Prod) http://brycehowitson.com