In previous tutorials, I’ve shown you how to build a responsive form with flexbox as well as how to style form elements. Today, we’ll continue the journey into the “forms” world and learn to create a responsive handmade SVG form from scratch.
Here’s the form that we’re going to build:
1. Begin With the Page Markup
We’ll start with an SVG and a form
element:
<svg style="display:none;">...</svg> <form class="handmade-form">...</form>
SVG Sprites
For our form, we’re going to need a bunch of handmade illustrations. Happily, Ian Yates, Web Design editor here at Tuts+, created for us some beautiful illustrations. Thank you, Ian :)
Similar to what we’ve done in a previous tutorial, we’ll create an SVG sprite and embed all the drawings within it. Then, inside the form, we’ll render the target icon whenever we need it by calling the use
element.
Here’s the markup for the SVG sprite:
<svg style="display:none;"> <symbol id="input1" viewBox="0 0 347.11 49.7" preserveAspectRatio="none">...</symbol> <symbol id="input2" viewBox="0 0 345.27 56.51" preserveAspectRatio="none">...</symbol> <symbol id="input3" viewBox="0 0 344.17 48.76" preserveAspectRatio="none">...</symbol> <symbol id="input4" viewBox="0 0 347.13 54.94" preserveAspectRatio="none">...</symbol> <symbol id="textarea" viewBox="0 0 704.84 271.56" preserveAspectRatio="none">...</symbol> <symbol id="fieldset" viewBox="0 0 998.06 602.62" preserveAspectRatio="none">...</symbol> <symbol id="checkbox_empty" viewBox="0 0 33.18 33.34">...</symbol> <symbol id="checkmark" viewBox="0 0 37.92 33.3" preserveAspectRatio="none">...</symbol> <symbol id="button" viewBox="0 0 256.6 60.02" preserveAspectRatio="none">...</symbol> </svg>
Notice the preserveAspectRatio="none"
attribute which we attached to most of the illustrations. We did this because, as we’ll see later, our icons will scale and loose their initial dimensions.
Form
The form will consist of a heading and an unordered list. Plus, we’ll use a .container
for setting a maximum width to the form and horizontally center its contents:
<form class="handmade-form"> <div class="container"> <h1>Contact us</h1> <ul> <li class="grid grid-2">...</li> <li class="grid grid-2">...</li> <li class="form-wrapper">...</li> <li class="form-wrapper">...</li> <li class="grid grid-3">...</li> </ul> </div> </form>
As you can see, each of the list items above contains one or more classes. Alternatively, for readability reasons, we could have created an extra child element and passed those classes to it, like this:
<li> <div class="grid grid-2">...</div> </li>
Inside the list, we’ll place the SVGs and form elements.
The first list item will include two required input fields along with their SVGs:
<div class="form-wrapper"> <input type="text" placeholder="Name*" required> <svg> <use xlink:href="#input1"></use> </svg> </div> <div class="form-wrapper"> <input type="text" placeholder="Surname*" required> <svg> <use xlink:href="#input2"></use> </svg> </div>
In the same way, the second list item will also hold two input fields along with their SVGs:
<div class="form-wrapper"> <input type="email" placeholder="Email*" required> <svg> <use xlink:href="#input3"></use> </svg> </div> <div class="form-wrapper"> <input type="tel" placeholder="Phone"> <svg> <use xlink:href="#input4"></use> </svg> </div>
Within the third list item, we’ll place a textarea and its associated illustration:
<textarea placeholder="Message"></textarea> <svg> <use xlink:href="#textarea"></use> </svg>
The fourth list item will include a fieldset
element. Inside it, we’ll put a legend
element, a list with two radio buttons along with their SVGs, and its related SVG:
<fieldset> <legend>Select preferred method of contact</legend> <ul class="checkbox-list"> <li> <input type="radio" id="email" name="contact-method" checked> <label for="email"> <svg> <use xlink:href="#checkbox_empty"></use> </svg> <svg class="checkmark"> <use xlink:href="#checkmark"></use> </svg> By Email </label> </li> <li> <input type="radio" id="phone" name="contact-method"> <label for="phone"> <svg> <use xlink:href="#checkbox_empty"></use> </svg> <svg class="checkmark"> <use xlink:href="#checkmark"></use> </svg> By Phone </label> </li> </ul> <svg> <use xlink:href="#fieldset"></use> </svg> </fieldset>
Finally, the fifth list item will contain the submit and reset buttons along with their SVGs:
<div class="form-wrapper"> <button type="submit">SUBMIT</button> <svg> <use xlink:href="#button"></use> </svg> </div> <div class="form-wrapper"> <button type="reset">RESET</button> <svg> <use xlink:href="#button"></use> </svg> </div>
2. Define Some Basic Styles
Before having a closer look at the individual form elements, let’s first define some basic CSS styles. These will include a custom font taken from Envato Elements, a few custom variables, and some reset rules:
@font-face { font-family: "Summer"; src: url(SummerFont-Regular.woff); } @font-face { font-family: "Summer Bold"; src: url(SummerFont-Bold.woff); } :root { --white: #fff; --red: #e31b23; } * { padding: 0; margin: 0; border: none; box-sizing: border-box; } input, textarea, button { font-family: inherit; font-size: 100%; background: none; outline: none; } [type="radio"] { position: absolute; left: -9999px; } button, label { cursor: pointer; } textarea { resize: none; } ul { list-style: none; } body { font: 32px/1.2 "Summer"; margin: 1.5rem 0; }
Note: for simplicity, I won’t walk through all the CSS rules in this tutorial. You can check the rest of them by clicking at the CSS tab of the demo project.
3. Build the Form Layout
On small screens all our form elements will be stacked:
However, on viewports 600 pixels wide and above, the form layout will change. More specifically:
- We’ll arrange the
input
s of the first two rows into two equal-width columns. - Each button will cover one-third of the parent row width.
Thanks to CSS Grid, we can easily build the desired multi-column layout. To begin with, we’ll set the .handmade-form .grid
container as being a grid. Then, we’ll use the grid-2
and grid-3
helper classes to define the number of grid columns:
@media screen and (min-width: 600px) { .handmade-form .grid { display: grid; grid-gap: 1.5rem; } .handmade-form .grid-2 { grid-template-columns: repeat(2, 1fr); } .handmade-form .grid-3 { grid-template-columns: repeat(3, 1fr); } }
All of these rules are placed within a media query, so they will only take effect on viewports 600px wide and above.
4. Style the Form Elements
With our structure sorted out, our next step is to specify some initial aesthetic styles to all form elements:
/*CUSTOM VARIABLES HERE*/ .handmade-form input:not([type="radio"]), .handmade-form textarea, .handmade-form button { width: 100%; } .handmade-form input:not([type="radio"]), .handmade-form textarea, .handmade-form fieldset { padding: 15px; } .handmade-form textarea { height: 200px; vertical-align: top; } .handmade-form legend { padding-top: 15px; margin: 0 auto; } .handmade-form ::placeholder { color: inherit; /*Fix opacity issue on Firefox*/ opacity: 1; } .handmade-form button { font-family: "Summer Bold"; color: var(--white); padding: 5px 10px; }
Positioning the Illustrations
The next and most challenging part of this exercise is to position the illustrations relative to their associated form element. To do this, we’ll use the position
property. An alternative implementation might be to set the drawings as background images. However, I am not a big fan of this implementation because it needs extra manipulation for making images adapt on various screens.
Let’s take note of our plan:
- The SVGs will be absolutely positioned elements.
- They will sit in the same position as their form control and have the same dimensions. That’s why earlier we set their
preserveAspectRatio
tonone
. - The form controls should be focusable, so we’ll give them a higher
z-index
.
Here are the required styles for this behavior:
.handmade-form .form-wrapper, .handmade-form input:not([type="radio"]), .handmade-form textarea, .handmade-form button, .handmade-form .checkbox-list label { position: relative; } .handmade-form input:not([type="radio"]), .handmade-form textarea, .handmade-form button, .handmade-form .checkbox-list label { z-index: 1; } .handmade-form .form-wrapper svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
5. Create Custom Radio Buttons
To create our custom radio buttons, once more, we’re going to take advantage of the “CSS checkbox hack technique”.
If you look again at the markup section, you’ll notice that the fieldset
element contains the .checkbox-list
list. Inside each label, there are two SVGs. The first one describes the unchecked state of the associated radio button, while the other one its checked state.
The radio buttons should fit on a single line, so we’ll turn the .checkbox-list
into a flex container. Also, we’ll absolutely position them relative to their label and give them some fixed dimensions (20px x 20px).
As a radio button changes its state, the SVG which describes its checked state will be animated. To achieve this effect, we’re going to work with the stroke-dasharray
and stroke-dashoffset
properties which we’ve extensively covered in a recent tutorial.
Here are the corresponding styles:
.handmade-form .checkbox-list { display: flex; justify-content: center; } .handmade-form .checkbox-list li:not(:last-child) { margin-right: 50px; } .handmade-form .checkbox-list label svg { top: 50%; left: -25px; width: 20px; height: 20px; transform: translateY(-50%); } .handmade-form .checkbox-list label .checkmark { stroke-dasharray: 233.69552612304688; stroke-dashoffset: 233.69552612304688; transition: stroke-dashoffset 0.5s linear; } .handmade-form [type="radio"]:checked + label .checkmark { stroke-dashoffset: 0; }
Note: Even though I’ve attached the stroke-*
properties to the parent SVG instead of its path
, the effect still works because these properties are inherited.
Conclusion
That’s it, folks! In this tutorial, we managed to build a responsive handmade SVG form from scratch. I hope that this exercise helped you learn something new and inspired you for using it in an upcoming project.
Here’s a reminder of what we built:
In an upcoming tutorial, we’ll go a step further and discuss how to make this form dynamic by incorporating it in a popular WordPress contact form plugin. Stay tuned!
As always, thanks a lot for reading!
No comments:
Post a Comment