I want to have a css grid, inside each cell of the grid, I want a title with a maximum size of 100% of the width of the cell. If title is too long, i want to scroll.
This is how it looks currently with the correct scrolling behavior but with a fixed width on the long title. Instead of a fixed width, i want a width of 100% of the cell width (so the grey block should be as long as red box)
codePen: https://codepen.io/vincent2303/pen/ExwZEpW
* {
margin: 0;
padding: 0;
}
body {
padding: 50px;
}
h2 {
font-size: 2em;
white-space: nowrap;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
width: 800px;
gap: 20px;
}
.box {
background-color: #e74c3c;
height: 30vh;
}
.title-wrapper {
background-color: #bdc3c7;
width: 300px;
overflow-x: scroll;
}
<div class=grid>
<div>
<div class="title-wrapper">
<h2>long title, i can scroll---------</h2>
</div>
<div class="box"></div>
</div>
<div>
<h2>Short title</h2>
<div class="box"></div>
</div>
</div>
Does anyone have an idea to do that ?
NOTE: To make the code example simple, the .grid class has a width of 800px but in reality, it's width is defined by its parent on which i can not predict the width (i'm working on a react app and this code will implement a component used in multiple places with different sizes).
If your grid potentially contains overflowing content, you cannot work with 1fr, here's why:
1fr is just short for minmax(auto, 1fr).
minmax(a, b) becomes a (without any minmax) when a >= b is true.
So in your case your grid behaves as if you had defined it as grid-template-columns: auto 1fr;, because auto is larger than 1fr for your first column.
To fix that, you need to tell your grid that it isn't allowed to extend the cells when content becomes too wide.
Use minmax(0, 1fr) instead of 1fr:
* {
margin: 0;
padding: 0;
}
body {
padding: 50px;
}
h2 {
font-size: 2em;
white-space: nowrap;
}
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
width: 800px;
gap: 20px;
}
.box {
background-color: #e74c3c;
height: 30vh;
}
h2 {
background-color: #bdc3c7;
overflow-x: scroll;
}
<div class=grid>
<div>
<h2>long title, i can scroll------------------ - - - - ----</h2>
<div class="box"></div>
</div>
<div>
<h2>Short title</h2>
<div class="box"></div>
</div>
</div>
As commented heres a possibility:
* {
margin: 0;
padding: 0;
}
body {
padding: 50px;
}
h2 {
font-size: 2em;
white-space: nowrap;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
width: 800px;
gap: 20px;
}
.box {
background-color: #e74c3c;
height: 50vh;
max-width: 400px;
}
.title-wrapper {
background-color: #bdc3c7;
width: 100%;
overflow-x: scroll;
}
<div class=grid >
<div>
<div class="box" >
<div class="title-wrapper" >
<h2>long title, i can scroll---------------------------------</h2>
</div>
</div>
</div>
<div>
<div class="box" >
<h2>Short title</h2>
</div>
</div>
</div>
Seems to be hard without JavaScript, Dev his answers works, but it involves a fixed max-width.
I've got a solution with JavaScript that seems to work.
/* Execute when the DOM is loaded, because otherwise the HTML elements might nog be in the DOM. */
window.addEventListener( 'DOMContentLoaded', () => {
generateGridItemsWidthVariable()
} )
/* Execute when the window is resized, because the width of the boxes might change. */
window.addEventListener( 'resize', () => {
generateGridItemsWidthVariable()
} )
/* Generate the variable for the grid items. */
function generateGridItemsWidthVariable() {
/* Select all the grid items and create an array to loop trough. */
let gridItems = Array.from( document.getElementsByClassName( 'grid__item' ) )
/* Loop trough the grid items. */
gridItems.forEach( gridItem => {
/* Reset the grid item width, because otherwise the item won't resize. */
gridItem.style.setProperty( '--grid-item--width', '' )
/* Get the width of the grid item. */
let gridItemWidth = gridItem.clientWidth.toString()
/* Set the width of the grid item as a CSS variable. */
gridItem.style.setProperty( '--grid-item--width', gridItemWidth + 'px' )
} )
}
* {
margin: 0;
padding: 0;
}
body {
padding: 50px;
}
h2 {
font-size: 2em;
white-space: nowrap;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
width: 100%;
max-width: 800px;
gap: 20px;
}
.grid__item {
--grid-item--width: 10px;
}
.box {
background-color: #e74c3c;
height: 30vh;
}
.title-wrapper {
background-color: #bdc3c7;
width: 100%;
}
.title-wrapper h2 {
max-width: var(--grid-item--width);
overflow-x: scroll;
}
<div class=grid >
<div class="grid__item">
<div class="title-wrapper" >
<h2>long title, i can scroll-----------------------</h2>
</div>
<div class="box" ></div>
</div>
<div class="grid__item">
<h2>Short title</h2>
<div class="box" ></div>
</div>
</div>
Well 1fr resolves to minmax(auto, 1fr) and this means that the minimum width of the grid column is min-content, that is more then 1fr. There is a quick solution to this issue just replacing 1fr by minmax(0px, 1fr), by this, parent's width is devided between 2 columns, and this way you have the scroll bar as you wish.
* {
margin: 0;
padding: 0;
}
body {
padding: 50px;
}
h2 {
font-size: 2em;
white-space: nowrap;
}
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 20px;
}
.box {
background-color: #e74c3c;
height: 30vh;
}
h2 {
background-color: #bdc3c7;
overflow-x: auto;
}
<div class=grid>
<div>
<h2>long title, i can scroll----------------------------</h2>
<div class="box"></div>
</div>
<div>
<h2>Short title</h2>
<div class="box"></div>
</div>
</div>
Related
MCVE
I would like to nest a grid within another grid, and have a tall content box within the nested grid's content div. However no matter I set the overflow property of this content div to scroll, the content box grows causing the outer grid to exceed the viewport. So the viewport gets a scrollbar. The scrollbar of the content div is present but disabled.
// html
<div class="outergrid">
<div class="row1">
Outer Grid Header
</div>
<div class="row2">
<div class="header">
Inner Grid Header
</div>
<div class="box">
Tall Box
</div>
</div>
</div>
// style scss
*{
padding: 0px;
margin: 0px;
}
.outergrid {
display: grid;
grid-template-rows: 50px 100%;
grid-gap: 10px;
background-color: #0ff;
div {
background-color: #afa;
}
}
.row1{
grid-row: 1;
}
.row2{
grid-row: 2;
display: grid;
grid-template-rows: 50px 100%;
grid-gap: 5px;
.header {
grid-row: 1;
background-color: #ffa;
}
.contentbox {
grid-row: 2;
overflow: scroll;
.tallcontent {
background-color: #89f;
height: 1000px;
}
}
}
screenshot highlighting the problem
If I understood you correctly, then perhaps this solution (pure CSS, without SCSS) below can help you. The solution is to enforce a constraint on the height of the parent elements.
* {
padding: 0px;
margin: 0px;
}
.outergrid {
--grid-gap: 10px;
display: grid;
grid-template-rows: 50px calc(100% - 50px - var(--grid-gap));
grid-gap: var(--grid-gap);
background-color: #0ff;
max-height: 100vh;
}
.outergrid div {
background-color: #afa;
}
.row1 {
grid-row: 1;
}
.row2 {
--grid-gap: 5px;
grid-row: 2;
display: grid;
max-height: 100%;
grid-template-rows: 50px calc(100% - 50px - var(--grid-gap));
grid-gap: var(--grid-gap);
}
.row2 .header {
grid-row: 1;
background-color: #ffa;
}
.row2 .contentbox {
grid-row: 2;
overflow: scroll;
}
.row2 .contentbox .tallcontent {
background-color: #89f;
height: 1000px;
}
<div class="outergrid">
<div class="row1">
Outer Grid Header
</div>
<div class="row2">
<div class="header">
Inner Grid Header
</div>
<div class="contentbox">
<div class="tallcontent">
Tall Content
</div>
</div>
</div>
</div>
I need to setup the following DIV structure (See image below. It tells more than a 1000 words)
The structure consists of 2 colums. The main column (left) has a variable width and 100% height.
The right colums has a FIXED width of 380px and 100% height.
Then inside the right column I need 3 DIVS.
The top DIV has a fixed height of 200px and must be aligned to the top.
The bottom DIV has a fixed height of 150px and must be aligned to the bottom.
The middle DIV has a variable height and must fill up the space vertically.
This is the DIV setup And the CSS I have:
.main-content {
width: 100%;
padding: 0px;
}
.col-1 {
width: calc(100% - 380px);
min-height: calc(var(--vh, 1vh)*100);
background-color: #2693FF;
float: left;
}
.col-2 {
width: 380px;
min-height: calc(var(--vh, 1vh)*100);
float: right;
}
.col-2-top {
height: 200px;
background-color: #00B200;
}
.col-2-middle {
height: 100%;
background-color: #FF8000;
}
.col-2-bottom {
height: 100px;
background-color: #B25900;
}
<div class="main-content">
<div class="col-1"></div>
<div class="col-2">
<div class="col-2-top"></div>
<div class="col-2-middle"></div>
<div class="col-2-bottom"></div>
</div>
</div>
Then... Column 1 and 2 should stack when the viewport width becomes less than 768px.
Column 1 on top and Column 2 below it.
Like this:
I think I'm almost there, but I'm having problems with the height of the Main DIV and the heights and aligning of the DIV col-2 middle DIV. I also need a bit helpt to get these divs stack nicely above each each other.
I would suggest that you use grid layout instead of floating around your <div>s, grid layout allows you to structure your layout and separate them in columns and rows, and areas using grid-template-areas.
for max-width:748 just add media query, here is how it might be implemented:
body {
padding: 0;
margin: 0;
}
.main-content {
display: grid;
background-color: #2196F3;
grid-template-areas:
'main fixed-top'
'main variable-mid-area'
'main fixed-bottom';
background-color: #2196F3;
height: 100vh;
grid-template-columns: 1fr 380px;
grid-template-rows: 200px 1fr 150px;
}
.main-content > div {
color: #fff;
font-size: 30px;
vertical-align: middle;
display: flex;
justify-content: center;
align-items: center;
}
.main {
grid-area: main;
background-color: #2693FC;
}
.variable-mid-area {
grid-area: variable-mid-area;
background-color: #FF8015;
}
.fixed-top {
grid-area: fixed-top;
background-color:#00B21F;
}
.fixed-bottom {
grid-area: fixed-bottom;
background-color: #B2590B;
}
#media only screen and (max-width: 768px) {
.main-content {
grid-template-areas:
'main'
'fixed-top'
'variable-mid-area'
'fixed-bottom';
grid-template-rows: 300px 200px 1fr 150px;
grid-template-columns: 100%;
height: auto;
}
}
<div class="main-content">
<div class="main"> main </div>
<div class="fixed-top"> 200 </div>
<div class="variable-mid-area"> auto </div>
<div class="fixed-bottom"> 150 </div>
</div>
If you have any questions how the css works, feel free to ask them in the comments.
I know the background-colors are irrelevant but they help to visualize it.
.container {
min-width: 768px;
width: 100%;
display: grid;
grid-template-columns: calc(100% - 380px) 1fr;
min-height: 100vh;
}
.col1 {
background-color: dodgerblue;
}
.col2 {
display: flex;
flex-direction: column;
}
.col2-row1 {
height: 200px;
background-color: orange;
}
.col2-row2 {
background-color: forestgreen;
height: 100%;
}
.col2-row3 {
height: 150px;
background-color: red;
}
#media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
}
}
<div class="container">
<div class="col1">1</div>
<div class="col2">
<div class="col2-row1">2</div>
<div class="col2-row2">3</div>
<div class="col2-row3">4</div>
</div>
</div>
I'm learning CSS Grid layout and i have a problem about positioning.
What i want is to create a page layout composed by a left-side menu, top-bar menu and a main content page like the image below:
I have been able to achieve the goal, but now i want to fix the position of the top bar and sidebar while main content is scrolling.
I set position:sticky to both containers but it does not working.
How can i fix?
Here is my code:
* {
margin: 0 !important;
padding: 0 !important;
box-sizing: border-box !important;
}
.grid-container {
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: 10% 100vh;
grid-template-areas:
"LeftMenu TopMenu"
"LeftMenu Main";
}
.LeftMenu {
background-color: #a4a4a4;
grid-area: LeftMenu;
width: 200px;
height: 100%;
}
.TopMenu {
background-color: #d49494;
grid-area: TopMenu;
width: 100%;
height: 100%;
display: flex;
}
.Main {
background-color: #8990eb;
grid-area: Main;
width: 100%;
height: 100vh;
}
<div class="xdg-component-appnav-menu">
<div class="grid-container">
<div class="LeftMenu">left menu</div>
<div class="TopMenu">top menu</div>
<div class="Main">
<p style="padding-bottom: 1000px;">Content</p>
</div>
</div>
</div>
You don't need position: sticky. It's extra complication and still isn't fully supported by some browsers.
Just use overflow: auto on the main container.
.grid-container {
display: grid;
height: 100vh;
grid-template-columns: 200px 1fr;
grid-template-rows: 10% 90%;
grid-template-areas:
"LeftMenu TopMenu"
" LeftMenu Main ";
}
.LeftMenu {
grid-area: LeftMenu;
background-color: #a4a4a4;
}
.TopMenu {
grid-area: TopMenu;
background-color: #d49494;
}
.Main {
grid-area: Main;
overflow: auto; /* key adjustment */
background-color: #8990eb;
}
body {
margin: 0;
}
<div class="xdg-component-appnav-menu">
<div class="grid-container">
<div class="LeftMenu">left menu</div>
<div class="TopMenu">top menu</div>
<div class="Main">
<p style="height: 1000px;">Content</p>
</div>
</div>
</div>
I have a problem with making spaces between grid elements. This is my HTML code:
<div class="container">
<div class="col">Hello1</div>
<div class="col">Hello2</div>
<div class="col">Hello3</div>
<div class="col">Hello4</div>
</div>
And this is my CSS code:
body {
background-color: green;
}
.col {
margin: 0 2vw;
padding: 1vh 1vw;
}
.container {
width: 80vw;
margin: 0 auto;
display: grid;
grid-template-columns: 25% 25% 25% 25%;
background-color: maroon;
}
I have used this layout before, and it has worked just fine, but for some reason this doesn`t work now. I want to make space between each of the four columns, and make that space the same colour of the background color (in this case green).
Use grid gap for spacing and move the background color to grid item instead of grid container so you can see the gap:
body {
background-color: green;
}
.col {
padding: 1vh 1vw;
background-color: maroon;
}
.container {
width: 80vw;
margin: 0 auto;
display: grid;
grid-gap:2vw;
grid-template-columns: 1fr 1fr 1fr 1fr;
}
<div class="container">
<div class="col">Hello1</div>
<div class="col">Hello2</div>
<div class="col">Hello3</div>
<div class="col">Hello4</div>
</div>
I want to grow a grid to fill the remaining vertical space. There are a couple divs above a calendar of fixed height, and I'd like those to remain a fixed height. As the window grows vertically, I'd like only the calendar to change, with each row growing evenly, stopping at the bottom of the window with no scrollbars.
* {
box-sizing: border-box;
padding: 0px;
margin: 0px;
}
html,
body {
background: #92bde7;
color: #485e74;
font-family: Tahoma, Geneva, Verdana, sans-serif;
height: 100%;
}
.selection {
padding: 5px;
}
.header {
display: grid;
grid-template-columns: repeat(7, 1fr);
text-align: center;
}
.header>div {
padding: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* this is the element that needs to stretch to remaining available window space */
.calendar {
display: grid;
grid-template-columns: repeat(7, 1fr);
}
/* these elements should stretch evenly as the window grows */
.calendar>div {
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 0.5em;
min-height: 100px;
}
.prev>div,
.next>div,
.date>div {
text-align: left;
}
.prev,
.next {
background: #c9e6ff;
color: #666;
}
.date {
background: #f9feff;
}
<div class="selection">
<h1><a class="back">❮</a> 6 / 2018 <a class="forward">❯</a></h1>
</div>
<div class="header">
<div>Sunday</div>
<div>Monday</div>
<div>Tuesday</div>
<div>Wednesday</div>
<div>Thursday</div>
<div>Friday</div>
<div>Saturday</div>
</div>
<div class="calendar">
<div class="prev">30</div>
<div class="prev">31</div>
<div class="date">1
<div class="item">Stuff to do...</div>
</div>
<div class="date">2</div>
<div class="date">3</div>
<div class="date">4</div>
<div class="date">5</div>
<div class="date">6</div>
<div class="date">7</div>
<div class="date">8</div>
<div class="date">9</div>
<div class="date">10</div>
<div class="date">11</div>
<div class="date">12</div>
<div class="date">13</div>
<div class="date">14</div>
<div class="date">15</div>
<div class="date">16</div>
<div class="date">17</div>
<div class="date">18</div>
<div class="date">19</div>
<div class="date">20</div>
<div class="date">21</div>
<div class="date">22</div>
<div class="date">23</div>
<div class="date">24</div>
<div class="date">25</div>
<div class="date">26</div>
<div class="date">27</div>
<div class="date">28</div>
<div class="date">29</div>
<div class="date">30</div>
<div class="next">1</div>
<div class="next">2</div>
<div class="next">3</div>
</div>
Try wrapping the whole thing a in a flex container, then using flex properties to set your lengths. Something like this:
body {
display: flex;
flex-direction: column;
height: 100vh;
}
.selection {
flex: 0 0 50px; /* flex-grow, flex-shrink, flex-basis */
}
.header {
flex: 0 0 25px;
display: grid;
grid-template-columns: repeat(7, 1fr);
}
.calendar {
flex: 1; /* dynamic length; consumes all remaining space */
overflow: auto;
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-auto-rows: 1fr;
}
jsFiddle demo
There are several ways to accomplish this. One way to do this is to leverage vh units. I made a CodePen with your code and made a slight tweak to the height of your rows in CSS:
.calendar > div {
...
height: calc(20vh - 17px)
}
This means that each row will take up 20% of the vertical space of the screen minus 17px, which allows for the header. This assumes there will always be five rows.