SethCalkins
12/12/2014 - 10:58 PM

Minimal Form Interface - Single Input

Minimal Form Interface - Single Input

@import url(http://fonts.googleapis.com/css?family=Lato:400,700,400italic);
.simform {
	position: relative;
	margin: 0 auto;
	padding: 2em 0;
	max-width: 860px;
	width: 100%;
	text-align: left;
	font-size: 2.5em;
}

.simform .submit {
	display: none;
}

/* Question list style */
.simform ol {
	margin: 0;
	padding: 0;
	list-style: none;
	position: relative;
	-webkit-transition: height 0.4s;
	transition: height 0.4s;
}

.simform ol:before {
	content: '';
	background-color: rgba(0,0,0,0.1);
	position: absolute;
	left: 0;
	bottom: 0;
	width: 100%;
	height: 2.35em;
}

.questions li {
	z-index: 100;
	position: relative;
	visibility: hidden;
	height: 0;
	-webkit-transition: visibility 0s 0.4s, height 0s 0.4s;
	transition: visibility 0s 0.4s, height 0s 0.4s;
}

.questions li.current,
.no-js .questions li {
	visibility: visible;
	height: auto;
	-webkit-transition: none;
	transition: none;
}

/* Labels */
.questions li > span {
	display: block;
	overflow: hidden;
}

.questions li > span label {
	display: block;
	-webkit-transition: -webkit-transform 0.4s;
	transition: transform 0.4s;
	-webkit-transform: translateY(-100%);
	transform: translateY(-100%);
}

.questions li.current > span label,
.no-js .questions li > span label {
	-webkit-transition: none;
	transition: none;
	-webkit-transform: translateY(0);
	transform: translateY(0);
}

.show-next .questions li.current > span label {
	-webkit-animation: moveUpFromDown 0.4s both;
	animation: moveUpFromDown 0.4s both;
}

@-webkit-keyframes moveUpFromDown {
	from { -webkit-transform: translateY(100%); }
	to { -webkit-transform: translateY(0); }
}

@keyframes moveUpFromDown {
	from { transform: translateY(100%); }
	to { transform: translateY(0); }
}

/* Input field */
.questions input {
	display: block;
	margin: 0.3em 0 0 0;
	padding: 0.5em 1em 0.5em 0.7em;
	width: calc(100% - 2em);
	border: none;
	background: transparent;
	color: rgba(0,0,0,0.8);
	font-size: 1em;
	line-height: 1;
	opacity: 0;
	-webkit-transition: opacity 0.3s;
	transition: opacity 0.3s;
}

.questions .current input,
.no-js .questions input {
	opacity: 1;
}

.questions input:focus,
.simform button:focus {
	outline: none;
}

/* Next question button */
.next {
	position: absolute;
	right: 0;
	bottom: 2.15em; /* padding-bottom of form plus progress bar height */
	display: block;
	padding: 0;
	width: 2em;
	height: 2em;
	border: none;
	background: none;
	color: rgba(0,0,0,0.4);
	text-align: center;
	opacity: 0;
	z-index: 100;
	cursor: pointer;
	-webkit-transition: -webkit-transform 0.3s, opacity 0.3s;
	transition: transform 0.3s, opacity 0.3s;
	-webkit-transform: translateX(-20%);
	transform: translateX(-20%);
	pointer-events: none;
	-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

.next:hover {
	color: rgba(0,0,0,0.5);
}

.next::after {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	text-transform: none;
	font-weight: normal;
	font-style: normal;
	font-variant: normal;
	line-height: 2;
	speak: none;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

.next.show {
	opacity: 1;
	-webkit-transform: translateX(0);
	transform: translateX(0);
	pointer-events: auto;
}

/* Progress bar */
.simform .progress {
	width: 0%;
	height: 0.15em;
	background: rgba(0,0,0,0.3);
	-webkit-transition: width 0.4s ease-in-out;
	transition: width 0.4s ease-in-out;
}

.simform .progress::before {
	position: absolute;
	top: auto;
	width: 100%;
	height: inherit;
	background: rgba(0,0,0,0.05);
	content: '';
}

/* Number indicator */
.simform .number {
	position: absolute;
	right: 0;
	overflow: hidden;
	margin: 0.4em 0;
	width: 3em;
	font-weight: 700;
	font-size: 0.4em;
}

.simform .number:after {
	position: absolute;
	left: 50%;
	content: '/';
	opacity: 0.4;
	-webkit-transform: translateX(-50%);
	transform: translateX(-50%);
}

.simform .number span {
	float: right;
	width: 40%;
	text-align: center;
}

.simform .number .number-current {
	float: left;
}

.simform .number-next {
	position: absolute;
	left: 0;
}

.simform.show-next .number-current {
	-webkit-transition: -webkit-transform 0.4s;
	transition: transform 0.4s;
	-webkit-transform: translateY(-100%);
	transform: translateY(-100%);
}

.simform.show-next .number-next {
	-webkit-animation: moveUpFromDown 0.4s both;
	animation: moveUpFromDown 0.4s both;
}

/* Error and final message */
.simform .error-message,
.simform .final-message {
	position: absolute;
	visibility: hidden;
	opacity: 0;
	-webkit-transition: opacity 0.4s;
	transition: opacity 0.4s;
}

.simform .error-message {
	padding: 0.4em 3.5em 0 0;
	width: 100%;
	color: rgba(0,0,0,0.7);
	font-style: italic;
	font-size: 0.4em;
}

.final-message {
	top: 50%;
	left: 0;
	padding: 0.5em;
	width: 100%;
	text-align: center;
	-webkit-transform: translateY(-50%);
	transform: translateY(-50%);
}

.error-message.show,
.final-message.show {
	visibility: visible;
	opacity: 1;
}

.final-message.show {
	-webkit-transition-delay: 0.5s;
	transition-delay: 0.5s;
}

/* Final hiding of form / showing message */
.simform-inner.hide {
	visibility: hidden;
	opacity: 0;
	-webkit-transition: opacity 0.3s, visibility 0s 0.3s;
	transition: opacity 0.3s, visibility 0s 0.3s;
}

/* No JS Fallback */
.no-js .simform {
	font-size: 1.75em;
}

.no-js .questions li {
	padding: 0 0 2em;
}

.no-js .simform .submit {
	display: block;
	float: right;
	padding: 10px 20px;
	border: none;
	background: rgba(0,0,0,0.3);
	color: rgba(0,0,0,0.4);
}

.no-js .simform .controls {
	display: none;
}

/* Remove IE clear cross */
input[type=text]::-ms-clear {
    display: none;
}

/* Adjust form for smaller screens */
@media screen and (max-width: 44.75em) {
	.simform {
		font-size: 1.8em;
	}
}

@media screen and (max-width: 33.5625em) {
	.simform {
		font-size: 1.2em;
	}
}

*, *:after, *:before { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
.clearfix:before, .clearfix:after { content: ''; display: table; }
.clearfix:after { clear: both; }

body {
	background: #26C281;
	color: rgba(0,0,0,0.45);
	font-size: 100%;
	line-height: 1.25;
	font-family: 'Lato', Arial, sans-serif;
}

a {
	color: rgba(0,0,0,0.25);
	text-decoration: none;
	outline: none;
}

a:hover, a:focus {
	color: rgba(0,0,0,0.6);
}

.codrops-header {
	margin: 0 auto;
	padding: 3em 1em;
	text-align: center;
	color: rgba(0,0,0,0.35);
}

.codrops-header h1 {
	margin: 0;
	font-weight: 400;
	font-size: 2.5em;
}

.codrops-header h1 span {
	display: block;
	padding: 0 0 0.6em 0.1em;
	font-size: 0.6em;
	opacity: 0.7;
}

/* To Navigation Style */
.codrops-top {
	width: 100%;
	text-transform: uppercase;
	font-weight: 700;
	font-size: 0.69em;
	line-height: 2.2;
}

.codrops-top a {
	display: inline-block;
	padding: 0 1em;
	text-decoration: none;
	letter-spacing: 1px;
}

.codrops-top span.right {
	float: right;
}

.codrops-top span.right a {
	display: block;
	float: left;
}

section {
	padding: 5em 2em 10em;
	background: #2dcb89;
	text-align: center;
}

section.related {
	padding: 3em 1em 4em;
	background: #465650;
	color: rgba(0,0,0,0.4);
	font-size: 1.5em;
}

.related > a {
	max-width: 80%;
	border: 2px solid rgba(0,0,0,0.3);
	display: inline-block;
	text-align: center;
	margin: 20px 10px;
	padding: 25px;
	-webkit-transition: color 0.3s, border-color 0.3s;
	transition: color 0.3s, border-color 0.3s;
}

.related a:hover {
	border-color: rgba(0,0,0,0.6);
}

.related a img {
	max-width: 100%;
	opacity: 0.4;
	-webkit-transition: opacity 0.3s;
	transition: opacity 0.3s;
}

.related a:hover img,
.related a:active img {
	opacity: 1;
}

.related a h3 {
	margin: 0;
	padding: 0.5em 0 0.3em;
	max-width: 300px;
	font-weight: 400;
	font-size: 0.75em;
	text-align: left;
}

@media screen and (max-width: 44.75em) {
	section { padding: 1em 2em; }
}

@media screen and (max-width: 25em) {
	.codrops-header { font-size: 0.8em; }
	section.related { font-size: 1.2em; }
	.codrops-icon span { display: none; }
}
/*!
 * classie - class helper functions
 * from bonzo https://github.com/ded/bonzo
 * 
 * classie.has( elem, 'my-class' ) -> true/false
 * classie.add( elem, 'my-new-class' )
 * classie.remove( elem, 'my-unwanted-class' )
 * classie.toggle( elem, 'my-class' )
 */

/*jshint browser: true, strict: true, undef: true */
/*global define: false */

( function( window ) {

'use strict';

// class helper functions from bonzo https://github.com/ded/bonzo

function classReg( className ) {
  return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
}

// classList support for class management
// altho to be fair, the api sucks because it won't accept multiple classes at once
var hasClass, addClass, removeClass;

if ( 'classList' in document.documentElement ) {
  hasClass = function( elem, c ) {
    return elem.classList.contains( c );
  };
  addClass = function( elem, c ) {
    elem.classList.add( c );
  };
  removeClass = function( elem, c ) {
    elem.classList.remove( c );
  };
}
else {
  hasClass = function( elem, c ) {
    return classReg( c ).test( elem.className );
  };
  addClass = function( elem, c ) {
    if ( !hasClass( elem, c ) ) {
      elem.className = elem.className + ' ' + c;
    }
  };
  removeClass = function( elem, c ) {
    elem.className = elem.className.replace( classReg( c ), ' ' );
  };
}

function toggleClass( elem, c ) {
  var fn = hasClass( elem, c ) ? removeClass : addClass;
  fn( elem, c );
}

var classie = {
  // full names
  hasClass: hasClass,
  addClass: addClass,
  removeClass: removeClass,
  toggleClass: toggleClass,
  // short names
  has: hasClass,
  add: addClass,
  remove: removeClass,
  toggle: toggleClass
};

// transport
if ( typeof define === 'function' && define.amd ) {
  // AMD
  define( classie );
} else {
  // browser global
  window.classie = classie;
}

})( window );





/**
 * stepsForm.js v1.0.0
 * http://www.codrops.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 * 
 * Copyright 2014, Codrops
 * http://www.codrops.com
 */
;( function( window ) {
	
	'use strict';

	var transEndEventNames = {
			'WebkitTransition': 'webkitTransitionEnd',
			'MozTransition': 'transitionend',
			'OTransition': 'oTransitionEnd',
			'msTransition': 'MSTransitionEnd',
			'transition': 'transitionend'
		},
		transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ],
		support = { transitions : Modernizr.csstransitions };

	function extend( a, b ) {
		for( var key in b ) { 
			if( b.hasOwnProperty( key ) ) {
				a[key] = b[key];
			}
		}
		return a;
	}

	function stepsForm( el, options ) {
		this.el = el;
		this.options = extend( {}, this.options );
  		extend( this.options, options );
  		this._init();
	}

	stepsForm.prototype.options = {
		onSubmit : function() { return false; }
	};

	stepsForm.prototype._init = function() {
		// current question
		this.current = 0;

		// questions
		this.questions = [].slice.call( this.el.querySelectorAll( 'ol.questions > li' ) );
		// total questions
		this.questionsCount = this.questions.length;
		// show first question
		classie.addClass( this.questions[0], 'current' );
		
		// next question control
		this.ctrlNext = this.el.querySelector( 'button.next' );

		// progress bar
		this.progress = this.el.querySelector( 'div.progress' );
		
		// question number status
		this.questionStatus = this.el.querySelector( 'span.number' );
		// current question placeholder
		this.currentNum = this.questionStatus.querySelector( 'span.number-current' );
		this.currentNum.innerHTML = Number( this.current + 1 );
		// total questions placeholder
		this.totalQuestionNum = this.questionStatus.querySelector( 'span.number-total' );
		this.totalQuestionNum.innerHTML = this.questionsCount;

		// error message
		this.error = this.el.querySelector( 'span.error-message' );
		
		// init events
		this._initEvents();
	};

	stepsForm.prototype._initEvents = function() {
		var self = this,
			// first input
			firstElInput = this.questions[ this.current ].querySelector( 'input' ),
			// focus
			onFocusStartFn = function() {
				firstElInput.removeEventListener( 'focus', onFocusStartFn );
				classie.addClass( self.ctrlNext, 'show' );
			};

		// show the next question control first time the input gets focused
		firstElInput.addEventListener( 'focus', onFocusStartFn );

		// show next question
		this.ctrlNext.addEventListener( 'click', function( ev ) { 
			ev.preventDefault();
			self._nextQuestion(); 
		} );

		// pressing enter will jump to next question
		document.addEventListener( 'keydown', function( ev ) {
			var keyCode = ev.keyCode || ev.which;
			// enter
			if( keyCode === 13 ) {
				ev.preventDefault();
				self._nextQuestion();
			}
		} );

		// disable tab
		this.el.addEventListener( 'keydown', function( ev ) {
			var keyCode = ev.keyCode || ev.which;
			// tab
			if( keyCode === 9 ) {
				ev.preventDefault();
			} 
		} );
	};

	stepsForm.prototype._nextQuestion = function() {
		if( !this._validade() ) {
			return false;
		}

		// check if form is filled
		if( this.current === this.questionsCount - 1 ) {
			this.isFilled = true;
		}

		// clear any previous error messages
		this._clearError();

		// current question
		var currentQuestion = this.questions[ this.current ];

		// increment current question iterator
		++this.current;

		// update progress bar
		this._progress();

		if( !this.isFilled ) {
			// change the current question number/status
			this._updateQuestionNumber();

			// add class "show-next" to form element (start animations)
			classie.addClass( this.el, 'show-next' );

			// remove class "current" from current question and add it to the next one
			// current question
			var nextQuestion = this.questions[ this.current ];
			classie.removeClass( currentQuestion, 'current' );
			classie.addClass( nextQuestion, 'current' );
		}

		// after animation ends, remove class "show-next" from form element and change current question placeholder
		var self = this,
			onEndTransitionFn = function( ev ) {
				if( support.transitions ) {
					this.removeEventListener( transEndEventName, onEndTransitionFn );
				}
				if( self.isFilled ) {
					self._submit();
				}
				else {
					classie.removeClass( self.el, 'show-next' );
					self.currentNum.innerHTML = self.nextQuestionNum.innerHTML;
					self.questionStatus.removeChild( self.nextQuestionNum );
					// force the focus on the next input
					nextQuestion.querySelector( 'input' ).focus();
				}
			};

		if( support.transitions ) {
			this.progress.addEventListener( transEndEventName, onEndTransitionFn );
		}
		else {
			onEndTransitionFn();
		}
	}

	// updates the progress bar by setting its width
	stepsForm.prototype._progress = function() {
		this.progress.style.width = this.current * ( 100 / this.questionsCount ) + '%';
	}

	// changes the current question number
	stepsForm.prototype._updateQuestionNumber = function() {
		// first, create next question number placeholder
		this.nextQuestionNum = document.createElement( 'span' );
		this.nextQuestionNum.className = 'number-next';
		this.nextQuestionNum.innerHTML = Number( this.current + 1 );
		// insert it in the DOM
		this.questionStatus.appendChild( this.nextQuestionNum );
	}

	// submits the form
	stepsForm.prototype._submit = function() {
		this.options.onSubmit( this.el );
	}

	// TODO (next version..)
	// the validation function
	stepsForm.prototype._validade = function() {
		// current questionВґs input
		var input = this.questions[ this.current ].querySelector( 'input' ).value;
		if( input === '' ) {
			this._showError( 'EMPTYSTR' );
			return false;
		}

		return true;
	}

	// TODO (next version..)
	stepsForm.prototype._showError = function( err ) {
		var message = '';
		switch( err ) {
			case 'EMPTYSTR' : 
				message = 'Please fill the field before continuing';
				break;
			case 'INVALIDEMAIL' : 
				message = 'Please fill a valid email address';
				break;
			// ...
		};
		this.error.innerHTML = message;
		classie.addClass( this.error, 'show' );
	}

	// clears/hides the current error message
	stepsForm.prototype._clearError = function() {
		classie.removeClass( this.error, 'show' );
	}

	// add to global namespace
	window.stepsForm = stepsForm;

})( window );





var theForm = document.getElementById( 'theForm' );

			new stepsForm( theForm, {
				onSubmit : function( form ) {
					// hide form
					classie.addClass( theForm.querySelector( '.simform-inner' ), 'hide' );

					/*
					form.submit()
					or
					AJAX request (maybe show loading indicator while we don't have an answer..)
					*/

					// let's just simulate something...
					var messageEl = theForm.querySelector( '.final-message' );
					messageEl.innerHTML = 'Thank you! We\'ll be in touch.';
					classie.addClass( messageEl, 'show' );
				}
			} );
<div class="container">
			<header class="codrops-header">
				<h1>Minimal Form Interface <span>Simplistic, single input view form</span></h1>	
			</header>
			<section>
				<form id="theForm" class="simform" autocomplete="off">
					<div class="simform-inner">
						<ol class="questions">
							<li>
								<span><label for="q1">What's your favorite movie?</label></span>
								<input id="q1" name="q1" type="text"/>
							</li>
							<li>
								<span><label for="q2">Where do you live?</label></span>
								<input id="q2" name="q2" type="text"/>
							</li>
							<li>
								<span><label for="q3">What time do you got to work?</label></span>
								<input id="q3" name="q3" type="text"/>
							</li>
							<li>
								<span><label for="q4">How do you like your veggies?</label></span>
								<input id="q4" name="q4" type="text"/>
							</li>
							<li>
								<span><label for="q5">What book inspires you?</label></span>
								<input id="q5" name="q5" type="text"/>
							</li>
							<li>
								<span><label for="q6">What's your profession?</label></span>
								<input id="q6" name="q6" type="text"/>
							</li>
						</ol><!-- /questions -->
						<button class="submit" type="submit">Send answers</button>
						<div class="controls">
							<button class="next fa fa-long-arrow-right""></button>
							<div class="progress"></div>
							<span class="number">
								<span class="number-current"></span>
								<span class="number-total"></span>
							</span>
							<span class="error-message"></span>
						</div><!-- / controls -->
					</div><!-- /simform-inner -->
					<span class="final-message"></span>
				</form><!-- /simform -->			
			</section>
		
		</div>