I'm writing a form that's supposed to be responsive, that is, when the browser window is small the left label "jumps" on top (see example below by removing the text-align property and resizing the browser).
Right now, it works well when the label text is left aligned.
For usuabilities issue, I'd like the label to be right aligned (I don't want them too far from the input box) but as soon as the flex has wrapped, I don't want them to be right aligned anymore.
Example code: https://jsfiddle.net/xhtfqbzL/
.cont {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
span
{
flex: 1 0 20vw;
text-align: right;
display: inline-block;
padding-right: 0.5em;
}
input
{
flex: 1 1 20rem;
display: inline-block;
}
<div class='cont'>
<span>Label</span>
<input type="text" placeholder="content">
</div>
So how to achieve this effect ?
Here is a hack to be used with caution (or not used at all ...)
.cont {
display: flex;
flex-wrap: wrap;
overflow: hidden;
}
.cont:before {
content: "";
flex: 1 0 20vw;
height: 1.2em;
}
span {
flex: 1 1 20rem;
position: relative;
}
span:before,
span:after {
content: attr(data-text);
position: absolute;
padding-right: 0.5em;
}
span:before {
left: 0;
bottom: 100%;
}
span:after {
top: 0;
right: 100%;
}
input {
width: 100%;
}
<div class='cont'>
<span data-text="Label">
<input type="text" placeholder="content">
</span>
</div>
Right now, my best attempt is this:
html, body { padding: 0; margin: 0 }
.cont
{
display: flex;
flex-wrap: wrap;
flex-direction: row;
}
.cont span, .cont input
{
display: inline-block;
}
.cont span
{
box-sizing: border-box;
text-align: right;
background: blue;
flex: 1 0 20rem;
}
#media screen and (max-width: 40rem) {
.cont span { text-align: left; }
}
.cont input
{
background: green;
flex: 1 1 50vw;
box-sizing: border-box;
}
<div class='cont'>
<span>Some Label</span>
<input type="text" value="Text here">
</div>
I did not want to have media queries because there's always the pain to find the breaking point (and this can evolve based on the design changes or device evolutions). Here, I've still used media queries but the 40rem magic value is computed, not selected by hand.
Typically, one of the form's row item has a fixed size, the other being relative to the viewport size. In this example, the fixed size is 20rem.
Since they are flex items, they'll wrap when they can't fit anymore on this row, that is when their width will reach their flex-basis property.
Thus, the width of the row is span_width[20rem] + input_width[50% * w] = 100% * w
I'm deducing that they will wrap when 100% w = 50% w + 20rem => 50%w = 20rem => w = 40rem
So they'll wrap when the viewport's width becomes lower than 40rem.
The main issue with this is that you must know the exact margin & padding size around such items and this is a real pain to maintain.
Another solution which is not using media queries:
html, body { padding: 0; margin: 0 }
#test
{
display: flex;
align-items: center;
flex-wrap: wrap;
flex-direction: row;
}
#test p
{
flex: 0 1 20rem;
padding:0;
margin:0;
}
#test p s
{
width: calc((48vw - 100%) * 5000);
min-width: 1%;
max-width: 16rem;
display: inline-block;
background: salmon;
height: 1rem;
}
#test p span
{
background: red;
padding-right: 0.5rem;
}
#test b
{
flex: 1 0 50vw;
background: yellow;
}
<div id="test">
<p>
<s></s>
<span>First</span>
</p>
<b>Second</b>
</div>
This works this way:
The label is split in 2 inline elements, a spacer and the label itself.
The spacer is using the Fab Four trick, that is, the width property is calculated to oscillate between +infinite and -infinite in order to be constrained by either min-width and max-width.
The breakpoint is set by the 2nd item in the flex's row (in the example, window_width:100vw - input_width:50vw): when the size left for the label is smaller than the minimum width of the row, its max-width property is used and this adds a "space" push the label element to the right. When the flex row wraps, the size left is now very large and above the breakpoint, and thus the min-width is selected (in my example, I've used 1% but it can be 0%, that is, almost no "margin" on the left of the label).
The caveat of this technic is that you must add a element (could probably be done with a ::before pseudo element here) and you must set a max-width less than the parent width - label width.
That point makes this solution is a pain to maintain.
Related
I've been trying to achieve the layout below using flexbox. I originally had a left hand sidebar containing the image & navigation, and a main content area. On mobile, the sidebar used to wrap under the main content.
The problem with that is that I need the image to remain at the top on mobile, so I've been trying with three sibling divs in one wrapper div.
Is this even possible with flexbox or will I need to use css grid?
Although CSS Grid would be the best approach to achieve the lay-out you want, it is possible using CSS Flexbox.
You just have to create a wrapper div with three divs inside (when doing a mobile first approach) and with .content set to flex: 1 to stretch out the height of your viewport.
Then for desktop (in this case #media screen and (min-width: 1000px)), change the order (MDN reference of order) of .navigation and .content and give all three divs appropriate widths according to their needs. The only change to div.wrapper is that it needs flex-flow: column wrap to wrap correctly.
.wrapper {
display: flex;
flex-direction: column;
height: 100%;
min-height: 100%;
}
.box {
display: flex;
}
.content {
flex: 1;
}
#media screen and (min-width: 1000px) {
.wrapper {
flex-flow: column wrap;
}
.navigation {
order: 2;
}
.content {
order: 3;
}
.image,
.navigation {
width: 200px;
flex: 50%;
}
.content {
width: calc(100% - 200px);
flex: 0 0 100%;
}
}
/* Generic styling */
html,
body {
height: 100%;
}
body {
margin: 0;
padding: 0;
}
.image {
background: orange;
height: 60px;
}
.content {
background: lightblue;
}
.navigation {
background: lightgreen;
height: 60px;
}
<div class="wrapper">
<div class="box image">Image</div>
<div class="box content">Content</div>
<div class="box navigation">Navigation</div>
</div>
I have a flexbox-based layout with two panels (top and bottom) occupying 1/3 and 2/3 of the viewport, respectively. (Actually there are more panels, but I've distilled it to the minimal example).
The top panel is also a flex container, because I want its children to flow top to bottom and be vertically centered when there is room. When there is more stuff in top panel than would fit in it, I want it to be scrollable, hence overflow: auto.
The problem: the contents of top shrink to its size, even with flex-shrink: 0, and the scrollbar never pops up.
Observe how the content is shrunk in the following demo, even though it has an explicitly specified height:
html, body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.main {
display: flex;
height: 100%;
flex-direction: column;
}
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
justify-content: center;
padding: 20px;
}
.bottom {
overflow: auto;
flex-basis: 67%;
}
.content {
height: 500px;
background-color: #eee;
}
<div class="main">
<div class="top">
<div class="content">Content</div>
</div>
<div class="bottom"></div>
</div>
The questions:
How to fix this while preserving the layout requirements? Disabling display: flex for the top panel gives the desired effect in the demo. I could position contents of top in a flexboxless way, but I lose the benefits of flex layout and the automatic vertical centering.
Why does this happen? References to CSS spec would be welcome.
You wrote:
The problem: the contents of top shrink to its size, even with flex-shrink: 0, and the scrollbar never pops up.
Actually, the solution is flex-shrink: 0. So the question becomes, where did you apply it?
It wouldn't work if you applied it to top – a flex item in the primary container with flex-basis: 33% (i.e., height: 33%, in this case) – because the length of top is a percentage. As such, it will shrink / expand naturally as percentage lengths are relative to the parent container.
You need to apply flex-shrink: 0 to .content – a flex item in the nested container with a fixed height (height: 500px / flex-basis: 500px).
So this will work:
.content {
height: 500px;
flex-shrink: 0;
}
or this:
.content {
flex-basis: 500px;
flex-shrink: 0;
}
or, better yet, this:
.content {
flex: 0 0 500px; /* don't grow, don't shrink, stay fixed at 500px */
}
From the spec:
7.2. Components of
Flexibility
Authors are encouraged to control flexibility using the flex shorthand
rather than with its longhand properties directly, as the shorthand
correctly resets any unspecified components to accommodate common
uses.
body {
margin: 0;
}
.main {
display: flex;
flex-direction: column;
height: 100vh;
}
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
justify-content: center;
padding: 20px;
}
.bottom {
overflow: auto;
flex-basis: 67%;
}
.content {
flex: 0 0 500px;
background-color: #eee;
}
<div class="main">
<div class="top">
<div class="content">Content</div>
</div>
<div class="bottom"></div>
</div>
Then you have a second problem, which is that the upper section of the top element gets cut off and is inaccessible via scroll. This is caused by justify-content: center on the container.
This is a known issue. It is solved by using flex auto margins.
So instead of this:
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
/* justify-content: center; <--- REMOVE */
padding: 20px;
}
do this:
.content {
flex: 0 0 500px;
margin: auto 0; /* top & bottom auto margins */
background-color: #eee;
}
body {
margin: 0;
}
.main {
display: flex;
flex-direction: column;
height: 100vh;
}
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
/* justify-content: center; USE AUTO MARGINS ON FLEX ITEM INSTEAD */
padding: 20px;
}
.bottom {
overflow: auto;
flex-basis: 67%;
}
.content {
flex: 0 0 500px;
margin: auto 0;
background-color: #eee;
}
<div class="main">
<div class="top">
<div class="content">Content</div>
</div>
<div class="bottom"></div>
</div>
Here's a complete explanation:
Can't scroll to top of flex item that is overflowing container
The scrollbar appears when there are enough .content element shrinked to their very minimal height (one line height in this case).
That's not really how things work with flex. height is not strictly respected. If you still want to work with height, you can fix this by setting a min-height to .content according to the minimum height you want for them.
Or you can instead set flex on .content (and get rid of height):
css
flex: 100px 1 0;
Which will set a minimum height (flex-basis) of 100px, flex-grow at 1 so that it takes all the available space, and flex-shrink at 0 so that the element is always at least 100px tall.
I have two divs as shown below (A and B):
Section B is has an input field with max-height of 100px (as an example) and overflow-y auto: This way, the input field will only be certain height.
.section_B{
position: absolute;
bottom: 0;
width: 100%;
}
.section_B_input{
max-height: 100px;
overflow-y: auto;
}
Because Section B's height can be anywhere in between 20px and 100px (for example), the section A's height needs to be dynamic and is depended on Section B height.
I read that display:flex can be used somehow, but I am not sure how to.
Any suggestions?
Thanks!
The technique with flexbox is to add flex-grow: 1; to the element you want to have a dynamic height. Here is a quick example.
* {margin:0;padding:0;box-sizing:border-box;}
html,body,.flex {
min-height: 100vh;
}
.flex {
display: flex;
flex-direction: column;
}
.a {
flex-grow: 1;
background: #eee;
}
.b {
background: #333;
}
section {
padding: 2em;
}
input {
transition: padding .5s;
}
input:focus {
padding: 2em;
}
<div class="flex">
<section class="a">
</section>
<section class="b">
<input>
</section>
</div>
I've two flexbox columns, but one is just empty and is used as "left margin" for the other column.
http://codepen.io/FezVrasta/pen/MKWvwo
I'd like to avoid to insert in the markup the first column but maybe define to the second column something like
margin-left: flex-1
this should just add a left-margin wide as a "flex 1" unit.
Is there a way?
PS
clarifying for the one that voted to close because unclear.
You have a flexbox with width 300px, inside, you have a column with flex: 1 and one with flex: 2.
The "flex unit" will be 100px (thee are 3 flex units (1 + 2), 300/3= 100)
So, your issue is to clean the DOM and avoid an artificial element.
You can use a pseudo element for this
.flex {
display: flex;
}
.flex:before {
content: "";
background: red;
flex: 1;
}
.two {
background: blue;
flex: 2;
}
<div class="flex">
<div class="two"> 2</div>
</div>
It looks like you want an empty space on the left hand side and you want the second column to be pushed to the right. If this is the case I think you can resolve your issue by adding a width(%) to your second column and add the following property to your container:
.flex-container{
justify-content: flex-end;
}
Get rid of the first column all together.
You can use the justify-content property to get the desired result. You must give a width to the remaining column that, if you want to keep it flexible should be in % units.
.flex{
justify-content: flex-end;
}
.two {
flex-basis: 66%;
}
See codepen: http://codepen.io/anon/pen/YwzxqM
If you have more columns then you have to distribute the 100% width among them and you're done.
You can use flexbox grid which is same as bootstrap grid just with Flexbox
http://flexboxgrid.com/
DEMO
<div class="row">
<div class="col-xs-offset-4 col-xs-8 column"></div>
</div>
So here col-xs-offset-4 behave same as margin-left
So the criteria is that you want the left column to be a "flex-margin" at the constant length of one flex-unit? Take a look and see if I'm grokking* correctly.
http://codepen.io/01/pen/VewzJL
CSS
html,
body {
box-sizing: border-box;
font: 400 16px/1.4 'Source Code Pro';
height: 100vh;
width: 100vw;
}
.flex {
width: 100%;
height: 100%;
display: flex;
flex-flow: row nowrap;
justify-content: baseline;
}
.one {
background: red;
flex: 0 2 25%;
}
.two {
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
align-content: flex-start;
background: blue;
flex: 2 0 75%;
}
.two div {
outline: 1px solid yellow;
background-color: cyan;
min-height: 10%;
height: 1.5em;
flex: 2 1 auto;
}
*That's awesome! The spellcheck accepted "grokking"!
How do I control the height of a flexbox so that it stays proportional to the width as the element grows?
I want the height of .inner to remain proportional to a given ratio as its width changes.
All examples of flexbox I've seen either holds the height constant when the width changes, or grows enough to contain its contents.
(haml)
.outer
.inner
%img
.inner
.inner
Perhaps the example will be helped if we include an image within it... or maybe not. just throwing an idea out there.
(sass)
.outer {
display: flex;
.inner {
flex: 1 1 auto;
}
}
There is no method specific to flexbox that would manage this.
There is well known padding-bottom trick that would permit this but it requires a pseudo-element (for preference) and an internal absolutely positioned div to hold the content.
Reference Web Link
As you will appreciate, absolute positioning is somewhat inflexible so laying out your content would be the main issue.
Applying this to flexbox:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.outer {
display: flex;
width: 500px;
margin: 1rem auto;
align-items: flex-start;
}
.inner {
flex: 1 1 auto;
border: 1px solid grey;
position: relative;
}
.inner:before {
content: "";
display: block;
padding-top: 100%;
/* initial ratio of 1:1*/
}
.content {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
/* otehr ratios */
.ratio2_1:before {
padding-top: 50%;
}
.ratio1_2:before {
padding-top: 200%;
}
.ratio4_3:before {
padding-top: 75%;
}
.ratio16_9:before {
padding-top: 56.25%;
}
<div class="outer">
<div class="inner">
<div class='content '>Aspect ratio of 1:1</div>
</div>
<div class="inner ratio2_1">
<div class='content'>Aspect ratio of 2:1</div>
</div>
<div class="inner ratio16_9">
<div class='content'>Aspect ratio of 16:9</div>
</div>
</div>