Flexible image sizing inside flexbox container - css

I have a flexbox layout with an image and a caption (stacked). I'd like the image to take up all the available screen space, minus whatever space the caption needs below. Each image + caption combo should never exceed 100% of the viewport height.
The problem: I'm struggling finding a way to make the image not exceed the height of the available space. (In other words, the image will be larger than the available area, and it needs to scale down to respect the 100vh height set on <section> in my code example below.
Example of desired look
Image space outlined in red, caption space outlined in blue
Demo of problem
I added a max-height to the <img> so you can see the desired effect. (Remove max-height on the <img> to see the problem.)
Ultimately I need it to work like this without setting a literal max-height. Effectively it should be max-height: calc(100vh - (height of caption)), but I'm trying to do this without javascript.

From earlier comment
to keep flex children inside boundaries and use a max-size in %, you need to use overflow:hidden;, so the browser can calculate those size, else content boxes can overflow.
example:
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
display: flex;
flex-direction: column;
height: 100vh;
margin: 0;
}
header {
display: flex;
justify-content: space-between;
background: lightblue;
padding: 1vmax;
}
section {
flex: 1;
overflow: hidden;
/* the browser will calculate size */
display: flex;
flex-direction: column;
align-items: center;
}
figure {
padding: 1vmin;
display: flex;
flex: 1 1 auto;
overflow: hidden;
/* the browser will calculate size */
}
img {
margin: auto;
max-height: 100%;
max-width: 100%;
flex-shrink: 1;
/* IE */
}
<header>
<h1>Logo</h1>
<nav>
Link Link Link
</nav>
</header>
<section>
<figure>
<img src="https://placekitten.com/1200/1200" />
</figure>
<aside>
<strong>Lorem Ipsum #1</strong>
<p>Vivamus a neque dui. Nunc tortor risus, mattis quis vulputate at, lacinia vel enim. Nam aliquet ipsum a mi malesuada, nec accumsan felis accumsan.</p>
</aside>
</section>

Related

Problems with Div and CSS

So, trying to figure out how to structure webpages, was trying to make a bodyWrapper to cover entire page using max-height: 100%, max-width: 100%. Then I was going to attempt to put all my other elements using 20% etc... I was hoping it will help when resizing, is that the proper way? Anyways, my CODING problem is my div is only covering one line of the entire page.
My code;
html {
max-height: 100%;
max-width: 100%
}
body {
max-height: 100%;
max-width: 100%;
}
#bodyWrapper {
max-height: 100%;
max-width: 100%;
background-color: black;
}
<meta name="viewpoint" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="myCssTemplate.css">
</head>
<body>
<div id="bodyWrapper">
<p> This is a test.</p>
</div>
</body>
You could set the bodyWrapper min-height to 100vh. That mean it will take 100% viewport height.
#bodyWrapper {
min-height: 100vh;
width: 100%;
background-color: black;
}
What you are trying to achieve is often referred to as "Grid System". The most popular framework for grids is Bootstrap. What Bootstrap does is changing the max-width property on so called media queries (e.g. 100%). Based on that you can create rows and columns. Columns have a relative width to their parent (row and containers), e.g. (like in your example) 20%.
Since this task has been done by multiple frameworks its quite important to understand how it works, but its not important to implement it by yourself. Just use a framework for this task.
When using max-height or max-width you are asking the maximum height of the element to not be over the amount you wrote for (in your case 100%) so technically, impossible to cover all the page. So in your case you'll have to ask for a minimum dimension.
#bodyWrapper
{
height: 100%; /* Can also use min-height: 100%; */
width: 100%; /* Can also use min-width: 100%; */
background-color: black;
}
After that you'll be free to put other element inside your wrapper the width you want.
First you will need to set the height:100% to the root element of the DOM html....
max-height:100% will not work because max-height defines what can be the maximum height of the element if the inner content will be added like below snippet...it will help you to understand max-height concept.
div {
width: 100px;
max-height: 100px;
background: red;
margin-bottom: 20px;
}
<div>Lorem ipsum dolor sit amet</div>
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed lacinia, ipsum a posuere consequat, purus erat faucibus orci, vel porta leo odio vitae ligula. Maecenas interdum quis dui eu faucibus.</div>
back to the question you will need to set min-height:100% to the #bodyWrapper instead of max-height and also you don't have to set max-width:100% or width:100% to the html, body, div, because there are block element by default and block element always take width:100% if not specified and width value
html,
body {
height: 100%;
margin: 0;
}
#bodyWrapper {
min-height: 100%;
background-color: black;
}
p {
margin: 0;
color: #ffffff;
}
<html>
<head>
<meta name="viewpoint" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="myCssTemplate.css">
</head>
<body>
<div id="bodyWrapper">
<p> This is a test.</p>
</div>
</body>
</html>

My position: sticky element isn't sticky when using flexbox

I was stuck on this for a little bit and thought I'd share this position: sticky + flexbox gotcha:
My sticky div was working fine until I switched my view to a flex box container, and suddenly the div wasn't sticky when it was wrapped in a flexbox parent.
.flexbox-wrapper {
display: flex;
height: 600px;
}
.regular {
background-color: blue;
}
.sticky {
position: -webkit-sticky;
position: sticky;
top: 0;
background-color: red;
}
<div class="flexbox-wrapper">
<div class="regular">
This is the regular box
</div>
<div class="sticky">
This is the sticky box
</div>
</div>
JSFiddle showing the problem
Since flex box elements default to stretch, all the elements are the same height, which can't be scrolled against.
Adding align-self: flex-start to the sticky element set the height to auto, which allowed scrolling, and fixed it.
Currently this is supported in all major browsers, but Safari is still behind a -webkit- prefix, and other browsers except for Firefox have some issues with position: sticky tables.
.flexbox-wrapper {
display: flex;
overflow: auto;
height: 200px; /* Not necessary -- for example only */
}
.regular {
background-color: blue; /* Not necessary -- for example only */
height: 600px; /* Not necessary -- for example only */
}
.sticky {
position: -webkit-sticky; /* for Safari */
position: sticky;
top: 0;
align-self: flex-start; /* <-- this is the fix */
background-color: red; /* Not necessary -- for example only */
}
<div class="flexbox-wrapper">
<div class="regular">
This is the regular box
</div>
<div class="sticky">
This is the sticky box
</div>
</div>
JSFiddle showing the solution
In my case, one of the parent containers had overflow-x: hidden; applied to it, which will break position: sticky functionality. You'll need to remove that rule.
No parent element should have the above CSS rule applied to it. This condition applies to all parents up to (but not including) the 'body' element.
If you are using flex in the parent element use align-self: flex-start for the element which you want to make sticky.
position: sticky;
align-self: flex-start;
top: 0;
overflow-y: auto;
You can also try adding a child div to the flex item with the contents inside and assign position: sticky; top: 0; to that.
That worked for me for a two column layout where the contents of the first column needed to be sticky and the second column appear scrollable.
For my situation, the align-self: flex-start (or justify-self: flex-start) solution does not work. I need to keep overflow-x: hidden as well since some containers swipe horizontally.
My solution required nested display: flex with overflow-y: auto to get the desired behaviors:
header can adjust height dynamically, which prevents playing with position: absolute or position: fixed
content scrolls vertically, constrained horizontally to the view width
sticky element can be anywhere vertically, sticking to the bottom of the header
other elements can slide horizontally
looks like the SO snippet tool can't render width on child elements to properly to demonstrate the horizontal slide, or maybe there's some other setting on my actual layout that makes it work...
note that a wrapper element that does nothing else is required to allow overflow-x: auto to work correctly in elements under a parent with overflow-x: hidden
body {
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
}
body>header {
background-color: red;
color: white;
padding: 1em;
}
.content {
overflow-x: hidden;
overflow-y: auto;
display: flex;
flex-direction: column;
}
article {
position: relative;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.horizontal_slide {
display: flex;
overflow-x: auto;
background-color: lightblue;
padding: .5em;
}
.horizontal_slide>* {
width: 1000px;
}
.toolbar {
position: sticky;
top: 0;
z-index: 10;
background-color: lightgray;
padding: .5em;
display: flex;
}
<header>Fancy header with height adjusting to variable content</header>
<div class="content">
<article class="card">
<h1>One of several cards that flips visibility</h1>
<div class="overflow_x_wrapper">
<div class="horizontal_slide">
<div>Reason why `overflow-x: hidden` on the parent is required
</div>
<div>Reason why `overflow-x: hidden` on the parent is required
</div>
<div>Reason why `overflow-x: hidden` on the parent is required
</div>
</div>
<div class="toolbar">Sticky toolbar part-way down the content</div>
<p>Rest of vertically scrollable container with variable number of child containers and other elements</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</article>
</div>
Make sure flex-wrapper has assigned a height, and set the overflow value to auto.
Then add "align-self: flex-start;" to the sticky element.
.flexbox-wrapper {
display: flex;
height: 600px; //<- nessary,and don't assign with %
overflow:auto; //<-fix
}
.regular {
background-color: blue;
}
.sticky {
position: -webkit-sticky;
position: sticky;
top: 0;
background-color: red;
align-self: flex-start; // <-fix
}
<div class="flexbox-wrapper">
<div class="regular">
This is the regular box
</div>
<div class="sticky">
This is the sticky box
</div>
</div>
I made a makeshift flexbox table and had this problem. To solve it, I simply put the sticky header row outside of the flexbox, just before it.
Global solution/rule from my experience:
Do not put structures with sticky elements directly inside { display : flex } containers.
Instead (for flex layouts) put your tables/divs with sticky elements inside a flex sub-container (e.g. with { flex: 1 1 auto }, but without { display : flex } ), then everything should work as intended.

Anchor to cover entire DIV - but with challenges and no javascript

I'm working on a little design challenge, and it's getting the better of me right now.
Essentially, it's a material design card, which means when I click it it takes me somewhere else.
The easy route would be (and as it is now) is to surround the content with an anchor. However, in this case I ONLY want the anchor text to be "My keyword".
Here's the simple html output:
<a class="post-card md-card">
<div class="md-card-title aspect-16x9">
<div class="title-large"></div>
</div>
<div class="md-card-content">
<div class="sup-text"></div>
</div>
</a>
So, the 2 things I want to do are:
Only have the keyword inside the anchor
Be able click the whole thing (the link covers the entire outer div)
Here's the stuff that make it more difficult:
The blue box on top has an aspect ratio set, which means its not a
constant height
The text inside the blue box is centered using Flexbox
The white box isn't a fixed height either
Here's how the aspect ratio is calculated:
.AspectRatio(#widthRatio:16; #heightRatio:9; #useableWidth:100%) {
&:extend(.clearfix all);
overflow:hidden;
max-width:#useableWidth;
&::before {
content:"";
float:left;
padding-top:percentage(#heightRatio / #widthRatio);
}
}
So I need to keep the keyword text where it is but make the whole thing clickable.
I've been playing around with the idea an absolutely positioned anchor on top, which I can do but I can't get it to stretch to the bottom without moving the text.
Any CSS gurus got some ideas?
This should give you a good starting place...
.post-card {
background-color: #63d9ff;
box-sizing: border-box;
position: relative;
width: 200px;
}
.post-card * {
box-sizing: inherit;
}
.post-card .md-card-title {
bottom: 0;
left: 0;
position: absolute;
right: 0;
text-align: center;
top: 0;
}
.post-card .md-card-title .content-prop,
.post-card .md-card-title .content {
display: inline-block;
vertical-align: middle;
}
.post-card .md-card-title .content-prop {
padding-top: 56%;
width: 0;
}
.post-card .md-card-title .content {
padding: 1rem;
width: 100%;
}
.post-card .space-prop {
display: block;
padding-top: 56%;
}
.md-card-content {
background-color: grey;
padding: 1rem;
}
<div class="post-card md-card">
<a class="md-card-title aspect-16x9" href="#">
<span class="content-prop"></span><!--
--><span class="content">My Keyword</span>
</a>
<span class="space-prop"></span>
<div class="md-card-content">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis mauris ut eros consectetur efficitur vitae at leo.
</div>
</div>
JSFiddle
So the explaination...
The whole post card is positioned relative, the anchor is then positioned absolute within it. The anchor is given a top, left, bottom, and right value of 0 which makes it cover it's parent container.
The content-prop and space-prop are given no height but have a top padding of 56%. This means that their top-padding value is 56% of their width which works out at a 16:9 ratio. The space banner is used here to add a empty gap at the top of the post card to make room for the anchor.
Both the content-prop and the content elements are set to display inline-block and vertical aligned to middle. Because the prop is taller than the content, the content floats in the centre. The HTML comment between these two elements eliminates white space so that the content div can be set to 100% width even when the prop is on the same horizontal row.

Put space between large items of CSS Flexbox row-wrap [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I want to put space between large items of a Flexbox row-wrap container.
Before I ask my question, please take a look at the last Codepen example at the end of the article in the link below ("Let's try something even better by playing with flex items flexibility!"):
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
My question;
When the window is the widest (> 800px), how can I add space between Yellow, Blue and Red (the two asides and the main)? At the moment they are flexed on a row but staked against each other.
My understanding:
There is no space between items because Blue (the main) would take 100% of the width of it were outside the flex, because of all the text inside it.
But if Yellow, Blue and Red were no more than, let's say, 20% wide each, then we would have space between them (with total space being 100% - 3x20% = 40%).
My question again:
Notwithstanding the fact that I have one or several items whose individual width outside of a Flexbox would be 100% because of a lot of text inside them, I would like to flex them on a row with space between them.
Is that easily possible?
Thank you
Fairly simply, just use margins. You can either put margin: 0 100px on main (my prefered method), or add a right margin to the first aside and a left margin to the second. Stealing Chris's (compiled) code, it would look like this (you may have to open the snippet in full screen):
.wrapper {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-flow: row wrap;
flex-flow: row wrap;
font-weight: bold;
text-align: center;
}
.wrapper > * {
padding: 10px;
flex: 1 100%;
}
.header {
background: tomato;
}
.footer {
background: lightgreen;
}
.main {
text-align: left;
background: deepskyblue;
}
.aside-1 {
background: gold;
}
.aside-2 {
background: hotpink;
}
#media all and (min-width: 600px) {
.aside {
flex: 1 auto;
}
}
#media all and (min-width: 800px) {
.main {
flex: 3 0px;
margin: 0 100px;
}
.aside-1 {
order: 1;
}
.main {
order: 2;
}
.aside-2 {
order: 3;
}
.footer {
order: 4;
}
}
body {
padding: 2em;
}
<div class="wrapper">
<header class="header">Header</header>
<article class="main">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
</article>
<aside class="aside aside-1">Aside 1</aside>
<aside class="aside aside-2">Aside 2</aside>
<footer class="footer">Footer</footer>
</div>
No messing around with width (or flex-basis) needed.

position:absolute and fill remaining space in div

Please have a look at the following configuration:
http://codepen.io/anon/pen/mJYLpe?editors=110
I would like my #right div to stay at the right of the screen with a pre-defined width and I would like my #left div to fill the remaining space. Note that the #imagetag needs to stay at the bottom right of the filler div without needing a manual positioning.
Please know that this is not a duplicate - all of the provided solutions here require the "filler" div to have no position property - however I need the position:absolute property to make overflow:hidden work properly for my case (as you can see contained image is bigger than the container - it needs to be hidden)
Open to suggestions.
Add
position: absolute;
right: 0;
on the #right and then use calc(100% - 200px) on #left
http://codepen.io/anon/pen/oXRdPv?editors=110
You need to use positioning for this case. This is a case of:
+-------+-----------+
| FIXED | FLUUUUUID |
+-------+-----------+
Or
+-------+-----------+
| FIXED | FLUUUUUID |
| | FLUUUUUID |
+-------+-----------+
Fixed-Fluid Model. In my snippet, I have demonstrated two kinds of examples. In the first case, the fluid is less in size. And the next has too long content.
Snippet
.parent {position: relative; margin: 0 0 15px; border: 1px solid #999; padding: 5px; padding-left: 100px;}
.parent .fixed {position: absolute; left: 5px; width: 90px; background-color: #99f;}
.parent .fluid {background-color: #f99;}
<div class="parent">
<div class="fixed">Fixed</div>
<div class="fluid">Fluid</div>
</div>
<div class="parent">
<div class="fixed">Fixed</div>
<div class="fluid">Fluid Lorem ipsum dolor sit amet, consectetur adipisicing elit. Itaque animi placeat, expedita tempora explicabo facilis nulla fuga recusandae officia, maiores porro eaque, dolore et modi in sapiente accusamus id aut.</div>
</div>
Edit from a previous answer, so comments might not be up to date.
Changes:
#left {
position: absolute;
height: 100%;
overflow: hidden;
width: calc(100% - 200px);
}
#right {
background-color: pink;
width: 200px;
height: 100%;
position: absolute;
right: 0;
z-index: 100;
}
And moved the image tag to be a container for the "left" and "right" boxes
See Fiddle:
http://codepen.io/anon/pen/NqVzNY?editors=110
I don't know what browsers you want to support but my go for this would be flexbox
Browser support of flexbox http://caniuse.com/#feat=flexbox
In flexbox you can set a min and max width to a div and next to it it will be responsive
http://codepen.io/anon/pen/NqVzpz?editors=110
set the carry to flex
#carry {
display: flex;
};
and the left to flex and only adding a width to the right one
#left {
flex: 1;
}
#right {
background-color: pink;
max-width: 200px;
min-width: 200px;
height: 100%;
flex: 1;
}
if you wane know what more awesome stuff flexbox is capable of check this https://css-tricks.com/snippets/css/a-guide-to-flexbox/
Add float: right on your #right element and add float: left on your #left element.
Also, the width of #left and #right elements should share a total of 100%.
Set the width and height to 100% on the #image element. Then apply the size of the image on background-size: 130%.
http://codepen.io/anon/pen/NqVMOv?editors=110

Resources