CSS Grid - nowrap-text auto-placement - css

I expect something like this using css grid:
I need 4 columns with the same width and unknown number of rows.
Each item's area / span depends on its own text's length
HTML:
<div>
<b>Any</b>
<b>One Size</b>
<b>XXL</b>
<b>XL</b>
<b>L</b>
<b>no_wrap_text</b>
<b>text</b>
<b>very_very_very_long_text</b>
</div>
CSS:
div{
display: grid;
grid-template-columns: repeat(4, 60px);
grid-gap: 4px;
}
b{
white-space: nowrap;
border: 2px solid black;
text-align: center;
}
But I have to manually add this:
b:nth-child(2),
b:nth-child(6) {
grid-column: span 2;
}
b:nth-child(8) {
grid-column: span 3;
}
Or dynamically calculate each <b> element using Javascript
el.style.gridColumn = isVeryLong ? 'span 3' : isLong ? 'span 2' : 'span 1'
Can I somehow attain the same result by just using pure css?

Related

Implicit grid-area doesn't work as expected

given this html
<div class="parent">
<div class="first">1</div>
<div class="second">2</div>
<div class="third">3</div>
<div class="fourth">4</div>
</div>
and this css
.parent {
display: grid;
grid-template-areas:
'first second'
'third third'
'fourth fourth'
}
I expected the grid area "third" and "fourth" to implicitly have 1 column, which would render like so
I understand I can fix this by specifying grid-area, curious if theres another approach?
https://jsfiddle.net/qgdh2b8a/2/
This isn't a completely different approach, but you could use grid-column and not use grid-template-areas entirely. This solution also uses grid-template-columns.
.parent {
display: grid;
grid-gap: 3px;
/* Defines two columns */
grid-template-columns: 1fr 1fr;
}
.parent > div {
/* Visibility and styling */
background-color: black;
color: white;
padding: 1rem;
text-align: center;
font-family: sans-serif;
}
.third, .fourth {
/* Sets the column that the item should span */
grid-column: 1 / -1;
}
<div class="parent">
<div class="first">1</div>
<div class="second">2</div>
<div class="third">3</div>
<div class="fourth">4</div>
</div>
Here, the third and fourth classes have this style applied: grid-column: 1 / -1. The slash specifies the amount of columns that the element should span. 1 is the first column and -1 is the first from last (the last column).
Examples:
grid-column: 3 Sets the grid column to 3.
grid-column: 1 / 3 Sets the element to span columns 1 through 3.
grid-column: 1 / -2 Sets the element to span columns 1 through the 2nd last one.
You can use grid-column.
.first {
gird-column: 1 / 3; //like this
}
This tells the grid to start from the beginning of the first column, till the beginning of the third column.
The same can work for grid-rows.
You can read more here: grid-column

Can I group group dl/dd/dt displays two different ways on same page?

The following script displays exactly the same on each side of the screen. What I am trying to discover is how to group one set of dl/dt/dd definitions to be left side as normal with the dd below the dt as multiple lines but another group that will show the dt/dd on the same single lines.
I cannot figure out how to create two different dt/dd displays for the same page.
If you execute the script with the dl/dt/dd section commented out and then a second time with it in you will see the effects I'm trying to achieve, but both the multiple line and single line displays occurring on the same page as the same display. I'm thinking that there must be a way to define the CSS statements to act differently at different places on the same page. Obviously I would not do the displays in a real project, the side-by-side display is for question demonstration purposes only.
Is there a way to do this?
I tried creating a class with the modified dl/dt/dd statements and then be referenced as a class. That approach does not seem to work.
<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0, user-scalable=yes"/>
<title> DL/DT/DD Demos </title>
<!-- link rel="stylesheet" href="common.css" media="screen" -->
<!-- See: https://medium.com/#jakehyer/css-grid-auto-fit-minmax-e54f99989668 -->
<style>
main {
display: grid;
gap: 0.25rem;
}
/* 12.5rem (9 boxes), 16 (7), 18 (6), 20 (5), 25 (4), 33rem (3), 40rem (2) all work */
/* */
.gridCols9 { grid-template-columns: repeat(auto-fit,minmax(12.5rem, 1fr)); }
.gridCols7 { grid-template-columns: repeat(auto-fit,minmax(16rem, 1fr)); }
.gridCols6 { grid-template-columns: repeat(auto-fit,minmax(18rem, 1fr)); }
.gridCols5 { grid-template-columns: repeat(auto-fit,minmax(20rem, 1fr)); }
.gridCols4 { grid-template-columns: repeat(auto-fit,minmax(25rem, 1fr)); }
.gridCols3 { grid-template-columns: repeat(auto-fit,minmax(33rem, 1fr)); }
.gridCols2 { grid-template-columns: repeat(auto-fit,minmax(40rem, 1fr)); }
/* */
main > fieldset { border: 1px solid black; }
.fontInfo {
white-space: pre-wrap;
font-family: monospace;
}
h3 {
background-color: lightgreen; text-align: center;
font-size: 1.2rem; cursor: pointer;
margin: 0; padding: 0;
}
article { margin-top: 0; display: block; }
/* Following from: https://stackoverflow.com/questions/1713048/how-to-style-dt-and-dd-so-they-are-on-the-same-line */
/* Comment out following for entirely different display */
dl {
display: grid;
grid-template-columns: max-content auto;
}
dt { grid-column-start: 1; }
dd { grid-column-start: 2; }
/* */
</style>
</head><body>
<main class="gridCols4">
<fieldset> <legend> Normal </legend>
<article><h3>DL/DT/DD Display</h3></article>
<dl>
<dt>Mercury</dt>
<dd>Mercury (0.4 AU from the Sun) is the closest planet to the Sun and the smallest planet.</dd>
<dt>Venus</dt>
<dd>Venus (0.7 AU) is close in size to Earth, (0.815 Earth masses) and like Earth, has a thick silicate mantle around an iron core.</dd>
<dt>Earth</dt>
<dd>Earth (1 AU) is the largest and densest of the inner planets, the only one known to have current geological activity.</dd>
</dl>
</fieldset>
<fieldset> <legend> Modified </legend>
<article><h3>DL/DT/DD Display</h3></article>
<dl class="singleLines">
<dt>Mercury</dt>
<dd>Mercury (0.4 AU from the Sun) is the closest planet to the Sun and the smallest planet.</dd>
<dt>Venus</dt>
<dd>Venus (0.7 AU) is close in size to Earth, (0.815 Earth masses) and like Earth, has a thick silicate mantle around an iron core.</dd>
<dt>Earth</dt>
<dd>Earth (1 AU) is the largest and densest of the inner planets, the only one known to have current geological activity.</dd>
</dl>
</fieldset>
</main>
</body></html>
Maybe I'm missing something, but it seems like you just need to set the dt/dd's as child elements of dl with the '>' character. Here's an example showing the two different version views:
main {
display: grid;
gap: 0.25rem;
}
/* 12.5rem (9 boxes), 16 (7), 18 (6), 20 (5), 25 (4), 33rem (3), 40rem (2) all work */
/* */
.gridCols9 { grid-template-columns: repeat(auto-fit,minmax(12.5rem, 1fr)); }
.gridCols7 { grid-template-columns: repeat(auto-fit,minmax(16rem, 1fr)); }
.gridCols6 { grid-template-columns: repeat(auto-fit,minmax(18rem, 1fr)); }
.gridCols5 { grid-template-columns: repeat(auto-fit,minmax(20rem, 1fr)); }
.gridCols4 { grid-template-columns: repeat(auto-fit,minmax(25rem, 1fr)); }
.gridCols3 { grid-template-columns: repeat(auto-fit,minmax(33rem, 1fr)); }
.gridCols2 { grid-template-columns: repeat(auto-fit,minmax(40rem, 1fr)); }
/* */
main > fieldset { border: 1px solid black; }
.fontInfo {
white-space: pre-wrap;
font-family: monospace;
}
h3 {
background-color: lightgreen; text-align: center;
font-size: 1.2rem; cursor: pointer;
margin: 0; padding: 0;
}
article { margin-top: 0; display: block; }
/* Following from: https://stackoverflow.com/questions/1713048/how-to-style-dt-and-dd-so-they-are-on-the-same-line */
/* Comment out following for entirely different display */
dl.singleLines {
display: grid;
grid-template-columns: max-content auto;
}
dl.singleLines>dt { grid-column-start: 1; }
dl.singleLines>dd { grid-column-start: 2; }
<main class="gridCols4">
<fieldset>
<legend> Normal </legend>
<article>
<h3>DL/DT/DD Display</h3>
</article>
<dl>
<dt>Mercury</dt>
<dd>Mercury (0.4 AU from the Sun) is the closest planet to the Sun and the smallest planet.</dd>
<dt>Venus</dt>
<dd>Venus (0.7 AU) is close in size to Earth, (0.815 Earth masses) and like Earth, has a thick silicate mantle around an iron core.</dd>
<dt>Earth</dt>
<dd>Earth (1 AU) is the largest and densest of the inner planets, the only one known to have current geological activity.</dd>
</dl>
</fieldset>
<fieldset>
<legend> Modified </legend>
<article>
<h3>DL/DT/DD Display</h3>
</article>
<dl class="singleLines">
<dt>Mercury</dt>
<dd>Mercury (0.4 AU from the Sun) is the closest planet to the Sun and the smallest planet.</dd>
<dt>Venus</dt>
<dd>Venus (0.7 AU) is close in size to Earth, (0.815 Earth masses) and like Earth, has a thick silicate mantle around an iron core.</dd>
<dt>Earth</dt>
<dd>Earth (1 AU) is the largest and densest of the inner planets, the only one known to have current geological activity.</dd>
</dl>
</fieldset>
</main>

How to prevent CSS Flex / Grid layouts to be reversed on dir="RTL"

I have a simple div (grid layout but the same would apply to flexbox), which shows three different text elements.
The problem is that whenever my html's dir attribute is set to rtl, the order of the grid items changes, and I'd like to be able to prevent that change in that specific case (where content order is not based on writing direction, but for semantic value.
For example's sake, here's what my items look like:
let direction = 'rtl';
document.getElementById('toggle-direction').addEventListener('click', () => {
direction = direction === 'rtl' ? 'ltr' : 'rtl';
document.documentElement.setAttribute('dir', direction);
});
.container {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: repeat(3, 1fr);
border-radius: 5px;
padding: 10px;
}
.left-arrow {
grid-column: 1 / 2;
grid-row: 1 / 2;
margin-inline-end: auto;
}
.center-content {
grid-row: 1 / 2;
grid-column: 2 / 3;
text-align: center;
}
.right-arrow {
grid-row: 1 / 2;
grid-column: 3 / 4;
margin-inline-start: auto;
}
#toggle-direction {
grid-row: 2 / 3;
grid-column: 1 / 4;
}
<div class="container">
<span class="left-arrow"><</span>
<span class="center-content">Center content</span>
<span class="right-arrow">></span>
<button id="toggle-direction">Toggle direction</button>
</div>
PS: I could add direction: ltr; to the .container selector, but that would create an undesired styling, since I want to use inline-oriented styling.
Is my understanding you only want the arrows to keep their orientation.
So you should isolate and wrap the elements you want the direction to consistent in containers with consistent flow direction (and put everything you want to update outside that containers) like so :
let direction = 'rtl';
document.getElementById('toggle-direction').addEventListener('click', () => {
direction = direction === 'rtl' ? 'ltr' : 'rtl';
document.documentElement.setAttribute('dir', direction);
});
.container {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: repeat(3, 1fr);
}
.ltr {
direction: ltr;
}
.left-arrow {
grid-column: 1 / 2;
text-align: center;
}
.center-content {
grid-column: 2 / 3;
text-align: center;
}
.right-arrow {
grid-column: 3 / 4;
text-align: center;
}
#toggle-direction {
grid-column: 1 / 4;
}
<!-- inherit direction flow -->
<div class="container ltr">
<!-- ltr direction flow -->
<p class="left-arrow">Left arrow</p>
<p class="center-content">Center content</p>
<p class="right-arrow">Right arrow</p>
</div>
<div class="container">
<!-- inherit direction flow -->
<button id="toggle-direction">Toggle direction</button>
</div>
Edits done: wrapped the first row you had inside the container in another container (class="colum rtl") that always has a direction.
Small css updates to keep the view.
Alternatively you could place the arrows container inside the container with the button. But in order to have consistent rtl direction you will need to wrap them there in a container (with class="rtl") and you need a little more updates to your css to keep the aspect.
Alternatively if there is really no way for you to isolate all the components you need to have consistent direction you could for some specific cases create a class class="inherit-direction" (for example) and in your js, when you change the direction of the document, change the containers direction with that class as well (document.getElementsByClassName("inherit-direction") and iterate trough them).
But I would strongly advise to use this last option only as last measure and limit the number of uses as much as possible.

CSS Grid auto placement in IE/EDGE

While using the old CSS grid spec that is supported by IE 11 and EDGE. Is it possible for the grid items to be auto placed like the current spec?
i.e. to not have to define the column on a grid item:
.item:nth-child(1) {
-ms-grid-column: 1;
}
.item:nth-child(2) {
-ms-grid-column: 2;
}
.item:nth-child(n) {
-ms-grid-column: n;
}
https://codepen.io/JoeHastings/pen/mMPoqB
The answer is NO (unfortunately).
Old specs section about auto-placement has such preamble
This section describes early thinking around automatic placement of Grid Items. Multiple algorithms are possible for such a feature. One is proposed here.
Run this code in IE/Edge and you'll see a lot of rows with 1 in console because IE/Edge stacks all grid items in first cell and you can't force IE/Edge to place grid items automatically. Setting -ms-grid-column and -ms-grid-row to auto won't change anything, because this value is not supported (as you can see in MSDN links). Demo:
var gridItems = document.querySelectorAll(".grid__item");
for (var i = 0; i < gridItems.length; i++) {
var gridItem = gridItems[i];
console.log(window.getComputedStyle(gridItem)["-ms-grid-row"]);
console.log(window.getComputedStyle(gridItem)["-ms-grid-column"]);
}
.grid {
display: -ms-grid;
-ms-grid-columns: 100px 100px 100px;
-ms-grid-rows: 100px 100px 100px;
}
.grid__item {
-ms-grid-row: auto;
-ms-grid-column: auto;
background-color: tomato;
color: white;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
}
<div class="grid">
<div class="grid__item">One</div>
<div class="grid__item">Two</div>
<div class="grid__item">Three</div>
<div class="grid__item">Four</div>
<div class="grid__item">Five</div>
<div class="grid__item">Six</div>
<div class="grid__item">Seven</div>
<div class="grid__item">Eight</div>
<div class="grid__item">Nine</div>
</div>

CSS Grid vertical columns with infinite rows

I have a list of items of unknown length (from a CMS). I want to display them in 2 vertical columns reading down. e.g.
1 4
2 5
3 6
etc...
I am trying to achieve this with CSS grid, however, it doesn't seem possible unless you set the number of rows up front. I have tried grid-auto-flow: column as per https://gridbyexample.com/examples/example18/ but this just adds additional columns when it gets to the end.
I feel like this should be possible with grid, but I can't find a way. Anyone have any ideas?
P.S. Please don't suggest CSS text columns.
Without knowing the exact amount of items this is not possible with CSS grid alone.
The only way to get around this limitation is to add a class to your second half of the items.
body {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-flow: row dense;
/* extra styles */
grid-gap: 0.5rem;
}
span {
grid-column-start: 1;
/* extra styles */
background-color: #def;
padding: 0.5rem;
}
.second-half {
grid-column-start: 2;
/* extra styles */
background-color: #abc;
}
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span class="second-half">5</span>
<span class="second-half">6</span>
<span class="second-half">7</span>
Example:
// This is just to simulate infinite scrolling
var counter = 9;
document.addEventListener('scroll', function(e) {
if (document.body.scrollTop > 50 || document.documentElement.scrollTop > 50) {
var span = document.createElement('span');
span.innerHTML = ++counter;
document.body.appendChild(span);
}
})
body {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-rows: 200px;
/* how much height must each element occupy! change that! */
grid-gap: 0.5rem;
}
span {
background: #3A3A3A;
text-align: center;
color: #FFFFFF;
line-height: 200px;
font-size: xx-large;
}
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
<span>6</span>
<span>7</span>
<span>8</span>
One solution if your HTML is generated you can calculate the grid-template-rows property on the container element with Math.ceil( NUM_ITEMS / NUM_COLUMNS )
In React:
function VerticalColumns(props) {
// props.numColumns matches `grid-template-columns` on `.container` element
const numRows = Math.ceil(props.items.length / props.numColumns);
const style = {
gridTemplateRows: `repeat(${numRows}, 1fr)`,
};
return (
<ul className='container' style={ style }>
{ props.items.map((item, index) => (
<li key={index}>{ item }</li>
)) }
</ul>
)
}
Base CSS:
.container {
display: grid;
grid-auto-flow: column;
grid-template-columns: repeat(2, 1fr);
}
You can use a flex in which there is a container and a flex item. You can limit the height of the container and then wrap the contents of flex to continue in the next column :-
<body>
<div class="container">
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
</div>
</body>
CSS:
.container {
height: 300px;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
Read more about flexbox

Resources