I have a form with legend
and fieldset
s, and need to have label
s by left side of input
s.
I want to stylish my form
label
s with CSS only, but CSS selectors cannot find preceding siblings, so I need to write label
after input
in HTML code in order to select the label
s connected with invalid input
s, and use float: left
.
When input
is filled incorrectly, corresponding label needs to be changed.
In following examples the label becomes red and validation fail description message unfolds.
Description label
needs to be on the same line as corresponding input (for aesthetic purposes) or, at least, left corner should be aligned by the most right point of the input
.
The problem occurs when I try to zoom in.
I emulate this by setting lower width to the outer div
.
#container {
width: 200px;
}
form {
border: 1px solid red;
overflow: auto;
}
fieldset {}
section {}
input[type="text"] {
display: inline-block;
}
input[type="text"]+label {
display: inline-block;
float: left;
text-align: right;
width: 80px;
}
input[type="text"]:invalid+label {
color: red;
}
input[type="text"]+label+label {
display: none;
}
input[type="text"]:invalid+label+label {
display: inline-block;
color: red;
}
.field-row {
display: inline-block;
white-space: nowrap;
}
<div id="container">
<form>
<fieldset>
<legend>Fieldset</legend>
<section>
<div>
<div class="field-row">
<input id="first" type="text" value="" required />
<label for="first">First</label>
<label>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</label>
</div>
</div>
<div>
<div class="field-row">
<input id="second" type="text" value="" />
<label for="second">Second</label>
</div>
</div>
</section>
</fieldset>
</form>
On this snippet you can see that the first row is very wide and goes out of outer fieldset
(at least in Google Chrome).
If I use overflow: auto
for the fieldset
, legend
text moves left when I move scrollbar.
When required input
is valid, corresponding label
goes to another row, but I need the label
to be on one line with connected input
.
The hack, which helped me, is to set min-width
of the field-row
block wider than label
+ input
width.
Though, this needs to know maximum allowed width of them and either use JavaScript
or change it on any padding
, margin
and width
changes of dependent fields.
Also it works well when I don't use float
.
Although, this approach will need to use JavaScript
to change change label
in input
validation state changed.
I make a ReactJS
application, so JavaScript
is not a problem,
but I want to investigate and use CSS capabilities as much as I can.
If you only need to support modern browsers, I recommend giving up on trying to use float for this and use flexbox instead.
All you need to do is to make the .field-row
div display: flex;
and then set the order
property for the input and label elements. Like this:
#container {
width: 200px;
}
form {
border: 1px solid red;
overflow: auto;
}
fieldset {}
section {}
input[type="text"] {
order: 2;
}
input[type="text"]+label {
text-align: right;
order: 1;
width: 80px;
}
input[type="text"]:invalid+label {
color: red;
}
input[type="text"]+label+label {
display: none;
order: 3;
}
input[type="text"]:invalid+label+label {
color: red;
display: inline;
}
.field-row {
display: flex;
white-space: nowrap;
}
<div id="container">
<form>
<fieldset>
<legend>Fieldset</legend>
<section>
<div>
<div class="field-row">
<input id="first" type="text" value="" required />
<label for="first">First</label>
<label>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</label>
</div>
</div>
<div>
<div class="field-row">
<input id="second" type="text" value="" />
<label for="second">Second</label>
</div>
</div>
</section>
</fieldset>
</form>
</div>