#4 Authenticator Inputs
Friday, October 27, 2017
In this article we will create and style the controls that we need for our authenticator component. In order to simplify the process we will learn how to create mixins in our pug files and define functions in our scss files. We will also see how Asp.Net will help us with the failure to load resources from a CDN by simplifying fallback to resources stored on our servers.
Parts
- Part 30: User Database
- Part 29: Canonical Url
- Part 28: Razor Page Handlers
- Part 27: Send Screen Finished
- Part 26: Stroke Dashoffset
- Part 25: Send Screen
- Part 24: Forgot Link Behaviour
- Part 23: Animating Controls (cont.)
- Part 22: Animating Controls
- Part 21: Hiding Control Groups
- Part 20: ModelState Errors (cont.)
- Part 19: ModelState Errors
- Part 18: Animating Info Pages (cont.)
- Part 17: Animating Info Pages
- Part 16: Keyboard Navigation
- Part 15: Accessing the DOM
- Part 14: All About the Username
- Part 13: CSRF Attacks
- Part 12: Http Requests
- Part 11: HMR
- Part 10: Color Inputs And Buttons
- Part 9: Animating Sub-Actions
- Part 8: Form Validation (cont.)
- Part 7: Form Validation
- Part 6: Form Group
- Part 5: Authenticator Validators
- Part 4: Authenticator Inputs
- Part 3: First Angular Component
- Part 2: Webpack
- Part 1: Beginning
What are we making?
As we mentioned in the previous article the first step in creating our application is to allow people to login and register. To do this we are creating an angular component for the user interface. In (a) we see what the login screen will look like and in (b) we see what the register screen will look like.


You can view a completed example of the authenticator component here.
Probably shouldn't have deleted that lib folder wayback when
-
WebUi
-
wwwroot
-
css
-
font-awesome-4.7.0
- css
- fonts
-
font-awesome-4.7.0
-
css
-
Pages
- _Layout.cshtml
-
wwwroot
During the setup of our project in the very first video one of the things that we deleted was the lib folder that was located in the wwwroot folder. While it is true that we did not need any of the contents I forgot that we would be using an icon set called Font Awesome. The primary way that we will be including the icons is through a cdn and not from our own servers but asp.net core provides us an easy way to test if a resource has been loaded from one source and if that fails to attempt to load it from another.
To be able to serve the backup copy from our server we need to visit Font Awesome and download the zip file. Once that is done we need to copy the result of extracting the contents to the 'wwwroot/css' folder (You can't make me add the lib folder back). If you would like you can of course add the lib folder back and place the font awesome folder into it. If you do just make sure that you update the fallback href in the link tag. You can leave the entire contents of the font awesome folder but there is no reason to leave the less and scss folders so we can freely delete them. Once that is done we can return to our _Layout.cshtml file and add the link tag shown in (c) to the head of the page. As I have already said this link will add code that will attempt to download the assets from the primary href and test if that succeeded. If it failed it will attempt to download it from the fallback href. In (d) we see the script that is used to determine if font awesome was loaded successfully from the primary location. It is not important that we analyze the script we just have to be aware that the fallback works by placing this script in the head tag of our browser.
_Layout.cshtml
<head>
...
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css"
asp-fallback-href="~/css/font-awesome-4.7.0/css/font-awesome.min.css"
asp-fallback-test-class="fa-lg"
asp-fallback-test-property="vertical-align"
asp-fallback-test-value="-15%" />
...
</head>
Font Awesome Fallback
<script>
!function(a, b, c, d) {
var e, f = document,
g = f.getElementsByTagName("SCRIPT"),
h = g[g.length - 1].previousElementSibling,
i = f.defaultView && f.defaultView.getComputedStyle
? f.defaultView.getComputedStyle(h)
: h.currentStyle;
if (i && i[a] !== b) for (e = 0; e < c.length; e++) f.write("<link href='" + c[e] + "' " + d + " />")
}("vertical-align", "-15%", ["\/css\/font-awesome-4.7.0\/css\/font-awesome.min.css"], "rel=\u0022stylesheet\u0022 type=\u0022text\/css\u0022 ");
</script>
I hate rounded corners
-
WebUi
-
Source
-
sass
-
2-base
- _variables.scss
-
2-base
-
sass
-
Source
I'm not completely sure why but I do not like rounded corners (e) on my ui elements. If you are with me on this we need to modify the
_variables.scss file in our 2-base folder by setting the $base-border-radius: 0;
. If on the other
hand you like rounded corners you can make this change by a case by case basis but you will need to handle the design of the inputs
and buttons which are next to each other shown in (a) and (b). The simple solution shown in
the left side of (e) being to use input {border-top-right-radius: 0; border-bottom-right-radius: 0;}
and button {border-top-left-radius: 0; border-bottom-left-radius: 0;}
.

The journey of a thousand miles begins with one step
-
WebUi
-
Source
-
components
-
authenticator
- authenticator.component.pug
- authenticator.component.scss
-
authenticator
-
components
-
Source
I will not be giving a 'play by play' for each bit of the authenticator.component.pug and authenticator.component.scss files as we modify but I will point out overall design decisions and anything else that I feel should be talked about specifically. We begin by creating an over all container for our component and dividing it in half vertically. I have decided that I would like the component to take up 1/3 of a 1080px width display which means that the max width will be 640px. I also want the height of the component to be the same for logging in as well as registering so once we have completed the design we will see the which has the largest height and set the height of the container to it.
As we modify the pug and scss files their corresponding code in our code blocks will behave differently. The pug blocks will contain the code for the whole file whereas the scss files will contain code that is to be added to any previous code. The parenthesis in the names indicate which sets should be used to create the image that follows them, if any.
authenticator.component.pug (1)
div.authenticator
div.inputs-container
h3 Login
form(novalidate)
div.form-control-group
label Username
div.input-group.invalid
input
button
i.fa.fa-times
div.input-buttons
button Reset
button(disabled="true") Send
div.info-container
authenticator.component.scss (1)
@import "../../sass/_bourbon-neat.scss";
@import "../../sass/2-base/_media-queries.scss";
$base-font-size: 16px;
$info-container-background-color: #484848;
$info-container-color: #dedede;
@function em($pixel) {
@return #{strip-unit($pixel)/strip-unit($base-font-size)}em;
}
.authenticator {
font-size: $base-font-size;
background-color: white;
margin: 0 auto;
@include media-gt-or-eq(640px) {
display: flex;
flex-direction: row;
width: em(640px);
.inputs-container, .info-container {
width: em(320px);
}
}
a {
text-decoration: underline;
text-decoration-style: ink;
color: #2593cc;
}
.inputs-container {
padding: em(10px);
}
.form-control-group {
label {
font-weight: 600;
}
}
.input-group {
display: flex;
flex-direction: row;
margin-bottom: em(12px);
input {
margin-bottom: 0;
min-width: 0;
}
button {
padding: em(12px) em(16px);
color: white;
line-height: 1;
}
}
.info-container {
display: flex;
flex-direction: column;
color: $info-container-color;
background-color: $info-container-background-color;
}
.info-pages {
flex: 1;
}
.need-to-login, .need-to-register {
text-align: right;
padding: em(10px);
a {
color: $info-container-color;
}
}
}

Our first pug mixin
-
WebUi
-
Source
-
components
-
authenticator
- authenticator.component.pug
-
authenticator
-
components
-
Source
As shown in (a) and (b) we will need several groups of elements
consisting of a label, an input and a button that I will call a form control group. One of the main reasons that we are bothering to use a language
like pug instead of html is to address these types of issues. We could of course just copy and paste things all
over the place and then be sure that we make changes to all of them but we are going to be smarter than that and use
a mixin. As shown at the top of (i) we define a mixin by simply using the
word mixin followed by a name and a pair of parenthesis that contain a comma separated list of parameters. We call the mixin by
prepending its name with a '+' and of course passing in the required parameters. In (i)
we are converting the six lines that make up our username form control group to +formControlGroup("Username")
.
authenticator.component.pug (2)
mixin formControlGroup(label)
div.form-control-group
label #{label}
div.input-group.invalid
input
button
i.fa.fa-times
div.authenticator
div.inputs-container
h3 Login
form(novalidate)
+formControlGroup("Username")
div.input-buttons
button Reset
button(disabled="true") Send
div.info-container
The rest of the controls
-
WebUi
-
Source
-
components
-
authenticator
- authenticator.component.pug
- authenticator.component.scss
-
authenticator
-
components
-
Source
Of course only having the username control isn't going to do us much good. We need to add several more controls to make the component useful. In (j) we have add five new controls: password, confirm password, email, confirm email and remember me. We have also added additional styling which will color the inputs and buttons depending on a conditional class (for demonstation we have added the invalid class for now), place pairs of controls next to each other, and created an acceptable looking remember me checkbox among several other changes.
authenticator.component.pug (3)
mixin formControlGroup(label)
div.form-control-group
label #{label}
div.input-group.invalid
input
button
i.fa.fa-times
div.authenticator
div.inputs-container
h3 Login
form(novalidate)
+formControlGroup("Username")
div.forgot
a Forgot your username?
div.dual-input-group
+formControlGroup("Password")
+formControlGroup("Confirm Password")
div.forgot
a Forgot your password?
div.dual-input-group
+formControlGroup("Email")
+formControlGroup("Confirm Email")
div.form-control-group.control-group-remember-me
input#remember-me(type="checkbox")
label(for="remember-me")
span #[i.fa]
| Remember me?
div.input-buttons
button Reset
button(disabled="true") Send
div.info-container
authenticator.component.scss (3)
$indeterminate-color: #BDC3C7 !default;
$invalid-color: #D91E18 !default;
$valid-color: #26A65B !default;
@mixin inputGroup($color) {
input, button {
border-color: $color;
&:focus {
outline: none !important;
box-shadow: 0 0 em(10px) $color;
}
}
button {
background-color: $color;
opacity: 1;
}
}
.authenticator {
.input-group {
&.indeterminate {
@include inputGroup($indeterminate-color);
}
&.invalid {
@include inputGroup($invalid-color);
}
&.valid {
@include inputGroup($valid-color);
}
input, button {
border-bottom: em(2px) solid;
transition: all 1s;
margin-bottom: 0;
}
}
.dual-input-group {
display: flex;
flex-direction: row;
.form-control-group {
flex: 1;
}
}
.forgot {
text-align: right;
}
.control-group-remember-me {
input[type="checkbox"] {
display: none;
+ label {
margin: em(10px) 0;
&:focus {
outline: none !important;
box-shadow: 0 0 em(10px) #BDC3C7;
}
}
}
input[type="checkbox"] + label span {
display: inline-block;
width: em(19px);
height: em(19px);
margin: em(-1px) em(4px) 0 0;
vertical-align: middle;
background-color: white;
cursor: pointer;
position: relative;
border: em(1px) solid $info-container-color;
i {
position: absolute;
left: em(1px);
top: 0;
}
}
input[type="checkbox"]:checked + label span {
i:before {
font-family: FontAwesome;
content: "\f00c";
}
}
}
}

In this article we have defined the general layout of our component and added the controls along with their styles to their pug and scss files. In the next article we will add and style the 'info' or right hand side of our user interface.