Good day. I have table with 2 columns: the first one for labels and the second one for control elements. Labels could be short and very long.
table{
padding-bottom: 20px;
}
td{ border: 1px solid}
<table style="width:100%;">
<tr>
<td class="first-column">very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very label</td>
<td class="second-column"><input style="width:100%"/></td>
</tr>
<tr>
<td class="first-column">label</td>
<td class="second-column"><input style="width:100%"/></td>
</tr>
</table>
<table style="width:100%;">
<tr>
<td class="first-column">medium label</td>
<td class="second-column"><input style="width:100%"/></td>
</tr>
<tr>
<td class="first-column">label</td>
<td class="second-column"><input style="width:100%"/></td>
</tr>
</table>
<table style="width:100%;">
<tr>
<td class="first-column">label</td>
<td class="second-column"><input style="width:100%"/></td>
</tr>
<tr>
<td class="first-column">label</td>
<td class="second-column"><input style="width:100%"/></td>
</tr>
</table>
I need the first column to be sized by it's content (width of the longest label), but in case of very long labels it should not fill more then 50% of table. Something like this
Could you please help me to write correct css for this case? I can't use Javascript, it should be css-only solution.
My own suggestion for this would be to firstly – unless this is impractical for other reasons – consolidate all your label and <input>
elements into the same parent-element, which simplifies the alignment of all columns.
To this end I've grouped the elements into a single <fieldset>
element, itself within a <form>
:
// This JavaScript has no part to play in the resizing of elements,
// but merely logs the size of each of the two columns in the
// relevant elements (the last <label> and the last <input>:
const D = document,
logLengths = () => {
let label = D.querySelector('label:last-of-type'),
input = D.querySelector('input:last-of-type');
[label, input].forEach(
(el) =>{
el[
el.tagName.toLowerCase() === 'input' ? 'value' : 'textContent'
] = Math.round(el.getBoundingClientRect().width*10)/10 + 'px';
});
};
logLengths();
D.querySelectorAll('label').forEach(
(label) => label.addEventListener('input', logLengths)
);
/* Generic CSS reset: */
*,
::before,
::after {
box-sizing: border-box;
font-size: 16px;
line-height: 1.5;
margin: 0;
padding: 0;
}
form {
border: 1px solid currentColor;
/* using CSS logical properties to place a 1em margin
on the block axis (top/bottom) in left-to-right, top-
to-bottom languages) */
margin-block: 1em;
/* ...and a margin of auto (to centre the element) on
the inline-axis (left and right in ltr languages): */
margin-inline: auto;
padding: 0.2em;
width: 90vw;
}
fieldset {
/* in order to use CSS Grid: */
display: grid;
/* setting gaps/'gutters' between rows of 0.5em,
and 0.25em gaps between adjacent columns: */
gap: 0.5em 0.25em;
/* defining two columns, the first of which is sized
'auto', allowing the layout to be sized appropriately
to the content within the limits of other columns,
the second column is sized using the 'minmax()'
function between a minimum size of 50% (because the
maximum permitted size of the first column is 50%),
and 1fr, which is the fractional unit of the remaining
space available: */
grid-template-columns: auto minmax(50%, 1fr);
}
/* this styles the 'information' <div> that provides
guidance about the interactivity of the editable
<label> elements: */
fieldset div {
grid-column: 1 / -1;
text-align: center;
padding: 0 2rem;
}
code, kbd {
font-family: consolas, ubuntu mono, monospace;
}
kbd {
border: 1px solid #aaa;
border-radius: 0.4em;
padding: 0.1em 0.3em;
}
label {
/* to indicate interactivity: */
cursor: pointer;
/* for positioning the pseudo element: */
position: relative;
}
/* entirely irrelevant to the demo, but just to
make it look a little prettier (adjust to
taste or remove as you like): */
label::before {
content: '';
background: linear-gradient(90deg, lime, #ffff);
height: 100%;
position: absolute;
transform: scaleX(0);
transition: transform 0.3s ease-in-out;
transform-origin: 0 100%;
width: 100%;
z-index: -1;
}
label:hover::before {
transform: scaleX(1);
}
.length {
background: revert;
background-color: #fff;
border-color: transparent;
border-top: 1px solid currentColor;
cursor: not-allowed;
font-style: italic;
text-align: center;
}
<form action="#">
<fieldset>
<!-- this <div> is here simply to provide some guidance as to the
interactivity of the <label> elements for the purpose of this
demo; otherwise it can be removed entirely: -->
<div>Most labels are <code>contentEditable</code>, though they will focus the <input> element when clicked; <kbd>shift</kbd>+<kbd>tab</kbd> to focus the <label></div>
<!-- because we're using a grid (and the majority of browsers don't yet
support display: subgrid) to create the aligned columns, the <input>
elements are the following-siblings of the <label> elements; in order
to associate the <label> with the correct <input> I've added an 'id'
attribute to each <input>, and the same id-value for the associated
<label> in their 'for' attribute; this means clicking/tapping on a
<label> will focus the appropriate <input>: -->
<label for="inputElement_0" contentEditable>longer label</label>
<input id="inputElement_0">
<label for="inputElement_1" contentEditable>label</label>
<input id="inputElement_1">
<label for="inputElement_2" contentEditable>medium label</label>
<input id="inputElement_2">
<label for="inputElement_3" contentEditable>label</label>
<input id="inputElement_3">
<label for="inputElement_4" contentEditable>label</label>
<input id="inputElement_4">
<label for="inputElement_5" contentEditable>label</label>
<input id="inputElement_5">
<!-- these elements are here purely to report the length of the
grid-columns in which they appear: -->
<label for="lengthInput" class="length"></label>
<input id="lengthInput" type="text" class="length" readonly>
</fieldset>
</form>
If subgrid is available in your browser (currently it's only available in Firefox), then the following is possible:
// Again, this JavaScript has nothing to do with the resizing, but only shows
// the widths of the grid-tracks the elements are in:
const D = document,
logLengths = () => {
let label = D.querySelector('label:last-of-type'),
input = label.querySelector('input:last-of-type');
[label, input].forEach(
(el) => {
let width = Math.round(el.getBoundingClientRect().width / 10) * 10,
val = `${width}px`;
if (el.matches('label')) {
label.firstChild.nodeValue = val;
} else if (el.matches('input')) {
el.value = val;
}
});
};
logLengths();
D.querySelectorAll('label').forEach(
(label) => label.addEventListener('input', logLengths)
);
*,
::before,
::after {
box-sizing: border-box;
font-size: 16px;
line-height: 1.5;
margin: 0;
padding: 0;
}
form {
border: 1px solid currentColor;
margin-block: 1em;
margin-inline: auto;
padding: 0.2em;
width: 90vw;
}
fieldset {
display: grid;
gap: 0.5em 0.25em;
grid-template-columns: auto minmax(50%, 1fr);
}
fieldset div {
grid-column: 1 / -1;
text-align: center;
padding: 0 2rem;
}
code,
kbd {
font-family: consolas, ubuntu mono, monospace;
}
kbd {
border: 1px solid #aaa;
border-radius: 0.4em;
padding: 0.1em 0.3em;
}
label {
cursor: pointer;
position: relative;
/* Using display: grid to allow us to make use
of subgrid: */
display: grid;
/* instructing the layout engine to use the
grid-columns/grid-tracks of the parent
element: */
grid-template-columns: subgrid;
/* positioning the grid-start in the first
grid-column and the grid-end in the last
grid-column: */
grid-column: 1 / -1;
}
label::before {
content: '';
background: linear-gradient(90deg, lime, #ffff);
height: 100%;
position: absolute;
transform: scaleX(0);
transition: transform 0.3s ease-in-out;
transform-origin: 0 100%;
width: 100%;
z-index: -1;
}
label:hover::before {
transform: scaleX(1);
}
.length {
background: revert;
background-color: #fff;
border-color: transparent;
border-top: 1px solid currentColor;
cursor: not-allowed;
font-style: italic;
text-align: center;
}
<form action="#">
<fieldset>
<div>All labels are <code>contentEditable</code>, though they will focus the <input> element when clicked; <kbd>shift</kbd>+<kbd>tab</kbd> to focus the <label></div>
<!-- because we can use subgrid, we can place the <input> elements
inside of their associated <label> elements, which removes the
need to assign either a 'for' attribute to the <label> or an
'id' attribute to the <input>, since they're automatically
associated this way via nesting: -->
<label contentEditable>longer label
<input>
</label>
<label contentEditable>label
<input>
</label>
<label contentEditable>medium label
<input>
</label>
<label contentEditable>label
<input>
</label>
<label contentEditable>label
<input>
</label>
<label contentEditable>label
<input>
</label>
<label class="length">
<input type="text" class="length" readonly>
</label>
</fieldset>
</form>
References: