I'm having difficulty finding the right technology/technique to display my layout. See picture:
Desired layout picture
This layout is similar to a calendar. An example cell is denoted in blue. The challenging part for me is a cell may have a child element that should span multiple cells/columns. The width of the calendar is full width, so each cell is of dynamic width.
I was originally using an HTML table but found that to be inflexible, so I have started using CSS Grid. With CSS Grid I still don't appear to be able to accomplish the desired layout. It looks like only direct child elements of the grid-container can be placed into the grid (in my case, these are cells). I desire the child elements of the cells to also be placed throughout the grid. This appears somewhat supported using "display: contents", but this is not support on all browsers, such as IE and Edge. I also see mention of CSS subgrid specification, but that doesn't appear to be available/supported yet.
Are there any other layout techniques or strategies which should allow for my desired layout design?
Edit: Here is a fiddle: https://jsfiddle.net/sy70c2j6/1683. I'd like some sort of styling to be able to specify that span2 and span3 span across 2 or 3 cells respectively.
// This doesn't accomplish it, but I desire something like this
.span2 {
grid-column: span 2;
}
You could kinda do this with grid-layout. You could create rows and for each row draw a line with :after pseudo-element. And you could also create columns with absolute positioned elements. Then in each row you can create cells that can span on multiple columns.
.grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
position: relative;
}
.row {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-gap: 20px;
grid-column: span 7;
position: relative;
}
.cols {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
height: 100%;
width: 100%;
display: grid;
grid-template-columns: repeat(7, 1fr);
z-index: -1;
grid-gap: 20px;
}
.cols > div {
border-left: 1px solid black;
grid-column: span;
}
.row:after {
content: '';
border-bottom: 1px solid black;
width: 100%;
position: absolute;
left: 0;
bottom: 0;
right: 0;
}
body {
padding: 0;
margin: 0;
}
.cell {
border: 1px solid #8EBA75;
background: #D5E7D3;
padding: 20px;
}
.row-1 {grid-row: 1}
.row-2 {grid-row: 2}
.row-3 {grid-row: 3}
.col-1-4 {grid-column: 1 / 4;}
.col-1-2 {grid-column: 1 / 2;}
.col-3-4 {grid-column: 3 / 4}
.col-5-7 {grid-column: 5 / 7}
.col-3-5 {grid-column: 3 / 5}
<div class="grid">
<div class="row">
<div class="cell col-1-4 row-1"></div>
<div class="cell col-1-2 row-2"></div>
</div>
<div class="row">
<div class="cell col-3-4 row-1"></div>
<div class="cell col-5-7 row-1"></div>
<div class="cell col-3-4 row-2"></div>
<div class="cell col-3-5 row-3"></div>
</div>
<div class="cols">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
Related
Is it possible to use CSS grid to auto-fit the columns in a row to always take up the whole width?
I know this would be possible if you knew the number of columns, but is it possible with a dynamic number of columns?
Image for reference of what I'd like to achieve.
column example image
This is what I have so far, but you can see that the lower row item doesn't take up all the row width.
.wrapper {
display: grid;
grid-template-columns: 200px 200px;
column-gap: 20px;
}
.grid {
border: solid #FF8181 1px;
display: grid;
grid-template-rows: 40px repeat(8, minmax(0, 1fr));
width: 200px;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
}
.row-item {
background: #FFC555;
border: 1px solid #835600;
width: 100%;
height: 40px;
}
.item-1, .item-1 {
grid-row: 2 / span 1;
}
.item-2 {
grid-row: 6 / span 1;
font-size: 12px;
}
<div class='wrapper'>
<div class='grid'>
<div class='row-item item-1'></div>
<div class='row-item item-1'></div>
<div class='row-item item-2'>I'm too short</div>
</div>
<div class='grid'>
<div class='row-item item-1'></div>
<div class='row-item item-1'></div>
<div class='row-item item-1'></div>
<div class='row-item item-2'>Should be the whole width</div>
</div>
</div>
you can stretch a element in a grid over the whole width by using:
grid-column: 1 / all;
or
grid-column: 1 / -1;
unfortunately does it affect the other elements in the same grid.
A solution like "span last-column" doesnt exist yet, but is already discussed: https://github.com/w3c/csswg-drafts/issues/2402
Maybe they will implement the function soon. Good luck anyways
What I need doesn't exist yet. This does indeed look like the latest update:
A solution like "span last-column" doesnt exist yet, but is already
discussed: https://github.com/w3c/csswg-drafts/issues/2402
I calculated overlapping grid items and rendered items in subgrids based on this answer:
Group multiple overlapping timeblocks that may not have a direct overlap
With js its different.
Find the grid
Get the Style of the Grid
Filter the Style of the Grid for grid-template-columns
Set the Element grid-column to the number of columns
Your code will look like this:
window.addEventListener("DOMContentLoaded", function(){
// find the elemen by the class and safe it as grid
let grid = document.querySelector(".grid")
// sage the style of the element
const gridComputedStyle = window.getComputedStyle(grid);
// get the grid-template-columns style poperty of the element and format them to a useful value
const gridColumnCount = gridComputedStyle.getPropertyValue("grid-template-columns").split(" ").length
// log for debug
console.log(gridColumnCount)
// set the column style of the element to the span of the variable
document.getElementById("item-2").style.gridColumn = "1 / span " + gridColumnCount;
// set the row style because it got overwritten by the line above
document.getElementById("item-2").style.gridRow = "6 / span " + gridColumnCount;
})
note that i have changed your html and css too:
html:
<div class='grid'>
<div class='row-item item-1'></div>
<div class='row-item item-1'></div>
<div class='row-item item-1'></div>
<div class="row-item" id="item-2">whole width</div>
</div>
Just deleted the wrapper for a better overview
CSS:
.grid {
border: solid #FF8181 1px;
display: grid;
grid-template-rows: repeat(8, minmax(0, 1fr));
width: 300px;
grid-template-columns: repeat(auto, minmax(0, 1fr));
}
.row-item {
background: #FFC555;
border: 1px solid #835600;
width: 100%;
height: 40px;
}
.item-1, .item-1 {
grid-row: 2;
}
.item-2 {
grid-row: 5;
font-size: 12px;
}
I had to change the grid-template-value of grid from auto-fit to auto. Otherwise there would be a bug when counting the columns
can someone please tell me why the 3rd div is looking that way instead of being right under the 1st div? And what would I need to write in my CSS file to make the 3rd div positioned right under the 1st one?
This is the HTML:
<div class="div-main">
<div class="div-1">
1
</div>
<div class="div-2">
2
</div>
<div class="div-3">
3
</div>
</div>
This is the CSS:
.div-main {
border: 5px solid yellow;
padding: 15px;
}
.div-1 {
border: 1px solid black;
padding: 15px;
width: 30%;
height: 30vh;
display: inline-block;
}
.div-2 {
border: 1px solid black;
padding: 15px;
width: 69%;
height: 50vh;
display: inline-block;
}
.div-3 {
border: 1px solid black;
padding: 15px;
width: 30%;
height: 30vh;
display: inline-block;
}
Why this is happening?
By default, the block elements, whether they are block or inline-block, will fill all the available space in the row, so if the blocks should have an overlapping situation, you can't achieve it with block approaches.
How to solve it?
With the CSS grid approach, you can make sure how each element should be positioned in your box. You can specify how your columns should be distributed by using the grid-template-columns, and how your rows should do it, with the grid-auto-rows property. You can also ensure how much of your current available space should be filled with the children by using grid-column and grid-row on each child.
The grid approach:
.div-main {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
grid-auto-rows: minmax(100px, auto);
}
.div-1, .div-2, .div-3 {
border: 1px solid black;
}
.div-1 {
grid-column: 1 / 2;
grid-row: 1;
}
.div-2 {
grid-column: 2 / 4;
grid-row: 1 / 3;
}
.div-3 {
grid-column: 1;
grid-row: 2 / 3;
}
<div class="div-main">
<div class="div-1">1</div>
<div class="div-2">2</div>
<div class="div-3">3</div>
</div>
How the above code works?
In this example by default,
I distributed the available space for the column into 3 even blocks (grid-template-columns: repeat(3, 1fr)).
I set the minimum of each row to be 100px and allow them to grow if other blocks in the container want them to (grid-auto-rows: minmax(100px, auto)).
At last, I set the children to fill the available space the way I wanted (grid-column: *; grid-row: *;).
NOTE1: If in any case you want to make the third child gets bigger in height in comparison to the second child you can change the second part of grid-row: 2 / 3; to a bigger number like grid-row: 2 / 4;.
.div-main {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
grid-auto-rows: minmax(100px, auto);
}
.div-1, .div-2, .div-3 {
border: 1px solid black;
}
.div-1 {
grid-column: 1 / 2;
grid-row: 1;
}
.div-2 {
grid-column: 2 / 4;
grid-row: 1 / 3;
}
.div-3 {
grid-column: 1;
grid-row: 2 / 4;
}
<div class="div-main">
<div class="div-1">1</div>
<div class="div-2">2</div>
<div class="div-3">3</div>
</div>
NOTE2: You can learn more about how CSS grid works in the CSS-tricks website with more examples.
Here I don't directly say why; I think others have done a great job there. Here I show how you can place items in columns in a container, and then forced some sizing to show the effect of doing that within individual blocks - and why sizing to the containing element might be more desirable at times.
I added a left/right block to illustrate the effect of creating logical groups of elements to work with by simply wrapping them in a group-left and group-right thus if you change grid-template-columns: minmax(150px, 30%) 30%; to grid-template-columns: minmax(150px, 30%) 1fr; the right group takes up whatever is remaining - a typical left/right layout where you have say links in the left group for example.
The only difference between the to major containers here is the content of each and those styles added to those contained elements.
.parent {
display: grid;
grid-template-columns: minmax(150px, 30%) 30%;
}
.group-left {
border: solid purple 3px;
background-color: #77008822;
}
.group-left>* {
margin: 1rem;
}
.group-right {
border: solid orange 3px;
}
.content-block {
/* just here to center the text in both directions */
display: grid;
place-items: center;
border: 1px solid;
}
/* below here is now just color and border styling and some forced sizing */
.div-main {
border: 5px solid yellow;
/* padding: 15px;*/
}
.div-1 {
border: 1px solid black;
/* force a height */
height: 30vh;
}
.div-2 {
border: 1px solid black;
/* force a height */
height: 50vh;
background-color: #eeffdd;
/* this can make the second block wider and potentially overlap the next column */
width: 20em;
}
.div-3 {
border: 1px solid black;
/* force a height */
height: 30vh;
}
.forced-height {
height: 30vh;
background-color: #44ddff33;
}
.img-force {
border: dashed 2px cyan;
width: 50%;
height: 30vh;
}
<div class="parent div-main">
<div class="group-left">
<div class="content-block div-1">1 is a happy one
</div>
<div class="content-block div-2">
2 here
</div>
</div>
<div class="group-right">
<div class="content-block div-3">
3 is not sad
</div>
</div>
</div>
<div class="parent">
<div class="group-left">
<div class="content-block">1 is a happy one
</div>
<div class="content-block forced-height">
2 here
</div>
</div>
<div class="group-right">
<div class="content-block">
<img class="img-force" src="" alt="no soup for you"> 3 is not sad
</div>
</div>
</div>
Inline elements can't position themselves like what you want as they're block level elements. You can use CSS grid and set div-2 to span two rows. There's a lot to grid but it's really flexible. Have a look. I've annotated the CSS so you can see how I've done it.
Some good resources on CSS tricks and here's a video by Kevin Powell that's a handy introduction.
.div-main {
border: 5px solid yellow;
padding: 15px;
/* this makes a grid layout with 2 columns and as many rows as needed. */
/* There's only 3 divs so that'll automatically give us 2 rows */
/* The grid-template-columns property is the width of each column - though there are some qualifications with this */
display: grid;
grid-template-columns: 30% 69%;
}
.div-main>div {
/* I've moved this to its own rule so you don't need to repeat yourself */
border: 1px solid black;
padding: 15px;
}
.div-1 {
/* I've kept the height of your original divs */
height: 30vh;
}
.div-2 {
height: 50vh;
/* setting grid-row: span 2 makes this div use two rows when it's being displayed */
/* this allows the third div to appear to the left */
/* in effect there's 4 cells in this grid but the last, bottom right cell is taken up by div-2 as we've told it to span 2 rows */
grid-row: span 2;
}
.div-3 {
height: 30vh;
}
<div class="div-main">
<div class="div-1">
1
</div>
<div class="div-2">
2
</div>
<div class="div-3">
3
</div>
</div>
I am trying to have a UI like this :
This is how I have tried doing it :
A div at the top as cover Image
The next div containing two child divs as profile photo & UI details as -
<div>Image Cover photos<div>
<div style="display : flex;">
<div>profile photo</div>
<div>UI Details Card</div>
</div>
Then I'm using the transform : translateY(-50%) to the profile photo div to move 50 percent of the portion on top of the background cover photo.
This, however creates a problem, the UI details remains at the same place(which is ideal), but the baseline has been changed.I want it to have only 50% of the height, so the baseline matches with the profile photo as well, and also UI details wcard will have some text, I do not want it to overlap on the Cover Image background as well(as that of profile photo). How can I achieve this?
One way to solve this problem is to use CSS grid to place everything.
body {
padding: 100px;
}
.card {
height: 300px;
width: 300px;
display: grid;
grid-template-columns: 1fr 2fr;
grid-template-rows: repeat(3, 1fr);
outline: 1px solid;
}
.details {
background-color: blanchedalmond;
grid-area: 3/2/4/3;
}
.photo {
background-color: aquamarine;
grid-area: 2/1/4/2;
}
.cover {
grid-area: 1/1/3/3;
background-color: grey;
}
.cover img,
.photo img {
height: 100%;
width: 100%;
}
<div class="card">
<div class="cover">
<img src="https://source.unsplash.com/random/?1" alt="">
</div>
<div class="photo">
<img src="https://source.unsplash.com/random/" alt="">
</div>
<div class="details">Details here</div>
</div>
Here's a diagram showing the different grid areas :
Notice the overlap region between the blue box (photo) and the red box (cover). Since the photo div appears after the cover div in the DOM, the photo div will have higher priority and will occupy this overlap region.
You can make a 2 column, 3 row grid and place items where you want them.
Here's a simple example:
.container {
display: grid;
grid-template-columns: 1fr 4fr;
grid-template-rows: 1fr 1fr 1fr;
width: 30vw;
height: 20vw;
}
.container :nth-child(1) {
grid-column: 1 / span 2;
grid-row: 1 / span 2;
background: red;
}
.container :nth-child(2) {
grid-column: 1;
grid-row: 2 / span 2;
background: green;
}
.container :nth-child(3) {
background: blue;
}
<div class="container">
<div></div>
<div></div>
<div></div>
</div>
Obviously you will want to alter the relative sizes of the grid to suit your particular case.
Note: it depends on whether you want to put some text in the first item so that it comes directly above the second item or not as to whether you start the first div in the first column or the second column.
I'd want one child element to be 1/3 the size of its parent container and the other 2/3. The parent container is in a main container that uses grid-layout.
The parent container in question spans 2 columns of the main container it is contained in.
I've tried using margin, but it doesn't work: when I switch from a small screen to a large screen it moves to leave a gap.
<div class="main-container" style="display:grid; grid-template-columns: 1fr 2fr 1fr"> 1
<div></div>
<div class="parent-container" style="grid-column:span 2">
<div class="child-1"></div>
<div class="child-2"></div>
</div>
</div>
css code I've tried:
.child-1{
margin-left:-20%;
}
Child-1 must be 1/3 of parent-container and child-2 2/3 of parent-container
You can make use of the nested grid container. The dotted borders signify the width that the child elements take from the parent container. The solid borders are for the main container's child elements. Rest is explained in comments.
.main-container {
display: grid;
grid-template-columns: 1fr 2fr 1fr; /* 1/4th for Extra, 2/4th for Parent, 1/4th for Extra */
grid-auto-flow: column; /* Normal flow is row */
}
.parent-container {
display: grid; /* Nested Grid */
grid-template-columns: 1fr 2fr; /* 1/3rd for Child 1, 2/3rd for Child 2 of parent container*/
grid-auto-flow: column;
}
/* Extra styling for snippet, you just need the above logic */
.main-container {
font-size: 1.15em;
font-family: Sans-Serif;
}
.parent-container {
border: 2px solid black;
}
.parent-container>div {
background: #6660CE;
padding: 10px;
color: #fff;
text-align: center;
border: 2px dotted black;
}
.main-container>div {
background: #6660CE;
padding: 10px;
color: #fff;
text-align: center;
}
<div class="main-container">
<div class="extra">Extra</div>
<div class="parent-container">
<div class="child-1">Child 1 </div>
<div class="child-2">Child 2</div>
</div>
<div class="extra">Extra</div>
</div>
Simply give the parent element two columns with grid-template-columns, where the second column is twice the size of the first column (i.e. 1fr and 2fr).
This can be seen in the following:
.container {
display: grid;
grid-template-columns: 1fr 2fr;
}
.child-1 {
background: red;
}
.child-2 {
background: blue;
}
.child {
height: 50px;
}
<div class="container">
<div class="child child-1"></div>
<div class="child child-2"></div>
</div>
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.child:nth-child(1) {
grid-area: 1 / 1;
background-color: #9341aa;
}
.child:nth-child(2) {
grid-area: 2 / 1 / span 1 / span 2;
background-color: #ab41aa;
}
.child:nth-child(3) {
grid-area: 3 / 1 / span 1 / span 3;
background-color: #cab332;
}
<div class="container">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
</div>
Using calc, something like this should do the trick:
HTML:
<div class="main-container">
<div></div>
<div class="parent-container">
<div class="child-1"></div>
<div class="child-2"></div>
</div>
</div>
CSS:
.parent-container{
position: relative;
width: 100%;
height: 100%;
}
.parent-container .child-1,
.parent-container .child-2{
width: calc(100% / 3);
height: 100%;
float: left;
}
I am trying to create a disk utilization chart React component, where I need different colored boxes of various sizes based on the percentage.
How do I do this in CSS & HTML? What is the best way to have these boxes sized based on a percentage?
https://codepen.io/blitzkriegz/pen/Gzgpzb
<div class="wrapper">
<div class="box i">
<div class="box j">20% Item 1</div>
<div class="box k">30% Item 2</div>
<div class="box l">40% Free</div>
</div>
</div>
CSS:
body {
margin: 5px
}
.box {
background-color: gray;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
}
.box .box {
margin: 10px;
background-color: darkblue;
}
.j {
margin: 10px;
background-color: green;
}
.wrapper {
display: grid;
grid-gap: 10px;
grid-template-columns: 1fr 2fr 1fr 1fr 2fr 1fr;
background-color: #fff;
color: #444;
}
.i {
padding: 0;
grid-gap: 2px;
grid-column: auto / span 3;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
Consider using flex-box for this, rather than grid.
Flex box gives you an easy and convenient means of achieving what you want here; to control and set variable percentage based widths of box elements in a horizontal arrangement (along the "row" axis) without the need for the clearfix trick.
One approach might be as follows:
body {
margin: 5px
}
.box {
margin: 10px;
background-color: darkblue;
}
.j {
background-color: green;
/* Set percentage explicitly */
width:20%;
}
.k {
/* Set percentage explicitly */
width:30%;
}
.l {
/* Set percentage explicitly */
width:40%;
}
.wrapper {
padding: 0;
background-color: gray;
color: #fff;
border-radius: 5px;
font-size: 150%;
/* Add this to use flex-box */
display:flex;
flex-direction:row;
}
<!-- simplifiy you markup -->
<div class="wrapper">
<div class="box j">20% Item 1</div>
<div class="box k">30% Item 2</div>
<div class="box l">40% Free</div>
</div>
If you're using ReactJS to render the markup via JSX (I noticed reactjs is tagged in your OP), you can use this approach to set the widths of boxes dynamically via inline styling:
<div className="wrapper">
<div className="box j" style={{ width : '20%' }}>20% Item 1</div>
<div className="box k" style={{ width : '30%' }}>30% Item 2</div>
<div className="box l" style={{ width : '40%' }}>40% Free</div>
</div>
Hope that helps!