I am self taught and even if I get something to work, I don't like to move on if I don't understand why that something is now working.
I have an inline-block <h1>
element and the next element is an inline-block wrapper with six children; three are radio buttons with display: none;
and three are labels which are the visible elements. I have styling on the ::checked
pseudo-class which slightly changes the labels' size. When the inline-grid
element is not wrapped to the next line, I've encountered issues with my layout. When the first of the three children is selected, the layout shifts slightly, but does not when selecting either the second or third child.
My code:
*,
*::after,
*::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: rgb(236, 236, 236);
font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif;
min-height: 100vh;
}
h1 {
color: rgb(113, 0, 179);
display: inline-block;
margin-block: 1.5rem;
margin-inline: 1.25rem 5.5rem;
}
.format-select-wrapper {
align-items: end;
display: inline-grid;
grid-template-columns: 1fr 1fr 1fr;
margin-bottom: 1rem;
width: 627px;
}
input[type="radio"] {
display: none;
}
label {
background: rgb(113, 0, 179);
border-radius: 5px;
box-shadow: 0px 2px 0px black;
color: whitesmoke;
cursor: pointer;
font-weight: 600;
letter-spacing: 1px;
margin-inline: auto;
padding: 1.25rem 2rem;
text-align: center;
text-box-edge: cap alphabetic;
text-box-trim: trim-both;
user-select: none;
width: 95%;
}
label:hover,
label:focus-visible {
background: rgb(84, 44, 102);
}
input[type="radio"]:checked + label {
background: rgb(158, 0, 250);
font-size: 0.9rem;
height: 47px;
width: 175px;
}
<h1>Music Collection Catalog Lookup</h1>
<div class="format-select-wrapper">
<input type="radio" name="format" id="btn-records" value="Records" />
<label for="btn-records">RECORDS</label>
<input type="radio" name="format" id="btn-tapes" value="Tapes" />
<label for="btn-tapes">TAPES</label>
<input type="radio" name="format" id="btn-cds" value="CDs" />
<label for="btn-cds">CDS</label>
</div>
When I wrapped both elements and made that wrapper display:flex;
, everything is fine. I would love to understand what causes the weird layout shift. Finally, is what I did an acceptable solution or am I missing something here?
You're problem is caused by inline
. The way inline
works when multiple inline
text elements are on the same block axis/row is by comparing baselines of text (normally the lowest line that still touches the fontface) and attempting to ensure all baselines are at the same height. This is the root of your problem.
You can clearly see that when selecting the first label, everything is offset in that inline-grid
to make the first label's text's baseline (by default the bottom of the text) the same as the baseline of the h1
element on the same block axis. When selecting the text elements after that first child, the inline-grid
is offset to make the first label's text's baseline the same as the baseline of the h1
element yet again. At this point it may seem strange that the other label's text's baseline are not also set to be the same height as everything else on that block axis. I do not know exactly why this happens, but I am guessing that the inline
positioning algorithm only takes into consideration the first text element of a wrapper
object and after that the grid
positioning algorithm takes over inside the wrapper
object making the rest of the text's baselines different heights from the rest of the box axis.
This effect can be seen even more when adding another text element to the mix with height: 4em;
. The baseline of all of the text elements are put to the same height even though doing so requires making this new text element's content box bottom go super low on the screen.
Proposed Solution
Use a wrapper with display: flex; flex-wrap: wrap;
instead of using inline
to make by-default block
elements go on the same row. I have rewritten your code with these changes below.
*,
*::after,
*::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: rgb(236, 236, 236);
font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif;
min-height: 100vh;
}
.form-wrapper {
display: flex;
flex-wrap: wrap;
}
h1 {
color: rgb(113, 0, 179);
margin-block: 1.5rem;
margin-inline: 1.25rem 5.5rem;
}
.format-select-wrapper {
align-items: end;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
margin-bottom: 1rem;
width: 627px;
}
input[type="radio"] {
display: none;
}
label {
background: rgb(113, 0, 179);
border-radius: 5px;
box-shadow: 0px 2px 0px black;
color: whitesmoke;
cursor: pointer;
font-weight: 600;
letter-spacing: 1px;
margin-inline: auto;
padding: 1.25rem 2rem;
text-align: center;
text-box-edge: cap alphabetic;
text-box-trim: trim-both;
user-select: none;
width: 95%;
}
label:hover,
label:focus-visible {
background: rgb(84, 44, 102);
}
input[type="radio"]:checked + label {
background: rgb(158, 0, 250);
font-size: 0.9rem;
height: 47px;
width: 175px;
}
<div class="form-wrapper">
<h1>Music Collection Catalog Lookup</h1>
<div class="format-select-wrapper">
<input type="radio" name="format" id="btn-records" value="Records" />
<label for="btn-records">RECORDS</label>
<input type="radio" name="format" id="btn-tapes" value="Tapes" />
<label for="btn-tapes">TAPES</label>
<input type="radio" name="format" id="btn-cds" value="CDs" />
<label for="btn-cds">CDS</label>
</div>
<div>
More Reading