Reputation:
How to design an input text like in material design with a label that floats up and it doesn't overlap the content when the input is not focused, how can i implement that without using Materialise.CSS or any other library
Upvotes: 3
Views: 3480
Reputation: 9047
I prefer this technique for its markup simplicity, accessibility, and keyboard-friendliness.
It uses relative and absolute positioning (an absolute label inside a relative container) and never "replaces" the label with another element (such as with the placeholder pseudo-element) because replacement techniques cause flickering in my experience. It also manages the pointer events so that the label never gets in the way of clicking to select the input (but once it's out of the way, its text is selectable).
I've used ems, since we're dealing with animated text. This should allow the technique to scale to different font sizes without modifying the offsets used for padding and margins.
This uses the required
attribute and the :valid
selector to check whether an input was left blank (in which case, the label drops back down). If this doesn't work for your use case, it can be worked around by adding and removing an empty
class to the input using JavaScript (that's what the last CSS ruleset is for). Another option would be to use :placeholder-shown
with a dummy placeholder, if you're okay with not supporting Microsoft Edge. Remember, CSS's :empty
selector doesn't work the way you'd think it might on HTML inputs.
@import url('https://fonts.googleapis.com/css?family=Roboto');
body
{
/* just to make it feel a little more at home */
font-family: 'Roboto', sans-serif;
}
.floating-label
{
position: relative;
margin-top: 1.75em;
}
.floating-label input[type="text"]
{
border: none;
border-bottom: 1px solid gray;
margin-bottom: 1px;
}
.floating-label input[type="text"]:hover
{
border-bottom: 2px solid black;
margin-bottom: 0;
}
.floating-label input[type="text"]:focus,
.floating-label input[type="text"]:active
{
outline: none;
border-bottom: 2px solid #2196f3;
margin-bottom: 0;
}
.floating-label input[type="text"] + label
{
position: absolute;
pointer-events: none;
bottom: 0.1em;
left: 0.1em;
color: gray;
font-size: 1.0em;
transition: margin 0.2s ease,
color 0.2s ease,
font-size 0.2s ease;
}
.floating-label input[type="text"]:focus + label,
.floating-label input[type="text"]:active + label,
.floating-label input[type="text"]:valid + label
{
pointer-events: auto;
margin-bottom: 1.75em;
font-size: 0.7em;
}
.floating-label input[type="text"]:focus + label,
.floating-label input[type="text"]:active + label
{
color: #2196f3;
}
.floating-label input[type="text"].empty:not(:focus) + label,
.floating-label input[type="text"].empty:not(:active) + label
{
pointer-events: none;
margin-bottom: 0;
color: gray;
font-size: 1.0em;
}
<div class="floating-label">
<input id="first" type="text" required>
<label for="first">First Name</label>
</div>
<div class="floating-label">
<input id="last" type="text" required>
<label for="last">Last Name</label>
</div>
Upvotes: 6
Reputation: 1504
This is how I've done it previously. The CSS has be processed from SCSS which is why it's so specific.
.container {
width: 15%;
}
.container .form-group {
margin: 0 0 16px;
}
.container .form-group.field--invalid > .field__input-container .field__decoration:before {
border-color: #e4134f;
width: 100%;
}
.container .form-group .field__input-container {
position: relative;
margin-bottom: 8px;
padding-top: 16px;
}
.container .form-group .field__input-container .field__input {
font-size: 18px;
line-height: 28px;
font-weight: 700;
letter-spacing: -1px;
background: transparent;
border: 0;
outline: 0;
padding-top: 20px;
padding-bottom: 2px;
width: 100%;
}
.container .form-group .field__input-container .field__input:focus + .field__label, .container .form-group .field__input-container .field__input:valid + .field__label {
font-size: 14px;
line-height: 20px;
bottom: 50%;
}
.container .form-group .field__input-container .field__input:focus ~ .field__decoration:before, .container .form-group .field__input-container .field__input:valid ~ .field__decoration:before {
width: 100%;
}
.container .form-group .field__input-container .field__input:focus ~ .arrow-down {
border-color: transparent;
}
.container .form-group .field__input-container .field__input--dropdown {
border-color: #000;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.container .form-group .field__input-container .field__input--dropdown + .field__label {
color: #000;
}
.container .form-group .field__input-container select::-ms-expand {
display: none;
}
.container .form-group .field__input-container .field__label {
color: #848484;
font-size: 19px;
line-height: 28px;
bottom: 2px;
left: 0;
pointer-events: none;
position: absolute;
top: auto;
transition: bottom 0.3s ease, font-size 0.3s ease, line-height 0.3s ease;
margin: 0;
padding: 0;
border: 0;
vertical-align: baseline;
}
.container .form-group .field__input-container .field__label span {
margin: 0;
padding: 0;
border: 0;
vertical-align: baseline;
}
.container .form-group .field__input-container .field__decoration {
border-bottom: 1px solid;
border-color: #000;
}
.container .form-group .field__input-container .field__decoration:before {
display: block;
position: absolute;
content: "";
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid;
border-color: #000;
transition: width 0.3s ease, border-color 0.3s ease;
}
.container .form-group .field__input-container .arrow-down {
position: absolute;
right: 0;
bottom: 12px;
width: 0;
height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 6px solid #000;
transition: border-color 0.3s ease;
z-index: -1;
}
<div class="container">
<div id="user-first-name" class="form-group"><!-- field--invalid -->
<div class="field__input-container">
<input type="text" class="field__input" id="input-first-name" name="input-first-name" required value="" autocomplete="off">
<label class="field__label" id="username-label" for="input-first-name"><span>First name</span></label>
<div class="field__decoration"></div>
</div>
</div>
<div id="user-last-name" class="form-group"><!-- field--invalid -->
<div class="field__input-container">
<input type="text" class="field__input" id="input-last-name" name="input-last-name" required value="" autocomplete="off">
<label class="field__label" id="username-label" for="input-last-name"><span>Last name</span></label>
<div class="field__decoration"></div>
</div>
</div>
<div id="user-title" class="form-group"><!-- field--invalid -->
<div class="field__input-container">
<select class="field__input field__input--dropdown" id="input-title" name="input-title" required autocomplete="off">
<option selected value="" disabled hidden></option>
<option value="" disabled>Please select</option>
<option value="mr">Mr</option>
<option value="mrs">Mrs</option>
<option value="ms">Ms</option>
<option value="dr">Dr</option>
<option value="other">Other</option>
<option value="prefer not to say">Prefer not to say</option>
</select>
<label class="field__label" id="username-label" for="input-title"><span>Title</span></label>
<div class="field__decoration"></div>
<div class="arrow-down"></div>
</div>
</div>
</div>
Upvotes: 4