I'm trying to make a horizontally scrolling container that holds a bunch of tabs. I want to make an overlay (think an options menu) that can appear over a tab. And what I would like is for the overlay to sit over the top of the horizontal scrollbar. Here is an image with what I mean:
But here is what I actually get:
I've recreated my problem with a small example below:
.page {
display: flex;
flex-direction: column;
max-width: 800px;
margin: 0 auto;
}
.header {
height: 30px;
background: #ccc;
display: flex;
}
.items {
display: flex;
align-items: stretch;
margin-right: 0.5rem;
&:last-child {
margin-right: 0;
}
> * {
margin-right: 0.5rem;
&:last-child {
margin-right: 0;
}
}
}
//Why I'm using 2 wrappers: https://front-back.com/how-to-make-absolute-positioned-elements-overlap-their-overflow-hidden-parent/
.scroll-super-wrapper {
display: flex;
flex-grow: 1;
height: 100%;
width: 100%;
position: relative;
}
.scroll-wrapper {
display: flex;
flex-grow: 1;
overflow-x: hidden;
}
.tabs {
display: flex;
flex-wrap: nowrap;
align-items: start;
position: absolute;
top: 0;
right: 0;
bottom: -0.85rem; // Makes the scrollbar appear underneath
left: 0;
overflow-x: auto;
}
.tab {
background: #444;
color: #FFF;
display: flex;
align-items: center;
white-space: nowrap;
height: 100%;
text-align: center;
padding: 0 1rem;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
margin-right: 0.5rem;
position: relative;
&:last-child {
margin-right: 0;
}
}
.overlay {
position: absolute;
top: 100%;
left: 0;
height: 100px;
padding: 1rem;
background-color: black;
}
<div class="page">
<div class="header">
<div class="items left-items">
<button>Options</button>
<button>New</button>
</div>
<div class="items scroll-super-wrapper">
<div class="scroll-wrapper">
<div class="tabs">
<div class="tab">Tab 1</div>
<div class="tab">Tab 2</div>
<div class="tab">
Tab 3
<div class="overlay">
Overlay content
</div>
</div>
<div class="tab">Tab 4</div>
<div class="tab">Tab 5</div>
<div class="tab">Tab 6</div>
<div class="tab">Tab 7</div>
<div class="tab">Tab 8</div>
<div class="tab">Tab 9</div>
<div class="tab">Tab 10</div>
<div class="tab">Tab 11</div>
</div>
</div>
</div>
<div class="items left-items">
<button>Export</button>
<button>Share</button>
</div>
</div>
</div>
Stackoverflow's code snippets totally mess up the formatting. I've made a codepen here: https://codepen.io/anon/pen/rXLZgN
Is it possible to do this without using JavaScript to calculate the x/y coordinates and make the element position: fixed? I would like to do this with just CSS if possible.
You defined your tabs class as absolute to the relative scroll-super-wrapper
That means that the wrapper will always contain it.
so or you will built it again from in a different way.
or you can take your overlay class and set it position:fixed and it will take it out the relative of the wrapper (not recommended at all);
Related
I have made some cards and restricted them from wrapping, thus making
a card-wrapper which becomes container for Horizontal Scrollable
Cards.
I am Facing small issue. The Last Card does not get any spacing at
the end..
See below screenshot
html,
body {
width: 100%;
height: 100%;
}
body {
background-color:#f4f4f4;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
}
.card {
display: flex;
background-color: #fff;
min-width: 100%;
min-height: 200px;
margin: 1rem;
overflow-x: auto;
}
.card--content {
background-color: #DED3EE;
min-width: 200px;
margin: 5px;
}
<section class="card">
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
</section>
Codepen Link : Click
Any help?
I think you are looking for this solution. You should calculate .card-track width dynamicaly
This method use when creating image slider
html,
body {
width: 100%;
background-color:#f4f4f4;
display: flex;
justify-content: center;
align-items: center;
}
.card {
background-color: red;
width: 80%;
position: relative;
display: block;
overflow: hidden;
margin: 0;
padding: 0;
overflow-x: scroll;
}
.card-track {
position: relative;
top: 0;
left: 0;
display: block;
margin-left: auto;
margin-right: auto;
width: calc(200px*10 + 10px*10); /*Card content width x no of iems + margins */
}
.card--content {
background-color: #DED3EE;
width: 200px;
height: 120px;
margin: 5px;
float: left;
}
<section class="card">
<div class="card-track">
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
<div class="card--content"></div>
</div>
</section>
All you need to do is switch your card spacing from margin to padding and add an inner container to each card.
Overflowing margin doesn't push the items within the parent container, rather it pushes the siblings outside. Since you have overflow hidden (by auto/scroll), it is effectively rendered margin-right: 0;
A relatively easy fix is to use padding instead. But you'll have to create an inner container so that you can use padding on the contents and so that the background-color is contained within the spacing.
You can see a working example here: https://codepen.io/rjhewitt3/pen/MqYjeg
I am using a flex grid to lay out information. I want to highlight one of the cells within the grid so that it stands out to users by adjusting the height of the respective cell. However, my attempts have not gotten far as adjusting the properties of once cell will thereby affect the surrounding cells.
In my fiddle below, I have a class .highlighted within .flexbox-2 that I would like to change. Basically, the row 1 of the second column would have a taller height than the first and third column, but all the borders will still be aligned. I was thinking to apply position: absolute and change its CSS there, but this does not prove fruitful. I'm wondering if there are other routes I can take.
Check this jsfiddle
Code:
* {
box-sizing: border-box;
}
body {
font-family: helvetica, serif;
}
.container {
display: -webkit-flex;
-webkit-flex-direction: row;
}
.flex {
display: flex;
flex-direction: column;
}
.flex-row {
flex: 1;
border: 1px solid;
}
.flexbox-1 {
-webkit-flex: 1;
border: solid 3px red;
}
.flexbox-2 {
-webkit-flex: 1;
border: solid 3px green;
height: 200px;
margin-left: 10px;
position: relative;
}
.highlighted {
position: absolute;
border: 1px solid yellow;
padding-top: 20px;
}
.flexbox-3 {
-webkit-flex: 1;
border: solid 3px blue;
height: 200px;
margin-left: 10px;
}
<div class="container">
<div class="flex flexbox-1">
<div class="flex-row">row 1</div>
<div class="flex-row">row 2</div>
<div class="flex-row">row 3</div>
</div>
<div class="flex flexbox-2">
<div class="flex-row highlighted">row 1</div>
<div class="flex-row">row 2</div>
<div class="flex-row">row 3</div>
</div>
<div class="flex flexbox-3">
<div class="flex-row">row 1</div>
<div class="flex-row">row 2</div>
<div class="flex-row">row 3</div>
</div>
</div>
If you want that first row to stick out above the other two columns, you could use a negative margin-top:
.highlighted {
border: 1px solid yellow;
margin-top: -10px;
padding-bottom: 10px;
}
Working Example
Need to split all tabs in such a way that. Last tab width will be 50% width and rest all tabs must fit in 50%. For now I gave fixed width: 16.65%. Would like to avoid it as there could 2 or 3 tabs excluding last tab.
Can this be achieved using display: flex ?
* {
margin: 0;
}
.wrapper {
overflow: hidden
}
.wrapper .tab {
float: left;
width: 16.65%;
text-align: center;
background: #ccc;
}
.wrapper .last-tab {
width: 50%;
background: #999
}
<div class="wrapper">
<div class="tab">Tab 1</div>
<div class="tab">Tab 2</div>
<div class="tab">Tab 3</div>
<div class="tab last-tab">Last Tab</div>
</div>
You can do it like this:
* {
margin: 0;
}
.wrapper {
display: flex; /* displays children inline */
overflow: hidden;
}
.wrapper > .tab {
flex: 1; /* enables growing of flex-items so they can fill flex-containers width / this is the shorthand way, but you can also use: flex: 1 1 auto; (i.e. flex-grow, flex-shrink, flex-basis) */
/*float: left;*/
/*width: 16.65%;*/
text-align: center;
background: #ccc;
}
.wrapper > .last-tab {
/*width: 50%;*/
flex: 0 1 50%; /* adjusted to take half of the wrappers width (i.e. initial width is set to 50%) */
background: #999;
}
<div class="wrapper">
<div class="tab">Tab 1</div>
<div class="tab">Tab 2</div>
<div class="tab">Tab 3</div>
<div class="tab last-tab">Last Tab</div>
</div>
If you want to do it pure flexbox way without the use of the width property.
try this
* {
margin: 0;
}
.wrapper {
overflow: hidden;
display: flex;
width: 100%;
}
.wrapper .tab {
width: calc(50%/3);
text-align: center;
background: #ccc;
}
.wrapper .last-tab {
width: 50%;
background: #999
}
<div class="wrapper">
<div class="tab">Tab 1</div>
<div class="tab">Tab 2</div>
<div class="tab">Tab 3</div>
<div class="tab last-tab">Last Tab</div>
</div>
Take 2 blocks of 50-50%. Then you can easily divide inner divs as per your requirement.
Hope this helps you :)
* {
margin: 0;
}
.wrapper {
/*overflow: hidden*/
display:flex;
}
.wrapper .tab-container {
/*float: left;*/
display:flex;
width: 50%;
background: #ccc;
}
.wrapper .tab {
/*float: left;*/
display:flex;
width: 33.33%;
text-align: center;
}
.wrapper .last-tab {
width: 50%;
background: #999;
text-align: center;
}
<div class="wrapper">
<div class="tab-container">
<div class="tab">Tab 1</div>
<div class="tab">Tab 2</div>
<div class="tab">Tab 3</div>
</div>
<div class="last-tab">Last Tab</div>
</div>
We have two divs with content and a third div that is a background with absolute position.
Container is a flexbox.
All works fine in Chrome and Safari, but Firefox and IE11 factors in the absolute positioned div, and distributes space between divs like there are 3 divs in a row.
I've made jsfiddle example. Is there any way to fix this bug?
https://jsfiddle.net/s18do03e/2/
div.container {
display: flex;
flex-direction: row;
width: 100%;
height: 300px;
justify-content: space-between;
width: 100%;
outline: 1px solid;
}
div.c1 {
background: #aaeecc;
width: 100px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.c2 {
background: #cceeaa;
width: 200px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.bg {
background: #ccc;
width: 100%;
height: 100%;
z-index: 0;
left: 0px;
top: 0px;
position: absolute;
display: flex;
}
<div class="container">
<div class="c1">Content 1</div>
<div class="c2">Content 2</div>
<div class="bg">Background</div>
</div>
UPDATE: This issue has been resolved in Firefox (as of v52, released March 2017). The problem still exists in IE11.
Like you wrote in the question:
Firefox calculates absolute positioned div, and distributes space between divs like there are 3 divs in a row.
Firefox is considering the third div (.bg), which is absolutely positioned, an in-flow flex item and factoring it into its space-between calculation. (IE11 does this, too; Chrome and Edge ignore it.)
Clearly, this is not in compliance with the current flexbox spec:
4.1. Absolutely-Positioned Flex Children
As it is out-of-flow, an absolutely-positioned child of a flex
container does not participate in flex layout.
Here are some workarounds:
Why not move the absolutely positioned div between the other two?
Instead of this:
<div class="container">
<div class="c1">Content 1</div>
<div class="c2">Content 2</div>
<div class="bg">Background</div>
</div>
Try this:
<div class="container">
<div class="c1">Content 1</div>
<div class="bg">Background</div>
<div class="c2">Content 2</div>
</div>
div.container {
display: flex;
flex-direction: row;
width: 100%;
height: 300px;
justify-content: space-between;
width: 100%;
outline: 1px solid;
}
div.c1 {
background: #aaeecc;
width: 100px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.c2 {
background: #cceeaa;
width: 200px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.bg {
background: #ccc;
width: 100%;
height: 100%;
z-index: 0;
left: 0px;
top: 0px;
position: absolute;
display: flex;
}
<div class="container">
<div class="c1">Content 1</div>
<div class="bg">Background</div>
<div class="c2">Content 2</div>
</div>
OR... remove .bg from the flex container:
<div class="container">
<div class="c1">Content 1</div>
<div class="c2">Content 2</div>
</div>
<div class="bg">Background</div>
div.container {
display: flex;
flex-direction: row;
width: 100%;
height: 300px;
justify-content: space-between;
width: 100%;
outline: 1px solid;
}
div.c1 {
background: #aaeecc;
width: 100px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.c2 {
background: #cceeaa;
width: 200px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.bg {
background: #ccc;
width: 100%;
height: 100%;
z-index: 0;
left: 0px;
top: 0px;
position: absolute;
display: flex;
}
<div class="container">
<div class="c1">Content 1</div>
<div class="c2">Content 2</div>
</div>
<div class="bg">Background</div>
OR... use the flex order property to re-arrange the flex items.
Add this to your code:
.c2 { order: 1; }
div.container {
display: flex;
flex-direction: row;
width: 100%;
height: 300px;
justify-content: space-between;
width: 100%;
outline: 1px solid;
}
div.c1 {
background: #aaeecc;
width: 100px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.c2 {
background: #cceeaa;
width: 200px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
order: 1;
}
div.bg {
background: #ccc;
width: 100%;
height: 100%;
z-index: 0;
left: 0px;
top: 0px;
position: absolute;
display: flex;
}
<div class="container">
<div class="c1">Content 1</div>
<div class="c2">Content 2</div>
<div class="bg">Background</div>
</div>
It is happening because justify-content: space-between; Distribute items evenly The first item at the start, the last at the end. So just putt <div class="bg">Background</div> between <div class="c1">Content 1</div> and <div class="c2">Content 2</div>
like this
<div class="container">
<div class="c1">Content 1</div>
<div class="bg">Background</div>
<div class="c2">Content 2</div>
</div>
You can see the reason on https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content
Sometimes it is not possible to re-order stuff, for example when using ::before and ::after. In those cases, you can manually order the elements.
In your case, you would need to do:
.c1 {
order: -1;
}
.c2 {
order: 10;
}
The order property is part of the flex spec and lets you re-order flex items (reference on MDN). It's very handy for multiple purposes, this included.
I used -1 because the value is ordinal, so setting it to a negative number ensures it precedes all other defaults and you don't need to specify the value for ::before. For the same reason, using 10 ensures that the second div comes last even if you add a bunch of elements to the container. You can increase that to 100 or whatever.
Still, Firefox's behaviour seems counterintuitive. position: absolute normally removes the element for the usual dom flow and I would expect that element to be removed from the flex flow as well, just like in Safari and Chrome. I am not sure whether the spec clarify this.
As an alternative you can use the flex property inside the content selector(s):
div.c1 {
background: #aaeecc;
width: 100px;
position: relative;
z-index: 50; top: 20px;
display: flex;
flex: 1; /* add this */
}
This will set the flex-grow. It might not be exactly what you need, but maybe it helps somebody else that can not reorder the content divs or take them out from the flex wrapper.
Here is the demo:
https://jsfiddle.net/s18do03e/14/
I would like to have three separate vertical columns, is there a way I can change my code to make the columns vertical instead of horizontal (like they are now).
.cols {
font-weight: bold;
min-height: 50%;
min-width: 90%;
background: #000000;
margin-bottom: 15px;
display: table;
table-layout: fixed;
}
.cols div {
position: relative;
background: #232323;
}
.col {
display: table-cell;
}
<div class="cols">
<div class="col">Column 1</div>
<div class="col">Column 2</div>
<div class="col">Column 3</div>
</div>
Currently I have three horizontal boxes stretching across an outside container, I would like the three boxes to be evenly set out in vertical columns, if that makes sense.
If I understand what you mean, this can be done using flex:
.cols {
min-height: 50%;
min-width: 90%;
background: #000000;
margin-bottom: 15px;
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
}
.cols div {
background: #232323;
color: #fff;
}
<div class="cols">
<div class="col">Column 1</div>
<div class="col">Column 2</div>
<div class="col">Column 3</div>
</div>