'Fieldset legend CSS rendering behavior has changed in recent months... what happened?
I have a form with various nested <fieldset>
elements, each with a <legend>
, and today I noticed an error in my web page on all the browsers I've checked (Chrome, Firefox, Opera) which was not present earlier (6-12 months ago).
There is a <span class="undo">
element which displays as a curly green arrow using a background-image
. The intent is for it to get displayed when the surrounding <fieldset>
has class="modified"
and hidden otherwise --- this is implemented by changing the width
of the span.
The old behavior (still shows up on my Chromebook running Chrome OS 76.0.3809.136) which renders consistently with my intent:
The new behavior (on Chrome 87.0.4280.88 on my Windows 10 PC) has the "Foo configurator" wrapping to two lines for some reason.
Did something change in browser rendering recently to cause this? If so, what, and how can I fix?
HTML:
<!DOCTYPE html>
<head><style type="text/css">
form {
max-width: 500px;
}
body {
font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif;
}
fieldset {
padding: 0;
}
fieldset > legend {
font-size: 90%;
margin-left: 0.5em;
}
span.undo {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAIAAABvFaqvAAAABnRSTlMA/wD/AP83WBt9AAAACXBIWXMAAAsTAAALEwEAmpwYAAADEklEQVQ4jY2UXSxbYRjHn/ecnm5lLFTbxdIlbENTq0x8JD62RBHRm/lKCBIXJGw3jbhtuCERVy6wyDJEYhLbMuKiDYmwIRITESQNq1LZaFg5ynrannN2ceR1VrT7X53n/f/PL8/7CXxQzdnnGicbg2cESeAW8cD3f+83mo3qSDUPPAJ0W1IQ4nn++ijDMs2W5r7lPp7npaS0VFOqCFNoFBp9nD5BnvC/oAP3QeXHytnd2etpAhHZ6uzWl6158XkBPQaCln8uV4xV2E/swWaBUP3z+u6ibhkluxk0YZ2o/lzt9rqDULD0cfrxqvFwKlwoyba2Nuyp76s1MRqaoffoPY7n8LiMkoVRYQzLiEE7Jzs7rp0STQlCKBBEkZRWqa3V1VY/q5bL5I5Th8vjAgCdSrf5ZjNLnWVz2fbpfZxfd65rFVqtUhs4tQD5Of+sfXZgdWDGPmM32imC8rE+o9nYu9yLM4nyxPXX6xJCEgyE5fK4IqWRJEECAMuxhg8Gy7YFu5YaS+HjQkIofKxvwbGw4dzg4QZu1N0ogQIAJEF26jsJRGDXvG0GAAIAPH5P/nB+9vts3VudacYUssGUBylJMUm4XPm1cgmy/LDM7c4BAMdzXfNdp57TkKxEeSL+Pjw/vASJ//SyXueFMyRIvLLCEScAIDYiVhzacG6EoAC/ebSJS+F3AgCSlckS4uoZGF4bDg5a2l/a+r2Fy7TYtEuQ6p4q42EGNsat41O2qdsoDMu0TLWIp1b8tPgShAA1pjVig+XYqk9V83vz1ykXvou6L3ViS6fS5TzKAXyy/Zw/812msJGCpKS0IbWhPrU+KSaJIqjjP8fmbXPHtw7rkRVnEEKTVZNCR1cne+1wLXcgl2bogC4i7kRQBEUztJ/zB1hNaU09hh5h1/65IuZtc/lY+bn3/LYFEqtMUzZSNiIlpUJJiL2iJ0WjZaOKMEVwBEKoRlcz+GoQUwI7EnTgPmj/2j60OnTmPbuOSI9NN70wGRIMIZ5aLJfHNW2bXnQsOmiHj/VFy6K1Sm1BfEGyMll8Y7H+AvtLjkoh3mWsAAAAAElFTkSuQmCC');
display: inline-block;
height: 13px;
width: 1em;
font-size: 13px;
background-repeat: no-repeat;
background-size: 13px;
margin-top: 2px;
margin-right: -2px;
}
legend > span.undo {
width: 0em;
}
fieldset.modified > legend > span.undo {
width: 1em;
}
div.description {
padding-left: 4px;
font-size: 90%;
}
</style></head>
<body>
<form>
<fieldset class="field">
<legend title="">Foo configurator<span class="undo" title="Restore default"></span></legend>
<label title="">
<input name="foo_type" type="radio" value="123">Foo #1</label> <div class="description">First foo</div>
<span class="field-content">
<label><input name="foo_enable" type="checkbox" value="value">Enable</label>
<span class="undo" title="Restore default"></span>
<div class="description">Zoppity</div>
</span>
</fieldset>
<fieldset class="field modified">
<legend title="">Bar configurator<span class="undo" title="Restore default"></span></legend>
<label title="">
<input name="bar_type" type="radio" value="123">Bar #1</label> <div class="description">First bar</div>
<span class="field-content">
<label><input name="bar_enable" type="checkbox" value="value">Enable</label>
<span class="undo" title="Restore default"></span>
<div class="description">Zoppity</div>
</span>
</fieldset>
</form>
</body>
</html>
Solution 1:[1]
If you can see it correctly on Chrome 76, it's likely this change has been introduced by turning on LayoutNG in Chrome 77, which has been released in 2019 already, but some parts of it came out later. For example, I know I had some inconsistency with layouting in textareas and had to have a hack until about last year.
It would be ideal to test with Chrome 77 to see if it really is this thing or not.
Anyway, this looks like an interplay between the margin-right: -2px
and width: 0em
that it somehow calculates the layout as overflowing and add a new line.
By using BrowserStack I found it changed in Chrome 86 which happens to be a version in which Chrome implemented flex and grid for fieldsets. They may have needed subtle changes for this to happen as well.
Solution 2:[2]
The negative margin is pulling legend
's right border to left by 2px thus reduces legend's width by 2px. When you set width:0em
on span.undo
the negative margin still plays the role. But now legend
's width is 2px short to render the title text on the same line so it wraps.
We can simulate the behavior. See the code comments.
* {
font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif;
font-size: 16px;
}
/* legend is behaving like a block element with width fit-content */
div {
border: 1px solid;
width: fit-content;
}
/* undo icon is inline-block */
span {
display: inline-block;
}
/* title text */
span:first-child {
color: green;
}
/* undo elements with negative margins */
.negative-margin1 {
margin-right: -20px;
}
.negative-margin2 {
margin-right: -40px;
}
.negative-margin3 {
margin-right: -49px;
}
<div class="one"><span>Foo efg </span> <span>undo</span></div>
<div class="two"><span>Foo efg </span> <span class="negative-margin1">undo</span></div>
<div class="two"><span>Foo efg </span> <span class="negative-margin2">undo</span></div>
<div class="two"><span>Foo efg </span> <span class="negative-margin3">undo</span></div>
Depending on your font size the output may slightly vary, so here is how it's intended to look:
We can avoid negative margin by doing a little adjustment in padding:
form {
max-width: 500px;
}
body {
font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif;
}
fieldset {
padding: 0;
}
fieldset>legend {
font-size: 90%;
margin-left: 0.5em;
/* set padding right to zero */
padding-right: 0;
}
/* but we want 2px margin right for the title text */
span.ttl {
margin-right: 2px;
}
span.undo {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAIAAABvFaqvAAAABnRSTlMA/wD/AP83WBt9AAAACXBIWXMAAAsTAAALEwEAmpwYAAADEklEQVQ4jY2UXSxbYRjHn/ecnm5lLFTbxdIlbENTq0x8JD62RBHRm/lKCBIXJGw3jbhtuCERVy6wyDJEYhLbMuKiDYmwIRITESQNq1LZaFg5ynrannN2ceR1VrT7X53n/f/PL8/7CXxQzdnnGicbg2cESeAW8cD3f+83mo3qSDUPPAJ0W1IQ4nn++ijDMs2W5r7lPp7npaS0VFOqCFNoFBp9nD5BnvC/oAP3QeXHytnd2etpAhHZ6uzWl6158XkBPQaCln8uV4xV2E/swWaBUP3z+u6ibhkluxk0YZ2o/lzt9rqDULD0cfrxqvFwKlwoyba2Nuyp76s1MRqaoffoPY7n8LiMkoVRYQzLiEE7Jzs7rp0STQlCKBBEkZRWqa3V1VY/q5bL5I5Th8vjAgCdSrf5ZjNLnWVz2fbpfZxfd65rFVqtUhs4tQD5Of+sfXZgdWDGPmM32imC8rE+o9nYu9yLM4nyxPXX6xJCEgyE5fK4IqWRJEECAMuxhg8Gy7YFu5YaS+HjQkIofKxvwbGw4dzg4QZu1N0ogQIAJEF26jsJRGDXvG0GAAIAPH5P/nB+9vts3VudacYUssGUBylJMUm4XPm1cgmy/LDM7c4BAMdzXfNdp57TkKxEeSL+Pjw/vASJ//SyXueFMyRIvLLCEScAIDYiVhzacG6EoAC/ebSJS+F3AgCSlckS4uoZGF4bDg5a2l/a+r2Fy7TYtEuQ6p4q42EGNsat41O2qdsoDMu0TLWIp1b8tPgShAA1pjVig+XYqk9V83vz1ykXvou6L3ViS6fS5TzKAXyy/Zw/812msJGCpKS0IbWhPrU+KSaJIqjjP8fmbXPHtw7rkRVnEEKTVZNCR1cne+1wLXcgl2bogC4i7kRQBEUztJ/zB1hNaU09hh5h1/65IuZtc/lY+bn3/LYFEqtMUzZSNiIlpUJJiL2iJ0WjZaOKMEVwBEKoRlcz+GoQUwI7EnTgPmj/2j60OnTmPbuOSI9NN70wGRIMIZ5aLJfHNW2bXnQsOmiHj/VFy6K1Sm1BfEGyMll8Y7H+AvtLjkoh3mWsAAAAAElFTkSuQmCC');
display: inline-block;
height: 13px;
width: 1em;
font-size: 13px;
background-repeat: no-repeat;
background-size: 13px;
margin-top: 2px;
/*margin-right: -2px; we don't need this now*/
}
legend>span.undo {
width: 0em;
}
fieldset.modified>legend>span.undo {
width: 1em;
}
div.description {
padding-left: 4px;
font-size: 90%;
}
<form>
<fieldset class="field">
<legend title=""><span class='ttl'>Foo configurator</span><span class="undo" title="Restore default"></span></legend>
<label title="">
<input name="foo_type" type="radio" value="123">Foo #1</label>
<div class="description">First foo</div>
<span class="field-content">
<label><input name="foo_enable" type="checkbox" value="value">Enable</label>
<span class="undo" title="Restore default"></span>
<div class="description">Zoppity</div>
</span>
</fieldset>
<fieldset class="field modified">
<legend title=""><span class='ttl'>Bar configurator</span><span class="undo" title="Restore default"></span></legend>
<label title="">
<input name="bar_type" type="radio" value="123">Bar #1</label>
<div class="description">First bar</div>
<span class="field-content">
<label><input name="bar_enable" type="checkbox" value="value">Enable</label>
<span class="undo" title="Restore default"></span>
<div class="description">Zoppity</div>
</span>
</fieldset>
</form>
So instead of setting negative margin on child we can have 0 padding on the parent element.
In the current version(100.0.4896.88), Chrome assigns following CSS, by default, to legend elements:
legend {
display: block;
padding-inline-start: 2px;
padding-inline-end: 2px;
border-width: initial;
border-style: none;
border-color: initial;
border-image: initial;
}
We need to check if it was different before in earlier versions.
Solution 3:[3]
As of what is the reason for the change of behavior, I cannot tell.
Meanwhile, you can fix this behavior by adding
white-space: nowrap;
To your legend element, or
legend > span.undo { display: none; }
fieldset.modified > legend > span.undo { display: inline-block; }
Seems to do the job too, as well as to remove the negative margin
span.undo { margin-right: 0; }
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 |