Flexbox shrink to content size [duplicate] - html

In the below example, I have a button with the following styles...
.button-flexbox-approach {
/* other button styles */
display: flex;
justify-content: center;
align-items: center;
padding: 1.5rem 2rem;
}
http://codepen.io/3stacks/pen/JWErZJ
But it automatically scales to 100% instead of the width of the content. You can set an explicit width, but then that doesn't allow your text to wrap naturally.
How can I make a button that has the inner text centered with flexbox, but doesn't grow to fit the size of the container?
.container {
width: 100%;
height: 100%;
}
.button {
/* Simply flexing the button makes it grow to the size of the container... How do we make it only the size of the content inside it? */
display: flex;
justify-content: center;
align-items: center;
-webkit-appearance: none;
border-radius: 0;
border-style: solid;
border-width: 0;
cursor: pointer;
font-weight: normal;
line-height: normal;
margin: 0;
position: relative;
text-align: center;
text-decoration: none;
padding: 1rem 2rem 1.0625rem 2rem;
font-size: 1rem;
background-color: #D60C8B;
border-color: #ab0a6f;
color: #fff;
}
.one {
margin-bottom: 1rem;
}
.two {
/* You can put an explicit width on that, but then you lose the flexibility with the content inside */
width: 250px;
}
<div class="container">
<p>
Button one is flexed with no width and it grows to the size of its container
</p>
<p>
<a class="button one" href="#">Button</a>
</p>
<p>
Button two is flexed with an explicit width which makes it smaller, but we have no flexibility for changing the content
</p>
<p>
<a class="button two" href="#">Button 2</a>
</p>
</div>

Instead of display: flex on the container, use display: inline-flex.
This switches the container from block-level (which takes the full width of its parent) to inline-level (which takes the width of its content).
This sizing behavior is similar to display: block vs. display: inline-block.
For a related problem and solution see:
Make flex items take content width, not width of parent container

EDIT: Michael_B pointed out in his answer that inline-flex on the parent is the correct property for this use-case. I've updated my blog post accordingly.
It's not ideal, but if you wrap the button in a container that is inline-block, the button will scale naturally with the content, like so:
.button {
/* other button styles */
display: flex;
justify-content: center;
align-items: center;
padding: 1.5rem 2rem;
}
<div style="display: inline-block;">
<a class="button">Button</a>
</div>
You can then set the max-width on the container and the text will wrap to multiple lines, but retain the correct vertical centering.

Related

How can I make my image and a div with text to always be responsive and the same size?

I have an image and a div that go next to each other like this:
Instead it looks like this:
I tried to use flex-shrink and grow and that didn't help. Maybe I should take another approach? Or I am just using flexbox wrong here?
.explore-adoption {
display: flex;
flex-direction: row;
max-height: 487px;
height: 100%;
align-items: center;
margin-top: 100px;
width: 100%;
text-align: center;
}
.explore-info {
max-width: 600px;
width: 100%;
max-height: 487px;
height: 100%;
padding: 4em;
background-color: #363635;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
text-align: left;
flex-grow: 0;
gap: 1em;
}
.explore-image img {
width: 100%;
align-self: center;
}
<section class="explore-adoption">
<div class="explore-info">
<h2 class="section-title light">Explore adoption safely and securely</h2>
<p class="text-content light">Our platforms empower women considering adoprion by providing choice and privacy.</p>
<!-- ??? -->
<a href="#" class="section-link light">
<span>Learn more</span>
<i class="fa-solid fa-circle-arrow-right"></i>
</a>
</div>
<div class="explore-image">
<img src="https://via.placeholder.com/200" alt="Birthmother at computer">
</div>
</section>
Responsive Font-size
Set the default font-size for the page by setting it on the root element (html or :root). Any lengths set in rems will be relative to the font-size (from now on it will be referred to as fs) of root. If the fs of root is 16px (browser default) then a length of 2rem is 32px.
In the example, the fs of root is a relative length: 5vmin. vmin/vmax is relative to either the viewport's width or height. If it is vmin, then lengths are relative to the smaller length of the current viewport dimensions -- if it is in vmax, then lengths are relative to the larger length of the current viewport.
Ex. a viewport width of 800px and height of 450px -- 5vmin means 1rem is 5% of 450px and 5vmax means 1rem is 5% of 800px. Since lengths are relative to viewport, if the browser window changes at anytime, all lengths set in rem recalculates and adjusts accordingly. So if all of the lengths are in rem, you'd have a fully responsive page, but it's not perfect. There will be a point when the ratio of lengths will change when the viewport ratio is flipped from landscape to portrait or if one of the viewport dimensions becomes disappropriately smaller or bigger. That situation is beyond the scope of the question so I'll give you a hint: one solution is media queries.
Responsive Elements
Set width and height in percentages.
The parent element that contains both text and image elements should be a flex container and have no padding and margins and hide any vertical overflow (see Figure I).
Figure I - Parent Element (aka article.adoption)
.adoption {
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
margin: 0;
padding: 0;
overflow-y: hidden;
}
Next, the right-side element that has the text should be an flex container with a vertical stacking flow. Set it's height to 100% and it's width to 50%. (see Figure II)
Figure II - Text Element (aka section.info)
.info {
display: flex;
flex-flow: column nowrap;
justify-content: center;
width: 50%;
height: 100%;
}
Then on the left-side element that has the <img>, set it's height to 100%, width to 50%, remove padding, and hide any vertical overflow. (see Figure III)
Figure III - Image Element (aka figure.image)
.image {
width: 50%;
height: 100%;
padding: 0;
overflow-y: hidden;
}
For the <img>, I cropped it so that it's a square using a program: paint.NET. Then set <img> width to 100%. If there's a small gap at the bottom, add the following ruleset to figure.image: margin: 0 0 -0.5rem 0. That'll drag the image down and past the bottom border where it'll be hidden by overflow-y: hidden.
View this example in full page mode, dimensions are distorted if viewed in an <iframe>
Note there's an red dashed outline that shows that the two elements are flush -- this is for demonstration purposes and recommended that you remove the ruleset: outline: 1px dashed red from .adoption
html {
font: 300 5vmin/1.2 'Segoe UI'
}
.adoption {
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
margin: 0;
padding: 0;
color: whitesmoke;
background: #363635;
overflow-y: hidden;
outline: 1px dashed red;
}
.info {
background-color: #363635;
display: inline-flex;
flex-flow: column nowrap;
justify-content: center;
gap: 0.5rem;
width: 50%;
height: 100%;
padding: 1.75rem;
}
.title {
margin: 0;
font-size: 1.25rem;
}
.link {
color: whitesmoke;
text-decoration: none;
}
.image {
width: 50%;
height: 100%;
margin: 0 0 -0.5rem 0;
padding: 0;
overflow-y: hidden;
}
.image img {
width: 100%;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" rel="stylesheet">
<article class="adoption">
<section class="info">
<h2 class="title">Explore Adoption Safely & Securely</h2>
<p>Our platforms empower women considering adoption by providing choice and privacy.</p>
<a href="#" class="link">
<span>Learn more </span>
<i class="fa-solid fa-circle-arrow-right"></i>
</a>
</section>
<figure class="image">
<img src="https://i.ibb.co/zNPS1z7/mother.jpg" alt="Birthmother at computer">
</figure>
</article>
I made each "side" to be 50% width. Also added align-items: stretch; to the flex to stretch items vertically. Notice: to make it responsive I removed the max-height. Also added height:100% for the img in case the text makes the left side too high.
* {
box-sizing: border-box;
}
.explore-adoption {
align-items: center;
margin-top: 100px;
text-align: center;
display:flex;
align-items: stretch;
}
.explore-info {
width: 50%;
padding: 4em;
background-color: #363635;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
text-align: left;
flex-grow: 0;
gap: 1em;
}
.explore-image {
width: 50%;
}
.explore-image img {
display:block;
width: 100%;
height: 100%;
}
<section class="explore-adoption">
<div class="explore-info">
<h2 class="section-title light">Explore adoption safely and securely</h2>
<p class="text-content light">Our platforms empower women considering adoprion by providing choice and privacy.</p>
<!-- ??? -->
<a href="#" class="section-link light">
<span>Learn more</span>
<i class="fa-solid fa-circle-arrow-right"></i>
</a>
</div>
<div class="explore-image">
<img src="https://via.placeholder.com/200" alt="Birthmother at computer">
</div>
</section>
<br>

Anchor and button elements render differently with same styles

As title says-
I'm setting anchor and button to flex but their sizes are different.
Why does this happen? How can I achieve same visual on both elements with auto width?
a,
button {
display: flex;
align-items: center;
justify-content: center;
width: auto;
background: orange;
margin-bottom: 20px;
padding: 10px;
height: 50px;
margin: 0;
}
asdf
<button>asdf</button>
Here, in `width:auto`, auto tells the default value. The browser calculates the width.
Also both are different elements.
div {
display: flex;
flex-direction: row;
justify-content: space-evenly;
align-items: center;
}
a,
button {
width: auto;
background: orange;
padding: 30px;
margin: 0;
}
a {
padding-bottom:31px;
}
<div>
asdf
<button>asdf</button>
</div>
My problem consists of 2 parts:
button and a render differently
I wanted a to render like button in this example. So the component I'm designing will not make its users to wrap it and so on and so forth.
The correct answer is arround width property set to fit-content. It has pretty good browser support and I'll try to use it.
Thanks a lot for your answers!
This depends on the engine but the most probable cause of this in browsers is due to the box-sizing property of css. MDN defines box sizing as
By default in the CSS box model, the width and height you assign to an
element is applied only to the element's content box. If the element
has any border or padding, this is then added to the width and height
to arrive at the size of the box that's rendered on the screen. This
means that when you set width and height, you have to adjust the value
you give to allow for any border or padding that may be added. For
example, if you have four boxes with width: 25%;, if any has left or
right padding or a left or right border, they will not by default fit
on one line within the constraints of the parent container.
You can read more about this here: https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing how using
But the closest you can reach is by using width as inherit. This will cause the elements to inherit width from parent.
// Resetting both to have same type for similar render
a,button {
box-sizing: content-box;
border: unset;
}
a,
button {
display: flex;
align-items: center;
justify-content: center;
background: orange;
margin-bottom: 20px;
padding: 10px;
height: 50px;
margin: 0;
}
// Add width to parent element
.w-100 {
width: 100vw;
}
// This will cause the default width behaviour to inherit from parent
a,button {
width: inherit;
}
<div class="w-100">
asdf
<button>asdf</button>
</div>
This problem arises from button not being allowed to have some of the other display properties depending on the engine. Wrapping button in a span is the easiest solution.
asdf
<span><button>asdf</button></span>
a, span {
display: flex;
align-items: center;
justify-content: center;
width: auto;
background: orange;
margin-bottom: 20px;
padding: 10px;
height: 50px;
margin: 0;
}

how to handle images when flex direction is set to column

I am working with flex layouts and images. My goal is to stack the flex-items on top of each other on smaller screen widths, by setting flex-direction: column; but there is a strange behavior i can't understand. The flex item that wraps the image seems to take the full width of the image. I have created a CodePen that illustrates the problem. Thank you in advance
Let the width of the flex item be dictated by the flex container, then size the image to the size of the flex item.
When you set up a flex-direction: column flex container, the height of your flex items is controlled by the flex properties (grow, shrink, basis).
The width of a flex item in a flex-direction: column flex container can either be dictated by the flex container, or it can be left to the flex item to “decide” how big it wants to be.
In this case align-items: start controls the width of your flex items. start means that the items can size themselves however they want, and they will be positioned at the start side of your flex container.
The flex item that wraps your image doesn’t have its own size (it has the default width: auto), so your image’s width: 100% can’t be resolved. In that case the image behaves as though it had width: auto, and basically renders at its full size. Then, the flex item takes on that size, and that’s why it’s so big in your flex container.
You can force all your flex items to be the width of your column flex container by not setting align-items and letting it use the default normal value, with stretches the flex items to the width of the flex container. Then your flex items have a specific size, and your image’s width: 100% can be resolved against that size.
Here’s a working example:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
background-color: #ccc;
padding: 10px;
display: flex;
flex-direction: column;
}
.item {
background-color: #ff1b68;
padding: 40px;
margin: 3px;
color: #fff;
font-size: 40px;
}
.item--2 {
height: 200px;
}
.item--3 {
}
.item--3 img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item item--1">1</div>
<div class="item item--2">2</div>
<div class="item item--3">
<img
src="https://images7.alphacoders.com/462/thumb-1920-462576.jpg"
alt=""
/>
</div>
</div>
By default, each flex item has align-self: auto, which means it will look at the flex container’s align-items value. So you set align-items once, and all flex items use that. But you can specify different align-self for each item, if you want to, for example, keep your text flex items align-self: start, and your image flex item align-self: stretch.
You can use a calculated width on your image element setting it to view width units => width: calc(100vw - padding - margin). So your image element will be 100% of the view width minus the padding and margin of the it and its parent element. Also set your container to min-width: 1000px;.
If the following is not what you are looking for, please let me know.
Resize the browser to test dynamic resizing of image to fit within viewable screen
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
background-color: #ccc;
padding: 10px;
min-height: 1000px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex-wrap: wrap;
}
.item {
background-color: #ff1b68;
padding: 40px;
margin: 3px;
color: #fff;
font-size: 40px;
/*flex-grow: 1;*/
}
.item--2 {
height: 200px;
}
.item--3 {
order: 1;
}
.item--3 img {
width: calc(100vw - 130px);
object-fit: cover;
}
<div class="container">
<div class="item item--1">1</div>
<div class="item item--2">2</div>
<div class="item item--3">
<img src="https://images7.alphacoders.com/462/thumb-1920-462576.jpg" alt="">
</div>
</div>

Best way to vertically-align children in an inline-block

I’m trying to vertically align the content in the cyan div without using one of the following methods:
Vertical align (as it requires displaying as a table & will have knock-on effects on the parent div padding etc)
Line height (as there’s more than 1 line with 3 actual spans)
Table (due to knock-on effects like with vertical align)
Absolute positioning (as some other cyan divs will have more text than others, meaning some will have more lines than others)
Equal top & bottom padding (same reason as abs positioning)
Flexboxes (I forbid these as they have serious knock-on effects!)
The cyan div is an inline-block. What would be the best way to achieve this? I want to be able to set the padding between each of the 3 spans to space them out nicely.
NOTE:
I've updated my answer to include flex positioning but on mobile view the 2nd cyan div below is not aligned centrally
.sections {
background: #f2f2f2;
width: 100%;
height: auto;
text-align: center;
padding: 100px 10%;
box-sizing: border-box;
}
h1 {
font-size: 62px;
text-transform: uppercase;
font-weight: 400;
}
p {
font-size: 22px;
font-weight: 300;
padding-top: 10px;
}
.foster-cta {
box-sizing: border-box;
width: 250px;
height: 200px;
background: cyan;
display: inline-block;
border: 1px solid grey;
margin-top: 30px;
/* adding display flex makes the 2nd cyan div below not aligned centrally on mobile view */
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: center;
display: inline-flex;
}
.foster-top {
display: block;
font-size: 30px;
}
.foster-middle {
display: block;
text-transform: uppercase;
font-size: 22px;
}
.foster-bottom {
display: block;
font-weight: 300;
font-size: 16px;
}
<section class="sections">
<h1>header 1 tag</h1>
<p>some random text to go here lorem ipsum sit semei geono wfnwoenfowe fwenfuowe ffe efnpi enfo wfeonwofun weofun weofnwe ofunwe foiej.</p>
<div class="foster-cta">
<span class="foster-top">icon</span>
<span class="foster-middle">some random text here</span>
<span class="foster-bottom">a load more random text goes here that will be more descriptive and longer.</span>
</div><!--
--><div class="foster-cta" style="margin-left:50px;">
<span class="foster-top">icon</span>
<span class="foster-middle">text</span>
<span class="foster-bottom"> a load more random text goes here that will be more descriptive and longer a load more random text goes here that will be more descriptive and longer.</span>
</div>
</section>
Flexbox is your best bet when doing any sort of aligning in CSS. The code is also very simple:
.div-parent {
display: flex;
}
.div-child {
align-self: center;
}
This will align vertically centered only

How to implement this notification design and have it sensibly responsive

I am attempting to implement this, simple enough design for a notification block.
Including the ability to respond by wrapping the text block on smaller screens, similar to the below;
The intention here is to center align the notification to the parent row, and preferably when the viewport is too small, have the text wrap and height of the horizontal banner on which it sits increase in height accordingly. This will be included in a bootstrap project (may affect floats etc).
Here is a pen showing one of the more simple approaches (and probably closest so far) I've been trying to achieve this.
*,
html {
font-family: arial;
font-size: 20px;
}
.extra-row {
text-align: center;
padding: 1em;
border: 1px solid #eee;
background-color: #ccc;
}
.notification {
text-align: center;
color: #fff;
white-space: nowrap;
}
.notification-circle {
width: 150px;
height: 150px;
background-color: #3D7A1A;
-moz-border-radius: 75px;
-webkit-border-radius: 75px;
border-radius: 75px;
position: relative;
display: -webkit-flexbox;
display: -ms-flexbox;
display: -webkit-flex;
display: inline-flex;
-webkit-flex-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
justify-content: center;
}
.notification-icon {
font-size: 5em;
}
.notification-block {
min-height: 150px;
line-height: 150px;
display: inline-block;
margin-left: -30px;
vertical-align: top
}
.notification-block span {
background-color: #54A127;
padding: 1em;
padding-left: 50px;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container">
<div class="row extra-row">
<div class="col-lg-12">
<p>This is a row above</p>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="notification">
<div class="notification-circle"><span class="notification-icon">i</span>
</div>
<p class="notification-block"><span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. </span>
</p>
</div>
</div>
</div>
<div class="row extra-row">
<div class="col-lg-12">
<p>This is a row below</p>
</div>
</div>
</div>
There's quite a few suggestions for vertically centering text in this manner, most appear to rely on line-height which is an issue here with wrapping the text.
This may not be the best approach due to using line-height, but the problems here are;
Preventing the circular container and text container from
wrapping.
Wrapping the text within the container while still
maintaining the overall height/vertically centered position of the text block.
Wrapping the text with a sensible line-height.
Adding white-space: nowrap; to the .notification element does prevent #1, but prevents the text from wrapping, which simply extends past the viewport.
Can anyone shed any light on a better approach? Any thoughts would be appreciated.
Many thanks,
Baps.
Hopefully this sets you on the right path.
I've removed a lot of unnecessary code. I've also removed the prefixes for the demo.
This adjustment may be all you need:
.notification {
display: flex; /* 1 */
align-items: center; /* 2 */
color:#fff;
}
.notification-circle {
flex: 0 0 150px; /* 3 */
height: 150px;
background-color: #3D7A1A;
border-radius: 75px;
display: flex;
align-items: center;
justify-content: center;
}
.notification-block {
margin-left: -50px; /* 4 */
background-color: #54A127; /* 5 */
padding: 1em; /* 5 */
padding-left: 75px; /* 5 */
z-index: -1; /* 6 */
}
.notification-block span { }
.notification-icon { font-size: 5em; }
Revised Codepen
Notes:
Make wrapper a flex container
Vertically center both flex children (.notification-circle and .notification-block)
Don't grow. Don't shrink. Remain fixed at 150px width.
Changed from margin-left: -30px
Relocated code from span child
Ensure .notification-block doesn't overlap .notification-circle