Make element to take 100% of visible empty space without JS - css

So... How I do something like this with CSS only? Watch red element, it scales not scrolls:
Currently it works via JS, I catch onscroll and update size of the element according to calculated free space.
This is how it works now:
https://codepen.io/bswan-the-decoder/pen/OJPVxZj
<div class="header">
Header
</div>
<div class="header-spacer">
</div>
<div class="content">
<div class="sidebar">
</div>
<div class="page">
</div>
</div>
<div class="footer">
Footer
</div>
body {
background-color: black;
}
.header {
position: fixed;
width: 100%;
top: 0px;
left: 0px;
min-height: 80px;
background-color: #FFF;
z-index: 500;
}
.footer {
min-height: 50px;
bottom: 0px;
background-color: #FFF;
left: 0px;
}
.page {
min-height: 1000px;
width: 50%;
margin: 15px;
margin-left: auto;
margin-right: auto;
background-color: #AAA;
}
.sidebar {
position: fixed;
background-color: #F66;
left: 10px;
top: 105px;
width: 100px;
height: 200px;
}
let header = document.querySelector('.header');
let footer = document.querySelector('.footer');
let sidebar = document.querySelector('.sidebar');
let header_spacer = document.querySelector('.header-spacer');
header_spacer.style.height = header.clientHeight+'px';
document.addEventListener('scroll',(event)=>{
let height = window.innerHeight - header.clientHeight - 35;
let scroll = this.scrollY;
if(scroll+window.innerHeight+footer.clientHeight>=document.body.clientHeight)
{
height = (height - (
(scroll+window.innerHeight+footer.clientHeight)
- document.body.clientHeight));
}
sidebar.style.height = height+'px';
});
This my new layout:
https://codepen.io/bswan-the-decoder/pen/wvBapvV
<body>
<div class="header">
</div>
<div class="content">
<div class="sidebar_holder">
<div class="sidebar"></div>
</div>
<div class="page_holder">
<div class="page"></div>
</div>
</div>
<div class="footer">
</div>
</body>
body{
display: grid;
grid-template-rows: min-content auto min-content;
grid-template-columns: auto;
grid-template-areas: "header" "content" "footer";
background-color: #000;
min-height: 100vh;
}
.header{
grid-area: header;
background-color: #FFF;
min-height: 80px;
position: sticky;
top: 0px;
z-index: 5;
}
.footer{
grid-area: footer;
background-color: #FFF;
min-height: 40px;
}
.content{
grid-area: content;
display: grid;
grid-template-rows: auto;
grid-template-columns: min-content auto;
grid-template-areas: "sidebar page";
}
.sidebar_holder {
grid-area: sidebar;
}
.page_holder {
grid-area: page;
}
.sidebar {
width: 100px;
position: sticky;
top: 105px; /*how to make it (header height+20px) ?*/
height: 100px; /*How to make it (100vh - header height - visible height of footer)?*/
background-color: #F66;
}
.page {
height: 1000px;
width: 50%;
background-color: #AAA;
margin: 25px;
margin-left:auto;
margin-right:auto;
}
Update:
#Roddy - I tried but my knowledge is lacking so there was nothing of use pruduced by me.

To fill out the whole space with a div, you can use ViewHeight and ViewWidth units like:
vw and vh takes the percentage of the screensize and applies it,
100vw means 100% of the width
.whole-area {
height: 100vh;
width: 100vw;
background: orange;
}
<div class="whole-area">
<span>Text</span>
</div>

Related

Two scrolling blocks in flex container

I need to build a card with two scrolling areas. Initial idea was to use flexbox so I came up with this:
.card {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 50%;
min-width: 100px;
min-height: 0;
max-width: 80%;
max-height: 80%;
border: solid 1px black;
padding: 10px;
}
.photo {
background-color: silver;
margin-bottom: 10px;
aspect-ratio: 3;
}
.body {
display: flex;
}
.content {
flex: 1;
display: flex;
flex-direction: column;
margin-right: 10px;
}
.title {
background-color: silver;
margin-bottom: 10px;
}
.text {
flex: 1;
background-color: cyan;
min-height: 0;
overflow: auto;
}
.photos {
width: 100px;
min-height: 0;
overflow: auto;
}
.photos * ~ * {
margin-top: 10px;
}
.thumbnail {
background-color: lightgreen;
aspect-ratio: 3;
}
<div class="card">
<div class="photo">Photo</div>
<div class="body">
<div class="content">
<div class="title">Title</div>
<div class="text" contenteditable>
Full text<br>
Can be multiline and with vertical scroll
</div>
</div>
<div class="photos">
<div class="thumbnail">Thumbnail</div>
<div class="thumbnail">Thumbnail</div>
<div class="thumbnail">Thumbnail</div>
<div class="thumbnail">Thumbnail</div>
</div>
</div>
</div>
link to fiddle: https://jsfiddle.net/SkyLight/jxobz8qn/
The card has maximum width and height and Full text section (cyan one) can have pretty long content so that it should have scroll when needed. Thumbnails section can also have big amount of items so will also need to have scroll.
I know that overflow needs block to have height set in order to work but I can't figure out how to set it properly because the content should be limited mainly by Card's max size.
So can it be achieved with flexbox only or I'll need some other stuff? Would like to achieve the result with pure css.
Make the card element a flexbox container then use flex:1 on the body:
.card {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 50%;
min-width: 100px;
min-height: 0;
max-width: 80%;
max-height: 80%;
border: solid 1px black;
padding: 10px;
/* added */
display: flex;
flex-direction: column;
/**/
}
.photo {
background-color: silver;
margin-bottom: 10px;
aspect-ratio: 3;
}
.body {
display: flex;
flex:1; /* added */
min-height:0; /* added to make sure the content will shrink */
}
.content {
flex: 1;
display: flex;
flex-direction: column;
margin-right: 10px;
}
.title {
background-color: silver;
margin-bottom: 10px;
}
.text {
flex: 1;
background-color: cyan;
min-height: 0;
overflow: auto;
}
.photos {
width: 100px;
min-height: 0;
overflow: auto;
}
.photos * ~ * {
margin-top: 10px;
}
.thumbnail {
background-color: lightgreen;
aspect-ratio: 3;
}
<div class="card">
<div class="photo">Photo</div>
<div class="body">
<div class="content">
<div class="title">Title</div>
<div class="text" contenteditable>
Full text<br>
Can be multiline and with vertical scroll
</div>
</div>
<div class="photos">
<div class="thumbnail">Thumbnail</div>
<div class="thumbnail">Thumbnail</div>
<div class="thumbnail">Thumbnail</div>
<div class="thumbnail">Thumbnail</div>
</div>
</div>
</div>

Sizing a grid column based on a child's aspect ratio

My question is similar to this one: I'm trying to contain an aspect-ratio element within its parent element. One difference though, this aspect-ratio element has siblings—a header and a footer—and all this nice family should be center-aligned and share a common width.
Images are worth a thousand words:
GIFs are worth a thousand images:
I'm close to that result, but I'm not quite there yet:
body {
height: 100%;
margin: 0;
}
html {
background-color: lightgrey;
height: 100%;
}
#footer,
#header {
background-color: blue;
height: 50px;
}
#paper {
aspect-ratio: 1;
background-color: red;
margin: auto;
max-height: 100%;
overflow: hidden;
width: 100%;
}
#wrapper {
align-content: center;
display: grid;
height: 100%;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>
Any CSS wizard to help me out?
Not sure if you can get all the requirements but here is the best I could do (seems to work on chrome only)
body {
background-color: lightgrey;
margin: 0;
}
#footer,
#header {
background-color: blue;
height: 50px;
}
#paper {
aspect-ratio: 1;
background-color: red;
max-height: 100%;
max-width: 100vw;
}
#wrapper {
place-content: center;
display: grid;
height: 100vmin;
margin-block: max(0px,50vh - 50vmin);
grid-template-rows: auto minmax(0, 1fr) auto;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>
If the 50px is known you can do like below:
body {
background-color: lightgrey;
margin: 0;
--h: 50px; /* the fixed height */
}
#footer,
#header {
background-color: blue;
height: var(--h);
}
#paper {
aspect-ratio: 1;
background-color: red;
width: min(100vw,100vh - 2*var(--h));
}
#wrapper {
place-content: center;
display: grid;
height: min(100vh, 100vw + 2*var(--h));
margin-block: max(0px, (100vh - 100vw - 2*var(--h))/2);
grid-template-rows: auto minmax(0, 1fr) auto;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>
Try this solution, all the magic happens in grid-template-columns and grid-template-rows.
html {
background-color: lightgrey;
height: 100%;
}
body {
height: 100%;
margin: 0;
}
#wrapper {
--footer-header-height: 50px;
align-content: center;
display: grid;
height: 100vh;
grid-template-columns: 1fr minmax(auto, calc(100vh - var(--footer-header-height) * 2)) 1fr;
grid-template-rows: auto minmax(auto, 100vw) auto;
}
#footer,
#header {
grid-column: 2;
background-color: blue;
height: var(--footer-header-height);
}
#paper {
grid-column: 2;
aspect-ratio: 1 / 1;
background-color: red;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>
Actually Container Queries enable us to solve this kind of problems elegantly.
Support for this feature is currently very bad (see here), but it's part of Interop 2022 so I guess it'll look different by the end of the year.
I post this as an answer as it might help someone in the future 👽👋
Note that you currently need to turn on a flag on Chrome to be able to test it.
body {
container-type: size;
height: 100%;
margin: 0;
}
html {
background-color: lightgrey;
height: 100%;
}
#footer,
#header {
background-color: blue;
}
#paper {
background-color: red;
}
#wrapper {
align-content: center;
display: grid;
grid-template-columns: min(100cqi, (100cqb - 100px));
grid-template-rows: 50px min(100cqb - 100px, 100cqi) 50px;
justify-content: center;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>
Here's the same code but relying on viewport units (works in all browsers):
body {
height: 100%;
margin: 0;
}
html {
background-color: lightgrey;
height: 100%;
}
#footer,
#header {
background-color: blue;
}
#paper {
background-color: red;
}
#wrapper {
align-content: center;
display: grid;
grid-template-columns: min(100vw, (100vh - 100px));
grid-template-rows: 50px min(100vh - 100px, 100vw) 50px;
height: 100%;
justify-content: center;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>

Positioning of a Absolute Element in relation to a responsive image

I’m trying to create a responsive design where a sections section is always flush to the bottom edge of an image.
The section is a grid divided into three grid-areas:
“section1 section3”
“section2 section3"
I want the bottom of section1 to always align with the bottom of a responsive 3/2 image.
The closest I’ve been able to get to achieve what I’m looking for is by applying padding-bottom: min(50px, 3.5vw);
img{
object-fit: contain;
/* aspect-ratio: 3/2;*/
width: 100%;
padding-bottom: min(50px, 3.5vw);
}
div section{
position: absolute;
bottom: 0;
}
What I’m looking for is something like calc(heightOfTheContainer + XXpx)
I know I can do this in JavaScript document.getElementById("myImg").offsetHeight + XXpx;
Can I achieve what I want with just CSS using calc() minmax() or ??.
main{
height:100%;
width: 100%;
padding: 3.5vh;
}
div{
position: relative;
max-width: 800px;
margin: 0 auto;
}
img{
object-fit: contain;
/* aspect-ratio: 3/2;*/
width: 100%;
padding-bottom: min(50px, 3.5vw);
}
div section{
position: absolute;
display: grid;
grid-template-columns: 30% 70%;
grid-template-rows: 50% 50%;
grid-template-areas:
"section1 section3"
"section2 section3";
bottom: 0;
height:8vw;
width:100%;
}
.section1, .section2, .section3{
width:100%;
height:100%;
font-size: 20px;
font-weight:bold;
color:black;
}
.section1{
grid-area: section1;
background-color: red;
}
.section2{
grid-area: section2;
background-color: blue;
}
.section3{
grid-area: section3;
background-color: green;
opacity: .5;
}
<main>
<div>
<img src="../../images/portfolio/mainbnw.jpg" />
<section>
<div class="section1">
section 1
</div>
<div class="section2">
section 2
</div>
<div class="section3">
section 3
</div>
</section>
</div>
</main>
If you remove any bottom padding from the main and position the grid element relative rather than absolute it will end up immediately below the image.
So then translate it upwards by 50% of its height and the bottom of the first cell will always be aligned with the bottom of the image (whatever height you give the cell).
main {
height: 100%;
width: 100%;
padding: 3.5vh;
}
div {
position: relative;
max-width: 800px;
margin: 0 auto;
}
img {
object-fit: contain;
/* aspect-ratio: 3/2;*/
width: 100%;
}
div section {
position: relative;
transform: translateY(-50%);
display: grid;
grid-template-columns: 30% 70%;
grid-template-rows: 50% 50%;
grid-template-areas: "section1 section3" "section2 section3";
bottom: 0;
height: 8vw;
width: 100%;
}
.section1,
.section2,
.section3 {
width: 100%;
height: 100%;
font-size: 20px;
font-weight: bold;
color: black;
}
.section1 {
grid-area: section1;
background-color: red;
}
.section2 {
grid-area: section2;
background-color: blue;
}
.section3 {
grid-area: section3;
background-color: green;
opacity: .5;
}
<main>
<div>
<img src="https://picsum.photos/id/1015/600/400" />
<section>
<div class="section1">
section 1
</div>
<div class="section2">
section 2
</div>
<div class="section3">
section 3
</div>
</section>
</div>
</main>
If you use grid, you can a set few elements inside the same cell without the need of absolute.
here is an example made of 3 rows to show the idea:
img {
object-fit: contain;
aspect-ratio: 3/2;
}
.section1,
.section2,
.section3 {
font-size: 20px;
font-weight: bold;
color: black;
}
section {
display: grid;
grid-template-columns: 30% 70%;
grid-template-rows: 8fr auto auto;
grid-template-areas: "img img" "sec1 sec3" " sec2 sec3"
}
img {
grid-column: 1/3;
grid-row: 1/3;
width: 100%;
}
.section1 {
grid-area: sec1;
background-color: red;
}
.section2 {
grid-area: sec2;
background-color: blue;
}
.section3 {
grid-area: sec3;
background-color: rgba(0,128,0,0.5);
}
<section>
<img src="https://picsum.photos/id/1015/300/200" />
<div class="section1">
section 1
</div>
<div class="section2">
section 2
</div>
<div class="section3">
section 3
</div>
</section>

Horizontally-scrolling div inside a flex column

I can't seem to have a horizontally-scrolling div inside a flex column.
Codepen
.container { display: flex; margin: 20px; height: 300px; border: 1px solid blue; }
.side-nav { flex-shrink: 0; width: 100px; min-height: 100%; background: grey; }
.main { padding: 20px; }
.scrollable { overflow-x: auto; max-width: 100%; }
.long-content { width: 2000px; height: 50px; background: red; }
<div class='container'>
<div class='side-nav'>
</div>
<div class='main'>
<h1>Test</h1>
<div class='scrollable'>
<div class='long-content'>
This is supposed to scroll horizontally unless your window is super wide
</div>
</div>
<p>Some paragraph below the scrollable box</p>
</div>
</div>
If I change the max-width of .scrollable to px it would work, but I need it to fill the column.
What am I missing?
In my opinion, this is a Module"bug" (Wierd).
Anyway, one very simple solution is to use flex-basis (Instead of width).
Step 1 for main add width: 0;
.container {
display: flex;
margin: 20px;
height: 300px;
border: 1px solid blue;
}
.side-nav { flex-shrink: 0; width: 100px; min-height: 100%; background: grey; }
.main {
padding: 20px;
border: 5px dashed orange;
/* "new code" */
width: 0px;
}
.scrollable { overflow-x: auto; max-width: 100%; }
.long-content { width: 2000px; height: 50px; background: red; }
<div class='container'>
<aside class='side-nav'>
Aside
</aside>
<main class='main'>
<h1>Test</h1>
<div class='scrollable'>
<div class='long-content'>
This is supposed to scroll horizontally unless your window is super wide
</div>
</div>
<p>Some paragraph below the scrollable box</p>
</main>
</div>
Step 2 - main add flex-basis: 100%;
.container {
display: flex;
margin: 20px;
height: 300px;
border: 1px solid blue;
}
.side-nav { flex-shrink: 0; width: 100px; min-height: 100%; background: grey; }
.main {
padding: 20px;
border: 5px dashed orange;
/* "new code" */
width: 0px;
flex-basis: 100%;
}
.scrollable { overflow-x: auto; max-width: 100%; }
.long-content { width: 2000px; height: 50px; background: red; }
<div class='container'>
<aside class='side-nav'>
Aside
</aside>
<main class='main'>
<h1>Test</h1>
<div class='scrollable'>
<div class='long-content'>
This is supposed to scroll horizontally unless your window is super wide
</div>
</div>
<p>Some paragraph below the scrollable box</p>
</main>
</div>
One more option is to use width: 0; & flex-grow:1
.container {
display: flex;
margin: 20px;
height: 300px;
border: 1px solid blue;
}
.side-nav { flex-shrink: 0; width: 100px; min-height: 100%; background: grey; }
.main {
padding: 20px;
border: 5px dashed orange;
/* "new code" */
width: 0px;
flex-grow: 1;
}
.scrollable { overflow-x: auto; max-width: 100%; }
.long-content { width: 2000px; height: 50px; background: red; }
<div class='container'>
<aside class='side-nav'>
Aside
</aside>
<main class='main'>
<h1>Test</h1>
<div class='scrollable'>
<div class='long-content'>
This is supposed to scroll horizontally unless your window is super wide
</div>
</div>
<p>Some paragraph below the scrollable box</p>
</main>
</div>
I don't like any of those ideas - but this is life hh.

How to set grid-template

My css is below. What I want is the header shall be fixed at top of the page and having width equal to the width of container irrespective to the changing width of browser. However, I always get the width of header expands over the width of Container. Please instruct me how to fix it?
html {
font-size: 16px;
}
.container {
width: 80%;
margin: 0 auto;
}
header {
width: 100%;
display: grid;
grid-template-columms: auto auto;
grid-template-rows: 170px 60px;
position: fixed;
top: 0px;
margin: 0 auto;
}
<div class="container">
<header>
<div class="cart">
</div>
</header>
Set the width of the header to 80%;
Works automatically..
* {
margin:0;
padding:0;
}
.container {
width: 80%;
margin: auto;
border: 1px solid red;
height: 150vh;
}
header {
background: green;
position: fixed;
width: 80%;
top: 0;
height: 50px;
}
<div class="container">
<header>
<div class="cart">
</div>
</header>
Codepen Demo

Resources