I have two inputs, but the pre-displayed labels have different length, causing the inputs not to start from the same position (vertically speaking). How to achieve that?
Some of my attempts are:
input {
margin-left: 50px;
}
input {
vertical-align:middle;
}
Please notice that display: block; will make the label and input lose their initial corresponding positions!
My JSFiddle.
You can make use of CSS table layout. The parent element acts as a table row and the child elements as table cells.
.input-container {
display: table-row;
}
.input-container * {
display: table-cell;
margin-left: 5px;
}
<fieldset>
<legend>Team1</legend>
<div class="input-container">
<label for="player1">Player1</label>
<input type="number" name="player1">
</div>
<div class="input-container">
<label for="player2">Player2345</label>
<input type="number" name="player2">
</div>
</fieldset>
Related
All,
What is the simplest way to create a simple layout that looks like this, in HTML and CSS:
Specifically - a right-aligned label on the left, and a left-aligned value on the right.
I don't want to hard-code the width of the left-column - that should be determined based on the width of the longest label.
I'm looking for an approach that's at least reasonably semantic, works well with screen-readers (e.g., screen reader should read the label, and then the value, not all the labels, then all the values), and doesn't require a whole bunch of additional <div> elements.
This seems like a reasonably common layout, so I'm assuming there's a very easy way to do it. But I've yet to figure that out myself.
A <table> would work perfectly, but as everyone reminds me, never use a <table> just for layout. And this is clearly not tabular data.
Thanks in advance!
There are a couple of options, using minimal HTML; one using CSS Grid and the other using CSS flex-box layout.
CSS Grid:
/* A simple reset to ensure that all elements and
pseudo-elements have their margin and padding
set to zero, and all are using the same box-sizing: */
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* here we set the <form> element's layout to use
grid layout: */
form {
display: grid;
/* we use the repeat() function to create 2 columns,
each column sized with a minimum of 0 width and a
maximum of 1fr; the 'fr' unit is a fractional unit
and here forces each column to take one fractional
unit of the available space to create two equal-sized
columns: */
grid-template-columns: repeat(2, minmax(0, 1fr));
/* we use the 'gap' property (formerly 'grid-gap') to
specify the gap between grid elements; here we have
0.5em above and below and 1em to the left and right: */
gap: 0.5em 1em;
width: 80vw;
margin: 0 auto;
}
label {
/* aligning the text to the right of the <label> element: */
text-align: right;
}
label::after {
/* using the 'content' property of the pseudo-element to
add the colon character: */
content: ':'
}
<form>
<!-- using the 'for' attribute to associate the label with the
relevant <input> element; the value of the 'for' attribute
must be equal to the 'id' attribute-value of the relevant
<input> -->
<label for="input1">label</label>
<input type="text" id="input1" placeholder="input 1">
<label for="input2">A longer label</label>
<input type="text" id="input2" placeholder="input 2">
<label for="input3">Another slightly longer label</label>
<input type="text" id="input3" placeholder="input 3">
<label for="input4">A label that goes on, frankly, for quite a bit further than might be common for the average <label> element</label>
<input type="text" id="input4" placeholder="input 4">
</form>
JS Fiddle demo.
Flexbox:
/* A simple reset to ensure that all elements and
pseudo-elements have their margin and padding
set to zero, and all are using the same box-sizing: */
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* setting the layout of the <form> to flexbox: */
form {
display: flex;
/* allowing the child elements of the <form> to wrap
to new lines when necessary: */
flex-wrap: wrap;
width: 80vw;
margin: 0 auto;
}
/* Setting common properties for the <label> and <input>
elements: */
label,
input {
/* assigning the flex-grow and flex-shrink (respectively)
properties to 1, in order that they grow/shrink by the
same amount relative to each other; and setting the
flex-basis to 40% (the percentage derived from the parent)
in order to assign a width that's too large to accommodate
more than two elements per line: */
flex: 1 1 40%;
/* setting the margin above/below each 'row' to be 0.5em,
and 0 to the left and right: */
margin: 0.5em 0;
}
label {
text-align: right;
/* setting the margin-right of the <label> to 1em to enforce a
gutter between the <label> and the neighbouring <input> (the
CSS Box Alignment module (level 3) introduces the 'gap' property
that can also be used in the flexbox layout (among others) but
that's not yet supported by browsers, so we have to use margins: */
margin-right: 1em;
}
label::after {
content: ':'
}
<form>
<label for="input1">label</label>
<input type="text" id="input1" placeholder="input 1">
<label for="input2">A longer label</label>
<input type="text" id="input2" placeholder="input 2">
<label for="input3">Another slightly longer label</label>
<input type="text" id="input3" placeholder="input 3">
<label for="input4">A label that goes on, frankly, for quite a bit further than might be common for the average <label> element</label>
<input type="text" id="input4" placeholder="input 4">
</form>
JS Fiddle demo.
Now, with both of the above approaches there is one requirement that may not be appropriate for your use-case: the <label> element must implement a for attribute, which requires that the associated <input> must also have an id attribute (and those values must be equivalent).
To avoid that, we could instead nest the <input> within the <label>, which automatically associates the <label> and <input>; this would give (potentially) cleaner HTML:
Grid, again:
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
form {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.5em 1em;
width: 80vw;
margin: 0 auto;
}
label {
/* it's a crude simplification, but effectively we remove
the <label> from the DOM and instead show its contents;
the text portion, and the <input>, become independant
grid-items (sort of): */
display: contents;
}
<form>
<label>label
<input type="text" placeholder="input 1"></label>
<label>A longer label
<input type="text" placeholder="input 2"></label>
<label>Another slightly longer label
<input type="text" placeholder="input 3"></label>
<label>A label that goes on, frankly, for quite a bit further than might be common for the average label element
<input type="text" placeholder="input 4"></label>
</form>
JS Fiddle demo.
In the above snippet you may notice that:
we no longer use label::after to insert the presentational ':' characters, since that would – obviously – be placed after the <label> content, following the <input> and would create another grid-item (demo), and
the <label> text is no longer right-aligned; this is because text-align doesn't seem to work on the text.
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
form {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.5em 1em;
width: 80vw;
margin: 0 auto;
}
label {
display: contents;
text-align: right;
}
<form>
<label>label
<input type="text" placeholder="input 1"></label>
<label>A longer label
<input type="text" placeholder="input 2"></label>
<label>Another slightly longer label
<input type="text" placeholder="input 3"></label>
<label>A label that goes on, frankly, for quite a bit further than might be common for the average label element
<input type="text" placeholder="input 4"></label>
</form>
JS Fiddle demo.
We could improve things, visually, be wrapping the text-content of the <label> in an element, such as a <span> to restore that presentation:
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
form {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.5em 1em;
width: 80vw;
margin: 0 auto;
}
label {
display: contents;
text-align: right;
}
label span::after {
content: ':';
}
<form>
<label><span>label</span>
<input type="text" placeholder="input 1"></label>
<label><span>A longer label</span>
<input type="text" placeholder="input 2"></label>
<label><span>Another slightly longer label</span>
<input type="text" placeholder="input 3"></label>
<label><span>A label that goes on, frankly, for quite a bit further than might be common for the average label element</span>
<input type="text" placeholder="input 4"></label>
</form>
It's also worth noting that both CSS Grid and CSS Flexbox allow us to reorder elements visually in the browser differently than they appear in the DOM; this can allow us to style the <label> elements based on the state of the associated <input>, for example,
Grid:
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
form {
display: grid;
/* to ensure that the layout backfills the empty spaces
created by moving content around: */
grid-auto-flow: dense;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.5em 1em;
width: 80vw;
margin: 0 auto;
}
label {
display: contents;
text-align: right;
}
input {
/* positioning the <input> elements in the secpond
grid-track/column: */
grid-column: 2;
}
label span {
/* positioning the <span> elements in the first
grid-track/column: */
grid-column: 1;
}
label span::after {
content: ':';
}
input:placeholder-shown+span {
color: rebeccapurple;
}
input:not(:placeholder-shown)+span {
color: limegreen;
font-weight: bold;
}
input:focus+span,
input:active+span {
color: #f90;
}
<form>
<label>
<input type="text" placeholder="input 1">
<span>label</span>
</label>
<label>
<input type="text" placeholder="input 2">
<span>A longer label</span>
</label>
<label>
<input type="text" placeholder="input 3">
<span>Another slightly longer label</span>
</label>
<label>
<input type="text" placeholder="input 4">
<span>A label that goes on, frankly, for quite a bit further than might be common for the average label element</span>
</label>
</form>
JS Fiddle demo.
Flexbox:
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
form {
width: 80vw;
margin: 0 auto;
}
label {
display: flex;
margin: 0.5em 0;
}
label span,
label input {
flex: 1 1 50%;
}
label span {
order: 1;
text-align: right;
margin-right: 1em;
}
label input {
order: 2;
}
label span::after {
content: ':';
}
label span::after {
content: ':';
}
input:placeholder-shown+span {
color: rebeccapurple;
}
input:not(:placeholder-shown)+span {
color: limegreen;
font-weight: bold;
}
input:focus+span,
input:active+span {
color: #f90;
}
<form>
<label>
<input type="text" placeholder="input 1">
<span>label</span>
</label>
<label>
<input type="text" placeholder="input 2">
<span>A longer label</span>
</label>
<label>
<input type="text" placeholder="input 3">
<span>Another slightly longer label</span>
</label>
<label>
<input type="text" placeholder="input 4">
<span>A label that goes on, frankly, for quite a bit further than might be common for the average label element</span>
</label>
</form>
JS Fiddle demo.
It's worth noting, though, that grid-auto-flow: dense, as well as rearranging the visual presentation of elements, can cause problems for those users of the web using screen readers or keyboard interaction. So, while it enables some prettiness, it's worth considering if that prettiness comes at the cost of usability (and potential legally-required accessibility).
Further, in the latter examples, the use of a nested <span> element to allow for styling of the text-content of the <label> element (whether for alignment reasons, adding the ':' characters or styling in response to interaction with the <input> elements, this may be unnecessary complication of a potentially 'clean' HTML markup.
References:
Selectors:
:active.
::after.
:focus.
:not().
:placeholder-shown.
Properties
box-sizing.
display.
flex.
flex-basis.
flex-grow.
flex-shrink.
gap.
grid-auto-flow.
grid-column.
grid-template-columns.
minmax().
order.
repeat().
Property-values:
<length> (units).
Bibliography:
"A Complete Guide to Grid."
"A Complete Guide to Flexbox."
"Basic Concepts of Flexbox."
"Basic Concepts of Grid Layout."
CSS tables to the rescue : https://css-tricks.com/almanac/properties/d/display/
These aren't tables, but can be used to recreate table behavior.
.notATable {
display:table;
list-style:none;
padding-left:0;
}
.notATable > li
{
display:table-row;
}
.notATable > li > *
{
display:table-cell;
padding:5px;
}
.notATable label {
font-weight:bold;
text-align:right;
}
.notATable label:after {
content: ':';
}
<ul class="notATable">
<li><label>Label</label><div>Value</div></li>
<li><label>Another Label</label><div>Some Value</div></li>
<li><label>A longer Label</label><div>Another Value</div></li>
<li><label>Short Label</label><div>A different Value</div></li>
<li><label>Really, Really Long Label</label><div>Last Value</div></li>
</ul>
Now you can use CSS to change the layout of your markup, for example you could use media-queries to display your labels and values differently on mobile devices.
Is this simple?
*{
padding: 5px;
margin: 0;
font-family: verdana;
}
.d-table {
display:table;
}
.m-auto{
margin:auto
}
.d-flex{
display:flex;
}
.d-flex > div{
height: auto;
display: flex;
flex-direction: column;
justify-content: space-around;
}
label{
display:block;
text-align:right;
font-weight: bold;
margin-right: 5px;
}
<div class="d-table m-auto">
<div class="d-flex">
<div>
<label>Label:</label>
<label>Another label:</label>
<label>A long label:</label>
<label>Short label:</label>
<label>Really, really long label:</label>
</div>
<div>
<p>Value</p>
<p>Some value</p>
<p>Another value</p>
<p>A different value</p>
<p>Last value</p>
</div>
</div>
</div>
I want to have a group of labels that take as much horizontal space as the widest label and at the same time all input fields should shrink to accommodate the larger labels. I need this since I deal with translations and cannot be sure how wide a certain label will be.
Something like this:
form {
display: table;
width: 100%;
}
label {
width: 1%;
display: table-cell;
white-space: nowrap;
padding-right: 1em;
}
input {
display: inline-block;
width: 100%;
}
.input-group {
display: table-row;
}
* {
line-height: 2em;
}
body {
padding: 2em;
}
<form>
<div class="input-group">
<label for="name">Full name of your family and yourself</label>
<input type="text" id="name" name="name" placeholder="optional">
</div>
<div class="input-group">
<label for="email">Email</label>
<input type="text" id="email" name="email" placeholder="optional">
</div>
<div class="input-group">
<label for="phone">Phone</label>
<input type="text" id="phone" name="phone" placeholder="optional">
</div>
</form>
You can check this in Codepen too:
http://codepen.io/aboutandre/pen/bZRjqB
But is it there a more elegant way to do this that is more generic and doesn't involve using tables (be as HTML or CSS)?
How about specifying the width of the input fields to inherit from the desired/determining input field?
So for example if the name field is the determinant,make that have a width of 100% and and the other two elements -
width: inherit;
I have this HTML Code:
<h4>Company Details</h4>
<div>
<label for="company">Company</label>
<div>
<input type="text" name="company" value="<?php echo $customer["company"]; ?>" />
</div>
</div>
I would like to have the text inputs and labels displaying inline with each other then as the screen gets smaller to move the text inputs under the labels
There are many ways to achieve this. Chose the one that fits your project the best. I would set the label and the div containing the input as "inline" or "inline-block" using CSS. Then if you need then line to collapse into two, use media queries (see the last code snippet).
<h4>Company Details</h4>
<div class="input-group">
<label for="company">Company</label>
<div>
<input type="text" name="company" value="<?php echo $customer["company"]; ?>" />
</div>
</div>
// This will make them show inline occupying half the width
.input-group > label, .input-group > div{
display: inline-block;
width: 50%;
}
I sometimes have problems with extra pixels in inline-blocks,
and I typically solve it by floating the blocks or setting the
font size to zero.
// This way the label and input float
.input-group {
clear: both; // So the container doesn't collapse
}
.input-group > label, .input-group > div{
float: left;
width: 50%;
}
For the font size version:
// This way the font size in the container is zero
.input-group {
font-size: 0;
}
.input-group > label, .input-group > div{
display: inline-block;
width: 50%;
font-size: 12px;
}
Your div will automatically stretch to 100% of the screen, so You don't need media queries to set its width; it's already responsive as is. But if you insist on using them, or need them, add this as well:
#media (max-width: 500px) {
.input-group > label, .input-group > div{
display: block;
float: none;
}
}
It's pretty easy to achieve. Simply set:
.inputLine label, .inputLine div {
display: inline;
}
With the HTML being:
<h4>Company Details</h4>
<div class="inputLine">
<label for="company">Company</label>
<div>
<input type="text" name="company" value="My Company" />
</div>
</div>
Working example:
http://jsfiddle.net/wLjekogo/
Updated: april 20th, 2015
To achieve the requested output, while allowing multiple inline elements. You could use the following HTML/CSS combination:
HTML
<h4>Company Details</h4>
<div class="inputLine">
<label for="company">Company</label>
<input type="text" name="company" value="My Company" />
</div>
<div class="inputLine">
<label for="company">Company</label>
<input type="text" name="company" value="My Company" />
</div>
CSS
.inputLine {
display: inline-block;
}
.inputLine label {
display: inline-block;
max-width: "100%";
}
.inputLine input {
display: inline-block;
width: auto;
}
This would show all input fields inline, unless the screen is too small. In which case first the divs will be on a new line, and even more smaller, a vertical form.
Example:
http://jsfiddle.net/wLjekogo/2/
I'm using a form like the following:
<form action="#" method="post">
<div class="row">
<label for="email">E-Mail</label>
<input type="text" name="email" id="email">
</div>
<div class="row">
<label for="password">Password</label>
<input type="password" name="password" id="password">
<br>
<label for="passwordRepeat">Repeat Password</label>
<input type="password" name="passwordRepeat" id="passwordRepeat">
</div>
<div class="row">
<label for="phonenumber">Phone Number</label>
<input type="text" name="phonenumber" id="phonenumber">
</div>
</form>
with the following styles:
.row {
background-color: #eee;
margin-bottom: 10px;
padding: 5px;
}
.row > * {
display: inline-block;
vertical-align: middle;
}
.row > label {
width: 200px;
}
Take a look at the JSFiddle.
I'm using a <br> tag to break the line between a bunch of elements with the property display: inline-block. I'm aware that it is of course bad practice to use <br> instead of margin and padding. That's the reason it became so unpopular.
As far as I know there is no good reason to not use a single <br> tag in an inline element as it is intended to be: As a line break in text without creating a new text section. With display: inline-block, you simulate the inline behaviour to your block elements. Spaces between elements appear as they would in an inline element.
In my case, the <br> is used instead of two wrapper <div>'s. I do like my HTML code clean, so I hesitate in using to many wrapper <div>'s. Is it bad practice to use a <br> in this exact case? I think it is very clear what happens here, if you just read the HTML flie. What do you think about that (without any prejudgments about <br> in general)?
I believe the answer is Yes. <br /> is for line breaks in text and not for positioning, But I will give you a situation where it would hurt you in the long run. Say you have a mobile layout for your fields, and you want them to be 100% width on small screens - with labels above... and then in another case you want them to vertically align next to another... and then in another situation land in a grid like setup. Those linebreaks are going to become cumbersome.
Here is a jsFiddle of that.
I did see someone using them in a clever way where they used display: none; on them at certain break points that rendered them inactive. I didn't expect that to work. I can only really imagine using them for:
Cosmo magazine
style - huge
text layouts
and even then I would use lettering.js to insert spans. But hey --- it's not that people will say you were wrong... it's what does the job best. And I don't think that <br /> ever really suits positioning.
With HTML5, it seems like everything has an element now, so div's are for positioning. That seems pretty semantic to me.
HTML
<div class="input-wrapper">
<label data-required="required">E-Mail</label>
<input type="email" name="email" />
</div>
CSS
.your-form .input-wrapper {
width: 100%;
float: left;
margin-bottom: 2em;
}
.your-form label {
display: block;
width: 100%;
float: left;
}
[data-required="required"]:after{
content: "*";
color: red;
font-size: .8em;
vertical-align: top;
padding: .2em;
}
.your-form input{
display: block;
width: 100%;
float: left;
}
#media screen and (min-width: 28em) {
.your-form label {
width: auto;
float: none;
display: inline-block;
vertical-align: middle;
min-width: 10em;
}
.your-form input{
width: auto; /* overide previous rule */
float: none; /* overide previous rule */
display: inline-block; /* center vertically */
vertical-align: middle; /* center vertically */
/* min-width: 20em; */
font-size: 1.4em; /* just to show vertical align */
}
} /* end break point */
Yes, as you are using a content element for styling.
It might be shorter, but that doesn't mean it's cleaner.
Adding elements just for styling purposes should be avoided if possible.
And in this case it's possible: Demo
HTML:
<form action="#" method="post">
<div class="row">
<label>E-Mail <input type="text" name="email" /></label>
</div>
<div class="row">
<label>Password <input type="password" name="password" /></label>
<label>Repeat Password <input type="password" name="passwordRepeat" /></label>
</div>
<div class="row">
<label>Phone Number <input type="text" name="phonenumber" /></label>
</div>
</form>
CSS:
.row {
background-color: #eee;
margin-bottom: 10px;
padding: 5px;
}
.row > label {
display: block;
overflow: hidden;
width: 350px;
}
.row > label > input {
float: right;
}
I would avoid it where possible. You may be able to achive what you want, and not use floats by adding a margin to the input element like:
.row > input
{
margin-right:50%;
}
http://jsfiddle.net/pwtA4/
You may need to add some media queries if you want for smaller view ports
Here is what my work is so far:
http://jsfiddle.net/2RCBQ/
<div id="main">
<form>
<label>First Name:<input type="text" id="firstname"></label><br/>
<label>Last Name:<input type="text" id="lastname"></label><br>
<label>E-Mail:<input type="text" id="email"></label><br/>
<label>Phone:<input type="text" id="phone"></label><br/>
</form>
</div>
CSS
#main {
width:300px;
}
#main input {
float:right;
display:inline;
}
#main label {
color: #2D2D2D;
font-size: 15px;
width:250px;
display: block;
}
Currently, the label (on the left) is kind of towards to top of the input field (on the right). I want to vertically align them so the label since in the middle of the input field.
I've tried vertical-align and it does not work. Please help me try to figure out the problem. Thanks.
I feel nesting <span> adds a lot of unnecessary markup.
display: inline-block lets the <label> and <input> sit next to each other just like with float: right but without breaking document flow. Plus it's much more flexible and allows more control over alignment if you (or the user's screen reader) want to change the font-size.
Edit: jsfiddle
label, input {
display: inline-block;
vertical-align: baseline;
width: 125px;
}
label {
color: #2D2D2D;
font-size: 15px;
}
form, input {
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
form {
width: 300px;
}
<form>
<label for="firstname">First Name:</label><input type="text" id="firstname">
<label for="lastname">Last Name:</label><input type="text" id="lastname">
<label for="email">E-Mail:</label><input type="text" id="email">
<label for="phone">Phone:</label><input type="text" id="phone">
</form>
You can use flexbox css to vertical align.
Just wrap the parent element display-flex.
.display-flex {
display: flex;
align-items: center;
}
html:
I add span in your label so we can add style specific for the text label:
<div id="main">
<form>
<label><span>First Name:</span><input type="text" id="firstname"></label><br/>
<label><span>Last Name:</span><input type="text" id="lastname"></label><br>
<label><span>E-Mail:</span><input type="text" id="email"></label><br/>
<label><span>Phone:</span><input type="text" id="phone"></label><br/>
</form>
</div>
css:
#main label span {
position:relative;
top:2px;
}
demo
You can enclose the <label> elements in a span and set the span's vertical-align to middle
HTML
<div id="main">
<form> <span><label>First Name:<input type="text" id="firstname" /></label></span>
<br/> <span><label>Last Name:<input type="text" id="lastname" /></label></span>
<br/> <span><label>E-Mail:<input type="text" id="email" /></label></span>
<br/> <span><label>Phone:<input type="text" id="phone" /></label></span>
<br/>
</form>
</div>
CSS
#main {
width:300px;
}
#main input {
float:right;
display:inline;
}
#main label {
color: #2D2D2D;
font-size: 15px;
}
#main span {
display: table-cell;
vertical-align: middle;
width:250px;
}
http://jsfiddle.net/2RCBQ/2/
I think that the following is the only method that works for all input types.
label { display: flex; align-items: center; }
input { margin: 0; padding: 0; }
<label><input type="checkbox"> HTML</label>
<label><input type="radio"> JS</label>
<label>CSS <input type="text"></label>
<label>Framework
<select><option selected>none</option></select>
</label>
I put because it seems to be the simplest way to align different input types; however, margins work just fine.
I know this is a super-old post, but I feel that the answers mix things and come to different solutions.
The original author asked about the label text's vertical alignment of implicit labelling; some answers solve this by using explicit labelling. I think this was not asked for.
See the difference between implicit vs. explicit labelling here: https://css-tricks.com/html-inputs-and-labels-a-love-story/#aa-how-to-pair-a-label-and-an-input
As I'm confronted every now and then I'd like to share my solution for implicit labelling.
The problem at explicit labelling is easily solved, since then you have your label as its own box and can apply any CSS of your liking to it rather independent of the associated input field.
However, at implicit labelling, the situation is different, since then the label text and the input are not separated items in this box. I think you do not have any other choice but to add a span around the text if you want to address the text independently from the input (note: you may not use a div here. Inside a label, only phrasing content elements are allowed: https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#phrasing_content and div is not.)
This is what https://stackoverflow.com/a/15193954/8754067 stated above correctly, but the answer is lacking the dichotomy between implicit and explicit labelling. And has been not up-voted enough (at least in my personal view). Therefore, I feel the need to stress this again here.
form {
width: 400px;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
form label {
display: grid;
grid-template-columns: 10rem 1fr;
gap: 0.5rem;
min-width: 100%;
font-size: 15px;
/* increase height to see effect. */
height: 3rem;
}
form label span {
margin-block: auto;
}
<form>
<label><span>First Name (middle):</span><input type="text" id="firstname"></label>
<label><span>Last Name (middle):</span><input type="text" id="lastname"></label>
<label>E-Mail (default):<input type="text" id="email"></label>
<label>Phone (default):<input type="text" id="phone"></label>
</form>