<div class="in-touch">
<h2 class="in-touch__title">Get In Touch</h2>
<div role="form" class="wpcf7">
<div class="screen-reader-response"></div>
<form action="" class="wpcf7-form inline-form" id="js-inline-form">
<p>Hello, <span id="title" class="inline-form__label"> my name is </span> <span class="inline-form__input-wrap"> <span aria-labelledby="title" role="textbox" class="inline-form__input" contenteditable=""></span> </span> and you can <span id="email" class="inline-form__label"> email me at </span> <span class="inline-form__input-wrap"> <span aria-labelledby="email" role="textbox" class="inline-form__input" contenteditable=""></span> </span>. I am writing you today because <span id="interest" class="inline-form__label"> I'm interested in </span> <span class="inline-form__input-wrap"> <span aria-labelledby="interest" role="textbox" class="inline-form__input" contenteditable=""></span> </span>.
<br>
<input type="submit" value="Email that!" class="wpcf7-form-control wpcf7-submit inline-form__submit"><span class="ajax-loader"></span>
<input type="hidden" name="title" value="" class="wpcf7-form-control wpcf7-hidden">
<input type="hidden" name="email" value="" class="wpcf7-form-control wpcf7-hidden">
<input type="hidden" name="interest" value="" class="wpcf7-form-control wpcf7-hidden">
<br>
</p>
</form>
</div>
</div>
/*------------------------------------*\
::Colors
\*------------------------------------*/
// primary colors
$color-1: #00caa9;
$color-1--light: #19ffda;
$color-1--dark: #01987f;
$color-1--darker: #215d6e;
// drab gray colors
$color-drab: #515b5c;
$color-drab--light: #687273;
$color-drab--lighter: #a0abac;
$color-drab--dark: #3e4748;
$color-drab--darker: #262626;
// alert colors
$color--good: $color-1--light;
$color--bad: #DE5D5D;
$color--meh: #EBE857;
/*------------------------------------*\
::Fonts
\*------------------------------------*/
// font families
$fam-1: 'Vollkorn', serif;
$fam-2: 'Signika', sans-serif;
/*------------------------------------*\
::Layout
\*------------------------------------*/
// gaps and padding
$pad: 0.9375rem;
$gap: $pad;
$gap--baby: $pad*2.7;
$gap--big: $pad*3.6;
/*------------------------------------*\
::Extends
\*------------------------------------*/
%blockquote {
position: relative;
&:before,
&:after {
display: inline;
position: absolute;
height: .7em;
color: $color-drab;
}
&:before {
content: "\201C";
left: -.4em;
top: -.3em;
font-size: 5em;
}
&:after {
content: "\201D";
right: -.4em;
bottom: -.2em;
font-size: 5em;
}
}
%btn {
position: relative;
display: inline-block;
padding: 0.5em 1em;
margin-bottom: .1em;
background-color: $color-drab--lighter;
border: 0;
text-decoration: none;
text-align: center;
font-family: $fam-2;
color: #fff;
transition: box-shadow 300ms, opacity 300ms;
box-shadow: 7px 0 0 0 $color-drab inset;
// &, /*DEBUG*/
&:hover,
&:focus {
outline: 0;
box-shadow: -7px 0 0 0 $color-1 inset;
}
&:disabled {
opacity: .5;
cursor: default;
}
}
%btn--dark {
background-color: $color-drab--light;
}
// implementations
.btn { @extend %btn; }
.btn--dark { @extend %btn--dark; }
%all-headings {
margin-bottom: .4rem;
font-family: $fam-2;
}
%h2 {
@extend %all-headings;
position: relative;
font-size: 2.25em;
&:before {
content: "";
display: block;
width: 2rem;
height: .4375rem;
margin-bottom: .3em;
background-color: $color-1;
}
}
// implementation
h2 { @extend %h2; }
/*------------------------------------*\
::Global
\*------------------------------------*/
*, *:before, *:after {
box-sizing: border-box;
}
body {
font-family: $fam-1;
background-color: $color-drab--dark;
color: #fff;
}
button,
html input[type="button"],
input[type="reset"],
input[type="submit"] {
@extend %btn;
-webkit-appearance: button;
cursor: pointer;
}
ul,
ol {
margin: .2em 0;
padding-left: 1.2em;
li {
padding-left: 0;
margin: .5em 0;
list-style-position: outside;
}
}
ul {
list-style: circle;
}
/*------------------------------------*\
::Footer (whole page in codepen's case)
\*------------------------------------*/
.in-touch {
max-width: 25em;
margin: 0 auto;
padding: $gap;
}
/*------------------------------------*\
::Inline Form
\*------------------------------------*/
.inline-form {
@extend %blockquote;
margin: 0 $gap;
padding: 0 .2em;
line-height: 1.4;
color: $color-drab--lighter;
border-radius: .2em;
transition: 300ms;
&.sent {
background-color:rgba($color--good,.1);
box-shadow:0 0 0 1px rgba($color--good,.3) inset;
}
&.failed,
&.invalid {
background-color:rgba($color--bad,.1);
box-shadow:0 0 0 1px rgba($color--bad,.3) inset;
}
&__label {
cursor: pointer;
transition: 300ms;
&:hover {
color: rgba(#fff, .8);
}
&.active {
color: #fff;
}
}
&__input-wrap {
border-bottom: 1px solid $color-drab--light;
background-color: darken($color-drab, 5%);
letter-spacing: .8em;
cursor: pointer;
color: $color-drab--lighter;
transition: 300ms;
.inline-form__label.active + &:not(.error) {
background-color: transparent;
border-color: #fff;
color: #fff;
}
&.error {
background-color:rgba($color--bad,.5);
box-shadow:0 0 0 1px rgba($color--bad,.5) inset;
}
}
&__input {
transition: 300ms;
letter-spacing: 0;
&:focus {
color: #fff;
outline: none; // shown by parent's styling
}
}
&__submit {
float: right;
margin-top: 1.5em;
}
}
// response output (uses wpcf7's screen reader text)
.in-touch {
.screen-reader-response {
display: block;
margin-bottom: 1em;
li {
position: relative;
list-style: none;
&:before {
content: "x ";
font-family: $fam-2;
color: $color--bad;
}
}
}
}
View Compiled
// keep it fresh
console.clear();
/*------------------------------------*\
::Inline Form
\*------------------------------------*/
var inlineForm = {
// orchestrate the form
init: function(){
// cache variables
inlineForm.$form = $('#js-inline-form');
inlineForm.$wpcf7 = inlineForm.$form.parents('.wpcf7');
inlineForm.$labels = inlineForm.$form.find('.inline-form__label');
inlineForm.$inputs = inlineForm.$form.find('.inline-form__input');
inlineForm.$inputWraps = inlineForm.$form.find('.inline-form__input-wrap');
inlineForm.$submit = inlineForm.$form.find('.inline-form__submit');
// start running
inlineForm.wpcf7FixType();
inlineForm.createLabels();
inlineForm.bindAddValue();
inlineForm.bindFocus();
inlineForm.handleInvalid();
},
// identify selected field by class
createLabels: function(){
// on each input
inlineForm.$inputs.each(function(){
// get the jQuery and basic node objects
var $thisInput = $(this);
var inputNode = $thisInput[0];
// when focus is added
inputNode.addEventListener('focus', function(){
// only add .active to current label
inlineForm.$labels.removeClass('active');
$thisInput
.parent('.inline-form__input-wrap')
.prev('.inline-form__label')
.addClass('active');
});
});
// on click of each label
inlineForm.$labels.on('click', function(){
// set focus on the next input node
$(this)
.next('.inline-form__input-wrap')
.find('.inline-form__input')[0]
.focus();
});
},
// add focus on empty contenteditable's
bindFocus: function(){
// for each wrapper of the inputs
inlineForm.$inputWraps.each(function(){
// cache this and the input node inside
var $this = $(this);
var inputNode = $this.find('.inline-form__input')[0];
// set focus on wrap click to input node inside
$this.on('click touchstart', function(){
inputNode.focus();
});
});
},
// add contenteditable values to inputs
bindAddValue: function(){
inlineForm.$inputs.each(function(){
// cache
var $this = $(this);
var $thisWrap = $this.parent();
var inputNode = $this[0];
// find the hidden input for this contenteditable
var $matchingInput = inlineForm
.$form
.find('[name="'+$this.attr('aria-labelledby')+'"]');
// when contenteditable loses focus then add value to hidden input
inputNode.addEventListener('blur', function(){
$matchingInput.val($this.html());
});
// when contenteditable gains focus
inputNode.addEventListener('focus', function(){
// remove any errors
$thisWrap.removeClass('error');
// if the form was invalid
if(inlineForm.$form.hasClass('invalid') || inlineForm.$form.hasClass('failed')){
// if there are no more errors then identify as such
if(inlineForm.$form.find('.error').length == 0){
inlineForm.$form.removeClass('invalid failed');
inlineForm.$submit.val('Try again!').prop('disabled', false);
}
}
});
});
},
// handle invalid form submissions
handleInvalid: function(){
// when wpcf7 reports invalid
inlineForm.$wpcf7.on('wpcf7:invalid', function(){
// get ready to store the error block
var $err = null;
// show errors
var showError = function(){
// look through each error in the list
$err.each(function(){
// parse the text to get the offending field's name
var txt = $(this).text();
var txtArr = txt.split(':');
var inputName = txtArr[0].substring(5, txtArr[0].length);
// add an error class
$('#'+inputName).next('.inline-form__input-wrap').addClass('error');
// identify errors on the button
inlineForm.$submit.val('Can\'t send with errors...').prop('disabled', true);
});
}
// look for the error block
var checkErr = function(){
// try to find the block with its li's
$err = $('.screen-reader-response').find('li');
// if it's not there yet
if($err.length == 0 || $err.hasClass('error-msg')){
// recursively call to look again
setTimeout(function(){
checkErr();
}, 250)
// if it's there then parse the errors
} else {
$err.addClass('error-msg');
showError();
}
}
checkErr();
});
},
// fix required type of type=email for email field
wpcf7FixType: function(){
$('#js-wpcf7-req-email').replaceWith('<input type="hidden" name="email" value="" class="wpcf7-form-control wpcf7-hidden">');
},
};
jQuery(function($){
inlineForm.init();
});
// ignore: quick dirty mimic of wpcf7 plugin's php-side functionality
jQuery(function($){
$('#js-inline-form').on('submit', function(e){
e.preventDefault;
var $form = $(this);
var $inputs = $form.find('[type="hidden"]');
var $wpcf7 = $form.parents('.wpcf7');
var $resp = $wpcf7.find('.screen-reader-response');
var err = [];
$inputs.each(function(){
var val = $(this).val();
var title = $(this).attr('name');
if('' == val){
err.push('Your '+title+': you can\'t leave this blank.');
} else if ('email' == title && val.indexOf('@') === -1) { // much more in-depth in my php version
err.push('Your '+title+': this doesn\'t look accurate.');
}
});
if(err.length > 0){
$form.addClass('invalid');
$wpcf7.trigger('wpcf7:invalid');
var errHTML = '<ul>';
for(var i = 0; i < err.length; i++){
errHTML +=('<li>'+err[i]+'</li>');
}
errHTML += '</ul>';
$resp
.html('Oops! Looks like we\'ve got errors...'+errHTML);
} else {
$form.removeClass('invalid').addClass('sent');
$resp.html('Sent. Thanks!');
}
return false; // don't ever send in this codepen example...
});
});
This Pen doesn't use any external CSS resources.