Child take width % from parents parent - css

Is the following result possible with CSS:
So that li.item takes 50% of the width of div.wrapper, not the ul.list (which is extremly long).
I've added a snippet of a basic setup. Any ideas on the matter are appreciated (please keep in mind I'm looking for CSS options). A jsfiddle playground link: http://jsfiddle.net/6o8t9t8L/
.wrapper {
width: 400px;
overflow-y: hidden;
overflow-x: scroll;
border: 1px solid black;
}
.list {
list-style-type: none;
border: 1px solid red;
width: 2000px;
}
.item {
display: inline-block;
height: 200px;
width: 50%;
border: 1px solid green;
}
<div class="wrapper">
<ul class="list">
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
</ul>
</div>

I believe there are some 'workaround' solutions to your issue, so I'll pour in some of my ideas, maybe it will help you out a bit.
Idea 1: Position absolute and a bunch of :nth-child selectors
In order to make the .item have their width relative to .list wrapper, you can absolute position these items, and set .list wrapper to position relative, so that the .item width will be calculated based on .list width.
The major downfall of this idea would be that you have to position these elements next to each, like using the left property, but passing it like a loop:
first item will have left: 0;
second item will have left: 50%;
third item will have left: 100%;
and so on...+50% to the next items
You can either pour in a bunch of :nth-child(n), each with +50% left prop. from each other, OR use some sass stuff to make it faster.
Check out the demo here & sass demo here
*,
*:after,
*:before {
box-sizing: border-box;
}
.wrapper {
width: 400px;
overflow-y: hidden;
overflow-x: scroll;
border: 1px solid black;
/*make the grandparent, .wrapper, relative, so that the grandchilds, .item,
will calculate their width based on this width*/
position: relative;
}
.list {
list-style-type: none;
border: 1px solid red;
width: 2000px;
margin: 50px 0;
padding: 0;
/*since everyone has position absolute, theres no content flow, so a fixed height
has to be supplied*/
height: 200px;
}
.item {
width: 50%;
border: 1px solid green;
position: absolute;
left: 0;
/*you can set a height here, or position them like I did bellow*/
top: 51px;
bottom: 51px;
}
/*now the fun part starts
somehow these .items have to have left: +50% for each of them, like a loop somehow,
so you can either pour in a lot of nth-child(), for how many children you think this
list is going to have, or use sass to write it faster like i did here:
*/
.item:nth-child(1) {
left: 0;
}
.item:nth-child(2) {
left: 50%;
}
.item:nth-child(3) {
left: 100%;
}
.item:nth-child(4) {
left: 150%;
}
.item:nth-child(5) {
left: 200%;
}
.item:nth-child(6) {
left: 250%;
}
.item:nth-child(7) {
left: 300%;
}
.item:nth-child(8) {
left: 350%;
}
.item:nth-child(9) {
left: 400%;
}
.item:nth-child(10) {
left: 450%;
}
.item:nth-child(11) {
left: 500%;
}
<div class="wrapper">
<ul class="list">
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
</ul>
</div>
Idea 2: Display: flex
Using display: flex on .wrapper, will allow you to have the widths of the .item to be relative to their grandparent.
The major downfall of this idea would be that the width of .list element, will be overwritten by the width of .wrapper, no matter if you specify it or not. However, not all is lost, if you need that specific width for some styling, you can specify it, and use some pseudo classes with width: inherit, so they'll stretch to whatever width you specified in the first place.
Check out the demo here
*,
*:after,
*:before {
box-sizing: border-box;
}
.wrapper {
width: 400px;
overflow-y: hidden;
overflow-x: scroll;
border: 1px solid black;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
/*bring on the awesomeness*/
margin: 20px;
}
.list {
list-style-type: none;
/*border: 1px solid red;*/
/*you can keep this defined width, items will calculte their width
based on .wrapper class, wich will overwrite this classes width,
however if you have some use for this width, consider using :after, :before
classes like I did bellow, with .list:before*/
position: relative;
z-index: 1;
width: 2000px;
white-space: nowrap;
margin: 20px 0;
padding: 0;
font-size: 0;
/*display inline block extra spacing ....*/
}
.list:before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: inherit;
/*it will inherit the width you set above*/
border: 1px solid red;
}
.item {
display: inline-block;
vertical-align: top;
height: 200px;
width: 50%;
border: 1px solid green;
font-size: 16px;
/*bump back the font-size*/
}
<div class="wrapper">
<ul class="list">
<li class="item">a</li>
<li class="item">b</li>
<li class="item">c</li>
<li class="item">d</li>
</ul>
</div>

<div class="wrapper">
<ul class="list">
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
</ul>
</div>
.wrapper {
width: 400px;
border: 1px solid black;
}
.list {
list-style-type: none;
border: 1px solid red;
width: 400px;
display: block;
white-space:nowrap;
padding: 0;
margin: 0;
}
.item {
display: inline-block;
height: 200px;
width: 47%;
border: 1px solid green;
padding: 1%;
}
http://jsfiddle.net/btsewL9v/
I would suggest something like this..
EDIT:
I don't know how many items you are trying to cram into the list, but take a look at this:
http://lea.verou.me/2011/01/styling-children-based-on-their-number-with-css3/

Related

How to create progress steps in CSS

I have a list of items that I want to turn into a progress steps in CSS.
ol {
width: 800px;
}
li {
display: inline-block;
color: transparent;
height: 25px;
width: 25px;
background-color: #abc;
border-radius: 50%;
border: 2px solid #08f;
margin-right: 150px;
}
li:last-child {
margin-right: 0;
}
li:not(:last-child)::before {
content: "";
border: 2px solid #08f;
margin-left:25px;
width: 153px;
display: inline-block;
}
<ol>
<li>Step 1</li>
<li>Step 2</li>
<li>Step 3</li>
<li>Step 4</li>
</ol>
What I ideally want to do is:
Stop Step 4 from disappearing off the bottom
Use the content of the <li> as a label above the circle
Make the total width equal to 80% of the viewport width
I'm just trying to teach myself some more advanced CSS, and I've seen this pattern used somewhere else - but I've been trying for an hour or so to get there with no joy.
This is a learning exercise for me, so would love some explanation with the answer if you have the time.
Thanks,
body {
display: grid;
place-items: center;
margin: 0;
padding: 36px;
}
ol {
display: flex;
width: 80%;
padding: 12px 0;
}
li {
position: relative;
padding: 0;
flex-grow: 1;
height: 2px;
background: #08f;
display: flex;
align-items: center;
}
li:last-child {
margin-right: 0;
flex-grow: 0;
}
li::after {
content: "";
position: absolute;
left: 0;
display: inline-block;
color: transparent;
height: 24px;
width: 24px;
background: #abc;
border-radius: 50%;
border: 2px solid #08f;
}
span {
position: absolute;
top: -36px;
left: 12px;
width: max-content;
transform: translateX(-50%);
z-index: 1;
}
<ol>
<li><span>Step 1</span></li>
<li><span>Step 2</span></li>
<li><span>Step 3</span></li>
<li><span>Step 4</span></li>
</ol>
In my code the nodes are the pseudo elements, and I use the flex-grow property so that the rules (that are the li tags) are properly distributed. font-size: 0 hides the text and removes it from the content-size of the elements as well.
---- edit:
I removed the font-size: 0 and added span tags for the labels and the css to position it.

How to create dynamic bracket borders around content?

I'm trying to add this gray bracket to the right of a list. I tried playing around with borders on the div around the each item, but the items are separated by a few other tags and margin space top and bottom, also I don't know how to get only part of a top border showing. I need the brackets to expand and stay aligned if a selected item is expanded.
Any CSS recommendations?
This is where I'm at now https://jsfiddle.net/ux9dxoa9/
.item {
float: left;
clear:left;
border: 2px solid red;
padding: 8px 0px;
height: 100px;
width: 100px;
}
.other_div{
float: left;
padding: 18px 0px;
height: 100px;
width: 100px;
}
You can use pseudo elements
* {
margin: 0; padding: 0; box-sizing: border-box;
}
.outer {
display: inline-block;
border-right: 1px solid #ddd;
margin-left: 30px;
}
.outer, .outer > li {
position: relative;
}
.outer > li {
padding: 1em;
}
.outer:before, .outer:after, .outer > li:not(:first-child):before {
content: '';
position: absolute;
width: .5em;
height: 1px;
background: #ddd;
}
.outer:before, .outer:after, .outer > li:before, .outer > li:after {
right: 0;
}
.outer:before, .outer > li:before {
top: 0;
}
.outer:after, .outer > li:after {
bottom: 0;
}
<ul class="outer">
<li>item</li>
<li>item</li>
<li>nested
<ul>
<li>nested</li>
<li>nested</li>
<li>nested</li>
</ul>
</li>
<li>item</li>
<li>item</li>
</ul>

Disable offsetting content on hover selector

When I'm hovering content inside div I'm getting offsets toward right side, I know that's because I'm adding left border, but how can I avoid this?
ul li:hover {
border-left: 4px solid #FF2F5A;
}
https://jsfiddle.net/eeeek/j8xaru7m/
While one can set a transparent border for the non-hovered state, that might sometimes given unwanted alignment.
Here is a solution using a pseudo element that overcomes that. The position: relative; on the ul li is necessary for the absolute positioned pseudo to position itself properly.
ul li {
position: relative; /* added property */
margin-left: -26px;
list-style-type: none;
width: 491px;
height: 50px;
border-bottom: 1px solid #F2F2F2;
}
ul li > span {
line-height: 40px;
margin-left: 30px;
}
ul li:hover::before { /* updated rule */
content: '';
position: absolute;
top: 0; left: 0; bottom: 0;
width: 4px; background: #FF2F5A;
}
.items {
position: absolute;
margin-left: -17.7px;
}
.block-with-items-style {
margin-left: 50px;
height: 400px;
width: 300px;
border: 2px solid #f2f2f2;
box-shadow: 2px 2px 10px #adadad;
resize: vertical;
overflow: hidden;
}
<div class="block-with-items-style">
<div class="items">
<ul>
<li ng-repeat="record in records">
<span>1</span>
<span class="badge badge-style">2</span>
<button class="delete" ng-click="Delete($index)">3</button>
</li>
</ul>
</div>
</div>
Bonus: This way one can easily add an animation as well
ul li {
position: relative; /* added property */
margin-left: -26px;
list-style-type: none;
width: 491px;
height: 50px;
border-bottom: 1px solid #F2F2F2;
}
ul li > span {
line-height: 40px;
margin-left: 30px;
}
ul li::before { /* added rule */
content: '';
position: absolute;
top: 50%; left: 0; bottom: 50%;
width: 4px; background: #FF2F5A;
transition: top .5s, bottom .5s;
}
ul li:hover::before { /* updated rule */
top: 0; bottom: 0;
}
.items {
position: absolute;
margin-left: -17.7px;
}
.block-with-items-style {
margin-left: 50px;
height: 400px;
width: 300px;
border: 2px solid #f2f2f2;
box-shadow: 2px 2px 10px #adadad;
resize: vertical;
overflow: hidden;
}
<div class="block-with-items-style">
<div class="items">
<ul>
<li ng-repeat="record in records">
<span>1</span>
<span class="badge badge-style">2</span>
<button class="delete" ng-click="Delete($index)">3</button>
</li>
</ul>
</div>
</div>
Just create invisible (100% alpha) border with previous css directive. As long hover is active – it will overwrite that transparent border with red. Here's the code:
ul li {
margin-left: -26px;
list-style-type: none;
width: 491px;
height: 50px;
border-bottom: 1px solid #F2F2F2;
}
ul li > span {
line-height: 40px;
margin-left: 30px;
}
/* Sylogista's upgrade begin */
ul li:not(:hover)
{
border-left: 4px solid rgba(255,255,255,0);
}
/* Sylogista's upgrade end */
ul li:hover {
border-left: 4px solid #FF2F5A;
}
.items {
position: absolute;
margin-left: -17.7px;
}
.block-with-items-style {
margin-left: 50px;
height: 400px;
width: 300px;
border: 2px solid #f2f2f2;
box-shadow: 2px 2px 10px #adadad;
resize: vertical;
overflow: hidden;
}
<div class="block-with-items-style">
<div class="items">
<ul>
<li ng-repeat="record in records">
<span>1</span>
<span class="badge badge-style">2</span>
<button class="delete" ng-click="Delete($index)">3</button>
</li>
</ul>
</div>
</div>
:not(:hover) is optional. Code will work good without it, but in my opinion – using that can be good practice. When? That's not place for debate, but let me give one short example in the margin of that answer: when you're creating huge css using many of states in that way and relations between elements – it just looks clearer.
In that case it's clearer maybe without it, but I'll not change that. Even just because of that not so many people use :not or even remember about that.

How to force child elements to expand the parent element?

I make a drop-down menu with a lot of items (I don't know exactly how many).
I expect the child <ul> to expand to max-width when it has enough items, but actually it expands to parent <div>'s width only, no more.
Why?
Upd: I want to break long lines, not to shrink them by flex.
Upd 2: I want to the child <ul> to be not smaller than the parent <div>.
div {
background: aquamarine;
height: 50px;
position: relative;
width: 200px;
}
ul {
background: blue;
list-style-type: none;
margin: 0;
max-width: 500px;
padding: 5px;
position: absolute;
top: 100%;
}
li {
background: yellow;
margin: 5px;
padding: 5px;
}
a {
background: red;
color: white;
display: inline-block;
margin: 5px;
width: 30px;
}
<div>
<ul>
<li>
<a>A</a>
<a>B</a>
</li>
<li>
<a>A</a>
<a>B</a>
<a>C</a>
<a>D</a>
<a>E</a>
<a>F</a>
<a>G</a>
<a>H</a>
<a>I</a>
</li>
</ul>
</div>
You can move the relative positioning up to a parent element that is wide enough to handle the 500px:
div.wide-load {
width: 500px;
position: relative;
}
div.direct-parent {
background: aquamarine;
height: 50px;
width: 200px;
}
ul {
background: blue;
list-style-type: none;
margin: 0;
max-width: 500px;
padding: 5px;
position: absolute;
top: 100%;
}
li {
background: yellow;
margin: 5px;
padding: 5px;
}
a {
background: red;
color: white;
display: inline-block;
margin: 5px;
width: 30px;
}
<div class="wide-load">
<div class="direct-parent">
<ul>
<li>
<a>A</a>
<a>B</a>
</li>
<li>
<a>A</a>
<a>B</a>
<a>C</a>
<a>D</a>
<a>E</a>
<a>F</a>
<a>G</a>
<a>H</a>
<a>I</a>
</li>
</ul>
</div>
</div>
If you are unable to do this, then your only other option is to make the li's white-space:nowrap - but this will not respect your max-width:
div.direct-parent {
background: aquamarine;
position: relative;
height: 50px;
width: 200px;
}
ul {
background: blue;
list-style-type: none;
margin: 0;
max-width: 500px;
padding: 5px;
position: absolute;
top: 100%;
}
li {
background: yellow;
margin: 5px;
padding: 5px;
white-space: nowrap;
}
a {
background: red;
color: white;
display: inline-block;
margin: 5px;
width: 30px;
}
<div class="direct-parent">
<ul>
<li>
<a>A</a>
<a>B</a>
</li>
<li>
<a>A</a>
<a>B</a>
<a>C</a>
<a>D</a>
<a>E</a>
<a>F</a>
<a>G</a>
<a>H</a>
<a>I</a>
</li>
</ul>
</div>
li {
background: yellow none repeat scroll 0 0;
display: flex;
margin: 5px;
padding: 5px;
}
You can use
display: flex;
It's the principle of max-width. You force an item to never be larger than the specified unit, but this doesn't automatically mean that your element will ever reach that length. In this case, your absolutely positioned <ul> is still restricted to it's initial parent, which forces it to be a maximum of 200px long.
FWIW: An additional option not mentioned yet:
Use intrinsic sizing for the width:
ul {
...
width: max-content;
max-width: 500px;
...
}
From the spec:
max-content If specified for the inline axis, use the max-content inline size; otherwise compute to auto.
NB: you can use max-width: 500px in conjunction with the width property to ensure that the content width never exceeds 500px (max-width will override width).
div {
background: aquamarine;
height: 50px;
position: relative;
width: 200px;
}
ul {
background: blue;
list-style-type: none;
margin: 0;
width: -moz-max-content;
width: -webkit-max-content;
width: max-content;
max-width: 500px;
padding: 5px;
position: absolute;
top: 100%;
}
li {
background: yellow;
margin: 5px;
padding: 5px;
}
a {
background: red;
color: white;
display: inline-block;
margin: 5px;
width: 30px;
}
<div>
<ul>
<li>
<a>A</a>
<a>B</a>
</li>
<li>
<a>A</a>
<a>B</a>
<a>C</a>
<a>D</a>
<a>E</a>
<a>F</a>
<a>G</a>
<a>H</a>
<a>I</a>
<a>A</a>
<a>B</a>
<a>C</a>
<a>D</a>
<a>E</a>
<a>F</a>
<a>G</a>
<a>H</a>
<a>I</a>
</li>
</ul>
</div>
Browser Support is also pretty good (Except for IE/Edge)
You want something like this? I changed the width: 200px of div to max-width:500px and removed the max-width:500px in ul
div {
background: aquamarine;
height: 50px;
position: relative;
max-width: 500px;
}
ul {
background: blue;
list-style-type: none;
margin: 0;
padding: 5px;
position: absolute;
top: 100%;
}
li {
background: yellow;
margin: 5px;
padding: 5px;
}
a {
background: red;
color: white;
display: inline-block;
margin: 5px;
width: 30px;
}
<div>
<ul>
<li>
<a>A</a>
<a>B</a>
</li>
<li>
<a>A</a>
<a>B</a>
<a>C</a>
<a>D</a>
<a>E</a>
<a>F</a>
<a>G</a>
<a>H</a>
<a>I</a>
<a>E</a>
<a>F</a>
<a>G</a>
<a>H</a>
<a>I</a>
</li>
</ul>
</div>

pointer events with inline-block on ie11 windows 7

I have come across a very strange bug that only applies to ie11 on windows 7:
When applying pointer-events: none to a parent element, pointer-events:auto will not work on an element with that is display:inline-block
It may happen on windows 8 too but it seems to have fixed itself on Windows 10.
Below is an example snippet of what I mean, you can see the screen will turn light blue when ever it is hovered. I have removed the pointer events for everything and then turned it back on for the green box and the opaque white breadcrumb list.
You can see the green box turns has it's own pointer events (turning the background back to dark blue) where as the breadcrumb is totally ignored
html,
body {
position: relative;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#total {
position: absolute;
z-index: 1;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: blue;
display: block;
}
#total:hover {
background: lightblue
}
.no-pointer {
position: absolute;
left: 20px;
top: 150px;
width: 200px;
height: 200px;
border: 1px solid red;
z-index: 2;
pointer-events: none;
}
.pointer {
position: relative;
width: 100px;
height: 100px;
pointer-events: auto;
background: green;
display: block;
}
#breadcrumb {
position: absolute;
top: 20px;
left: 0;
right: 0;
max-width: 500px;
margin: auto;
width: 100%;
pointer-events: none;
z-index: 2;
}
.breadcrumb-list {
list-style: none;
background-color: #ffffff;
background-color: rgba(255, 255, 255, 0.5);
display: inline-block;
padding: 1em 50px;
pointer-events: auto;
}
.list-item {
display: inline-block;
}
<a id="total" href="#"></a>
<div class="no-pointer">
</div>
<div id="breadcrumb">
<ol class="breadcrumb-list">
<li class="list-item home-crumb">
<a class="crumb" href="#1">
<span>Home</span>
</a>
</li>
<li class="list-item">
<a class="crumb" href="#2">
<span>Test</span>
</a>
</li>
<li class="list-item">
<a class="crumb" href="#3">
<span>Test 2</span>
</a>
</li>
</ol>
</div>
Is there anyway to get this inline-block to work with ie11 on windows 7 (and maybe windows 8)?
ps I have used browser stack to test this and it worked properly on the setup I have described, so not sure if this is just localised to one laptop as I don't have any other windows 7 machines to test it on
Here is a fiddle I have messed around with
If you use the above fiddle and turn the inline-block element to a block element, you can see the pointer events work again
Luckily I have not used inline-block for centering the content but just so the element is able to be padded around the content.
This has meant that I can go back old school (in the days before inline-block) and float the element and make it block:
html,
body {
position: relative;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#total {
position: absolute;
z-index: 1;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: blue;
display: block;
}
#total:hover {
background: lightblue
}
.no-pointer {
position: absolute;
left: 20px;
top: 150px;
width: 200px;
height: 200px;
border: 1px solid red;
z-index: 2;
pointer-events: none;
}
.pointer {
position: relative;
width: 100px;
height: 100px;
pointer-events: auto;
background: green;
display: block;
}
#breadcrumb {
position: absolute;
top: 20px;
left: 0;
right: 0;
max-width: 500px;
margin: auto;
width: 100%;
pointer-events: none;
z-index: 2;
}
#breadcrumb:after {
content: '';
display: block;
height: 0;
overflow: hidden;
clear: both;
}
.breadcrumb-list {
list-style: none;
background-color: #ffffff;
background-color: rgba(255, 255, 255, 0.5);
display: block;
float: left;
padding: 1em 50px;
pointer-events: auto;
}
.list-item {
display: inline-block;
}
<a id="total" href="#"></a>
<div class="no-pointer">
</div>
<div id="breadcrumb">
<ol class="breadcrumb-list">
<li class="list-item home-crumb">
<a class="crumb" href="#1">
<span>Home</span>
</a>
</li>
<li class="list-item">
<a class="crumb" href="#2">
<span>Test</span>
</a>
</li>
<li class="list-item">
<a class="crumb" href="#3">
<span>Test 2</span>
</a>
</li>
</ol>
</div>
All I have to add to this is... stupid IE! Grrrrr!

Resources