En el presente texto, haremos un login con algunas animaciones y un preloader que seguramente habrás visto en algunas webs cuando te logueas y aparece algo que demuestra que la web está cargando (animaciones por lo general).
(Click en la imagen para ir al demo)
Citar
Usuario: john.doe@gmail.com
Contraseña: abc123
Contraseña: abc123
1. INDEX
- Necesitaremos importar fontawesome y nornalize.
La estructura HTML para nuestro login es sencilla:
Código
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="css/vendor/normalize.css"/> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"/> <link rel="stylesheet" href="css/main.css"/> </head> <body> <section class="main"> <form id="loginFrm" class="form"> <section class="form-head"> </section> <section class="form-body"> <div class="group"> <input type="text" id="user" class="txt"> </div> <div class="group"> <input type="password" id="pass" class="txt"> </div> <div class="group flex-end"> </div> </section> </form> </section> <script> 'use strict'; window.addEventListener('load', function() { let main = document.querySelector('.main'); main.classList.add('visible'); }); </script> </body> </html>
La estructura es muy simple. Consta de un formulario con dos partes:
- form-head: Aquí tendremos el título del form y el pin (el círculo con el ícono de un usuario).
- form-body: Aquí tendremos todos los grupos (label/inputs) y botones.
Vemos que hay un elemento con la clase 'tooltip'. Bien, éste elemento es como su nombre lo dice, un tooltip en cual nos servirá para mostrar los errores de autenticación.
Ahora veamos el CSS. Lo primero que haremos será aplicar la propiedad box-sizinz: border-box a HTML y hacer que todos los elementos hereden de él esa propiedad.
Código
html { box-sizing: border-box; } *, *:before, *:after { box-sizing: inherit; }
Luego, aplicamos estilos al container general, es decir, main:
Código
.main { align-items: center; background: rgba(136,211,124,1); background: linear-gradient(to bottom, rgba(136,211,124,1) 0%, rgba(136,211,124,1) 50%, rgba(190,144,212,1) 51%, rgba(190,144,212,1) 71%, rgba(190,144,212,1) 100%); background-repeat: repeat; display: flex; height: 100vh; justify-content: center; opacity: 0; transition: all .2s linear; visibility: hidden; } .main.visible { opacity: 1; visibility: visible; }
Aplicamos display: flex, align-items: center y justify-content: center para centrar el formulario, un centrado perfecto. Para ésto, .main debe de tener un alto, el cual se lo especificamos en 100vh (100 view height).
También, le damos una opacidad de 0 y una visibilidad oculta (hidden), además un transition: all .2s ease-in para que al cambiar opacity a 1 y visibility a visible, el efecto sea suave y bonito. Aquí es donde toma importancia .visible, que solamente cambia la opacidad y la visibilidad de .main.
Ahora vayamos con el formulario:
Código
.form { background-color: #f7f7f7; border: none; border-radius: 2px; box-shadow: 0 2px 5px rgba(0,0,0,.25), 0 -1px 5px rgba(0,0,0,.1); position: relative; width: 350px; }
Nada relevante. Solo tiene un color blanco humo, sin bordes, un redondeado de 2px, una sombra, un ancho y lo importante, una posición relative. Esta propiedad nos permitirá situar el pin y el tooltip con relación al formulario.
Código
.form-head { align-items: center; border-radius: 2px 2px 0 0; border-bottom: 2px solid #9B59B6; display: flex; height: 45px; } .form-head > span { color: #555; font-family: 'segoe ui'; font-size: 17pt; font-weight: lighter; margin-left: 12px; }
La cabecera del formulario no tiene nada que explicar. Vayamos con el pin:
Código
.form-head .logo { background-position: center; background-repeat: no-repeat; background-image: url('http://urbita.com/img/default/default_user_256.png'); background-size: cover; border-radius: 100%; box-shadow: 0px 2px 5px rgba(0,0,0,.25); height: 85px; left: calc(50% - 38.5px); margin: 0px; position: absolute; top: calc(0% - 38.5px); width: 85px; }
El pin tiene un ancho y alto de 85px, además un border-radius de 100%, lo que le dará la forma de un círculo. Tiene una pequeña sombra con un offset Y de 2px, lo que hará que la sombra se despligue hacia abajo. Las propiedades de background permiten que la imagen se centre y que cubra el tamaño del círculo.
La parte más importante es la propiedad position: absolute. Esta instrucción nos permite posicionar el pin de acuerdo al padre. Ahora, podemos centrarlo horizontalmente con solo left: calc(50% - 38.5px) y verticalmente con top: calc(0% - 38.5px) (por cuestión de entendimiento, si preguntas porqué no hice -38px xD). Ahora tenemos en pin centrado en la cabecera. ¿Fácil no?
Ahora, hagamos algo de magia. Hagamos al pin animado, para ésto, usaremos keyframes:
Código
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(1800deg); } }
La animación se llamará spin, y el elemento que la use empezará con una rotación de 0 grados y terminará con una rotación de 1800 grados (5 vueltas completas). Ahora, el pin la usará cuando tenga la clase 'auth', que informa que se ha hecho una autenticación en el formulario:
Código
.form-head .logo.auth { animation-name: spin; animation-duration: 3000ms; animation-iteration-count: 1; animation-timing-function: linear; animation-play-state: running; animation-fill-mode: forwards; animation-delay: 0s; }
La duración será de 3 segundos. Solo iterará una vez, será a una velocidad plana (linear) y sin tardanza antes de empezar la animación.
Ahora vayamos con el cuerpo del formulario:
Código
.form-body { padding: 40px 25px 10px 25px; } .group { align-items: center; display: flex; margin-bottom: 10px; } .group > label { color: #777; display: block; flex-grow: 1; font-family: 'segoe ui'; }
El cuerpo de formulario tendrá un padding de 40px hacia arriba, 25px hacia los lados y 10px hacia abajo (lo último para contrarrestrar el margin-bottom de los grupos, que contiene los label y los input. Los labels serán en forma de bloque y con un flex-grow: 1 que hará que el label tenga un tamaño definido.
Pondremos éstas 2 clases como apoyo, la primera para alinear los elementos hacia el final del bloque, y el otro para un centrado absoluto:
Código
.flex-end { justify-content: flex-end; } .flex-abs-center { align-items: center; justify-content: center; }
Ahora vayamos con los textboxes:
Código
.txt { border: 1px solid #ddd; border-radius: 2px; color: #777; font-family: 'segoe ui'; font-size: .9rem; outline: none; padding: .35rem .5rem; transition: all .2s ease; } .txt.invalid { border-color: rgba(231,76,60,.7); } .txt:hover { border-color: #ccc; } .txt:focus { border-color: #bbb; }
Nada del otro mundo. La clase invalid solo pintará el borde del textbox de rojo. Ahora los botones:
Código
.btn { border: none; border-radius: 2px; box-shadow: 0 2px 5px rgba(0,0,0,.2); font-family: 'segoe ui'; font-size: .9rem; outline: none; padding: .45rem 1.25rem; text-transform: uppercase; transition: all .3s ease; } .btn-radical { background-color: #F62459; color: rgba(255,255,255,.9); margin-top: 15px; } .btn-radical:hover { background-color: #DC1F4E; }
Tampoco nada que destacar. Un redondeado de 2px, fuente segoe ui, una ligera sombra y un padding. btn-radical solo le da color (radical) y un margin-top de 15px.
Por último, finalizaremos con el tooltip:
Código
.form .tooltip { background-color: rgba(0,0,0,.5); border-radius: 5px; box-shadow: 0px 2px 5px rgba(0,0,0,.25); color: rgba(255,255,255,.8); font-family: 'segoe ui'; font-size: .9rem; height: 100px; left: calc(100% - 35px); margin: 0; padding: .7rem .6rem; opacity: 0; position: absolute; text-align: center; top: -140%; transition: all .4s ease-in; visibility: hidden; width: 200px; }
El tooltip tiene un alto de 200px y un ancho de 100px. Tiene una posición absoluta, lo cual nos hace ubicarlo fácilmente de acuerdo a su padre (formulario). Le damos un left para que quede tirado más a la derecha y un top negativo muy grande para desplazarlo muy arriba, además una opacidad de 1. Le damos un top muy alto porque en la transición, lo bajaremos y así da la impresión que viene aparece de la nada (por la opacidad también).
Para hacer el efecto de la flecha, jugaremos un poco con las pseudoclases :before:
Código
.form .tooltip:before { border-color: rgba(0,0,0,.5) transparent; border-style: solid; border-width: 10px 10px 0px 10px; content: ""; left: 10px; position: absolute; bottom: -10px; }[/coder] Por último, para hacerlo visible, aplicaremos una clase: [code=css] .form .tooltip.visible { opacity: 1; top: -50%; visibility: visible; }
La clase visible, cambia la opacidad del tooltip, visiblidad y le da un top de -50%. Ésto lo que genera, es un efecto que el tooltip viene de arriba y en el camino se hace visible.
Código JS
Código
'use strict'; document.addEventListener('DOMContentLoaded', function() { let loginFrm = document.querySelector('#loginFrm'); loginFrm.addEventListener('submit', function(e) { e.preventDefault(); const ANIMATION_TIMEOUT = 3000; // duration of logo animation let logo = document.querySelector('.logo'); let user = document.querySelector('#user').value.toLowerCase(); let pass = document.querySelector('#pass').value; // if logo already is animated, the info is already processing if(logo.classList.contains('auth')) { return false; } logo.classList.add('auth'); // start animation // check credentials if(user == 'john.doe@gmail.com' && pass == 'abc123') { window.setTimeout(function() { sessionStorage.setItem('full_access', false); window.location.href = 'views/home.html'; }, ANIMATION_TIMEOUT); } else { window.setTimeout(function() { logo.classList.remove('auth'); // remove animation of logo showTooltip(); }, ANIMATION_TIMEOUT); } }); // show and hide tooltip function showTooltip() { let tooltip = loginFrm.querySelector('.tooltip'); tooltip.textContent = 'El email o contraseña ingresados no son correctos. Verifique e inténtelo nuevamente.'; tooltip.classList.add('visible'); // after 4 seconds, the tooltip disappear window.setTimeout(function() { tooltip.classList.remove('visible'); }, 4000); } }); // end script
El script es muy simple. Escuchamos por evento submit del formulario y obtenemos lo que se ha ingresado en las cajas de texto; también añadimos una constante llamada ANIMATION_TIMEOUT que especifica el tiempo que durará la animación del pin.
Añadimos la clase 'auth' al pin para empezar la animación. Comprueba las credenciales y si coinciden crear una sessión para el usuario (HTML5 WebStorage) y redirige hacia home, todo ésto con un delay del mismo tiempo que dura la animación, esto es, que una vez que acaba la animación, se redigirá hacia home.
Si las credenciales no coindicen, quitamos la clase 'auth' del pin para poder volver a iniciar la animación las veces que se requiera. Además, mostramos el tooltip informando de los errores de credenciales. El mismo tooltip desaparecerá al cabo de 4 segundos.
El script embebido lo único que hace es hacer visible el index para darle el efecto de 'aparición':
Código
'use strict'; window.addEventListener('load', function() { let main = document.querySelector('.main'); main.classList.add('visible'); });
2. HOME
HTML
- Normalize.css requerido
- Prefixfree requerido
Código
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="../css/vendor/normalize.css"/> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"/> <link rel="stylesheet" href="../css/home.css"/> </head> <body> <section class="main"> <header> <nav class="navbar"> <!-- brand --> <!-- menu --> <ul class="menu"> <li> <ul> </li> </ul> </li> </ul> </nav> </header> </section> </body> </html>
El marcado es simple. Consta de un header y un nav, con una lista incluída con dos elementos: Messages y un dropdown. El dropdown contiene una lista que son las opciones del usuario logueado.
CSS
Primero hacemos todos los elementos border-box:
Código
html { box-sizing: border-box; } *, *:before, *:after { box-sizing: inherit; }
Preload:
El preload es un simple etiqueta section con un elemento i dentro, el cual será la animación. Veamos que elementos tiene:
Código
.preload { align-items: center; background-color: #F22613; display: flex; height: 100vh; justify-content: center; left: 0; opacity: 0; position: absolute; top: 0; transition: all .5s ease; visibility: hidden; width: 100%; z-index: 1; } .preload.visible { opacity: 1; visibility: visible; } .preload i { color: rgba(255,255,255,.9); }
Como vemos, preload ocupa el 100%(100vh) de alto y ancho, además es de posición absoluta y se posiciona en el vértice izquierdo, lo que junto con sus medidas hace que cubra toda la pantalla. Tiene una opcidad de 0 y una visibilidad oculta (hidden) por defecto, además se comporta como flexbox y tiene un centrado absoluto (flex-align: center y justify-content: center).
El color del ícono animado es de color blanco con una opacidad de .9 (hace que se note apenas el fondo). La clase que activa la animación, se llama visible y lo único que hace es cambiar la opacidad a 1 y la visibilidad a visible.
El elemento i es animado con la ayuda de font awesome, pero se puede hacer manualmente, quizás lo veamos en otro tutorial.
Main
El main es donde están todos nuestros elementos. Es el container general. Tiene las siguientes propiedades:
Código
.main { opacity: 0; transition: all .6s ease; visibility: hidden; } .main.visible { opacity: 1; visibility: visible; }
Creo que en este punto ya sabemos que significa... Oculto por defecto y cuando se aplica la clase visible, se hace visible con animación. Ahora veamos el header y el nav:
Código
header { background-color: #22A7F0; color: rgba(255,255,255,.9); display: block; } .navbar { align-items: center; color: inherit; display: flex; justify-content: space-between; margin: 0px; padding: 0 .5rem; width: 100%; }
Ambos tienen un ancho de 100%. El header es block y nav es flex. El nav tiene la propiedad justify-content: space-between, que hace que los hijos de alineen hacia los lados dejando todo el espacio restante del padre entre ellos. ¿Cuáles son los hijos? Son el logo de la empresa y la lista.
El logo de la empresa tiene la clase brand y tiene los siguientes estilos:
Código
.brand { align-self: stretch; align-items: center; color: inherit; display: flex; font-family: 'segoe ui'; margin: 0px; padding: 0 .25rem; } .brand > i { margin-left: .8rem; }
De entrada, podemos ver que el brand tiene la propiedd align-self: stretch. Ésta propiedad sobreescribe el comportamiento por defecto de un hijo flex en fila (por defecto display: flex) en el eje Y. Es decir, si el padre especifica un align-items: center y el hijo especifica align-self, stretch, predomina el comportamiento del hijo. El valor stretch hace que el hijo se estire ocupando todo el alto del padre.
Veamos la lista y sus elementos:
Código
.navbar ul { display: block; list-style: none; margin: 0px; padding: 0px; } .navbar > .menu { display: block; } .menu > li { display: inline-block; transition: all .1s ease-in; } .menu > li:hover { background-color: #1C99DD; }
La lista es un bloque y sus elementos son bloque en línea, lo que hace que se muestre en forma horizontal dentro de la lista. Además, tienen una transición que anima el cambio de fondo cuando se hace hover sobre ellos. Además, ellos tendrán un margen a la derecha de 55px siempre y cuando no sea el último elemento de la lista y sus hijos (elementos a) serán elementos de bloque y tendrán cierto padding:
Código
.menu > li > a { display: block; padding: 1rem .5rem; } .menu > li > a > i { margin-left: 10px; } .menu > li:not(:last-of-type) { margin: 0 55px 0 0; }
Veamos ahora, el dropdwon que serán las opciones del usuario.
Código
/* last li (profile dropdown) */ .menu > li:last-of-type { position: relative; } /* dropdown */ .menu > li:last-of-type > ul { background-color: #22A7F0; border-radius: .1725rem; left: -50%; opcity: 0; padding: .5rem 0; position: absolute; top: 170%; visibility: hidden; transition: all .14s ease-in; } /* arrow of menu */ .menu > li:last-of-type > ul:after { border-color: #22A7F0 transparent; border-style: solid; border-width: 0 8px 8px 8px; content: ""; position: absolute; right: 8px; top: -7px; } .menu > li:last-of-type:hover > ul { opacity: 1; top: 130%; visibility: visible; }
El menú de usuario (dropdown), tendrá una posición relative, lo que permitirá aplicar a su lista interna, una posición absoluta y posicionarla debajo. La lista como ya dijimos tiene posición absoluta, una opacidad de 0, visibilidad oculta (hidden), un left de -50% lo que hará que la lista se desplace a la izquierda y un top de 170% que posiciona la lista muy abajo.
Cuando se haga hover en el menú de usuario, la lista interna se hace visible (opacidad, visibility) y el top se reduce a 130%, lo que da el efecto de desplazamiento hacia arriba mientras se hace visible.
Los li y a de la lista interna, ambos son block lo que hace que se muestre en forma de columna. Además, los li tienen se animan en cuanto se haga hover sobre ellos y se cambie el color.
Código
/* dropdown list items*/ .menu > li:last-of-type > ul > li, .menu > li:last-of-type > ul a { display: block; font-size: .9rem; transition: transform .1s ease-in; } /* dropdown options */ .menu > li:last-of-type > ul a { padding: .5rem .9rem; width: 8rem; } /* a elements of dropdown options */ .menu > li:last-of-type > ul li:hover { background-color: #1998DC; } /* margin left to dropdown icon */ .menu > li:last-of-type > a > i { margin-left: .8rem; } /* dropdown options icons */ .menu li:last-of-type > ul a > i { float: right; margin-top: .3125rem; }
Por último, la sección donde irá el avatar del usuario:
Código
/* profile image */ #profile-img { display: flex; justify-content: center; margin-bottom: 5px; pointer-events: none; width: 100%; } #profile-img > img { background-position: center; background-size: cover; border-radius: 50%; display: block; height: 5rem; width: 5rem; }
La sección que contiene la imagen del usuario es un bloque tipo flex que ocupa el 100% y que alinea la imagen al centro horizontal (justify-content: center). La imagen tiene un ancho y alto de 5rem (80px) y la imagen estará centrada y ocupara todos los 80px.
El resultado:
Código JS:
Código
'use strict'; window.addEventListener('load', function() { let full_access = sessionStorage.getItem('full_access'); let preload = document.querySelector('.preload'); let main = document.querySelector('.main'); // full_access is gived when user enter for first time if(full_access == "false") { // show preload animation preload.classList.add('visible'); // animation delay 3s. After that, the main content appear window.setTimeout(function() { preload.classList.remove('visible'); main.classList.add('visible'); },3000); // change the flag. Now, the user has full access sessionStorage.setItem('full_access', true); } // if user has full_access, just show the page else { main.classList.add('visible'); } }); document.addEventListener('DOMContentLoaded', function() { document.querySelector('#logout').addEventListener('click', function(e) { e.preventDefault(); // remove user's session sessionStorage.removeItem('full_access'); // redirect to login window.location.href = '/PreloadCSS'; }); });
Primero obtenemos el item 'full_access' de la sesión. Como sabemos, en el login, lo inicializamos en false cuando se ha logueado el usuario por primera vez.Seleccionamos el preload y el main y los guardamos en variables locales.
Verificamos, si full_access es false (lo será la primera vez que se loguee) se le aplica la clase 'visible' al preload, que como recordamos, al aplicarle visible se muestra el preload y su animación.
Creamos un delay de 3 segundos (el tiempo que tarda la animación) y cuando termine removemos la clase active del preload, haciendo que se oculte y agregamos la clase visible al main, haciendo que éste se haga visible. Al final, cambiamos el valor full_access por true.
Si ya se tiene acceso total, simplemente se agrega la clase visible al main, haciendolo visible (sin mostrar el preload). Lo anterior lo aplicamos en el evento load de window.
Por último, cuando el dom esté cargado, escuchamos por evento click en la opción logout del menú de usuario, removiendo la sesión y redirigiendo al login (fíjense que éste tbn tiene un efecto).
[/code]