Align list items by their bounding rect - html

I have a list that uses display: inline-flex. The font-sizes of different items in the list are different. I discover that the list items are aligned according to the position of the bottom of the text. This makes the items with the smaller text sizes to appear lower than the others.
However, I want to align the items by the base of their bounding rects.
I have have found a hack to resolve this: I place a span containing a dot either side of the text I want to show, I set the font-size of this dot to size of the largest font-size in the list, and I set the visibility of these spans to hidden.
Is there a non-hacky, pure CSS way to achieve this?
JSFiddle
body {
margin: 0;
}
ul {
padding: 0;
margin: 0;
}
li {
display: inline-flex;
border: 1px solid #000;
justify-content: center;
align-items: center;
width: 64px;
height: 64px;
font-size: 64px;
line-height: 64px;
}
li:last-child {
font-size: 16px;
}
span {
font-size: 64px;
visibility: hidden;
}
hr {
position: absolute;
top: 47px;
width: 204px;
}
<ul>
<li>1</li>
<li>2</li>
<li>THREE</li>
</ul>
<hr>
<ul>
<li>1</li>
<li>2</li>
<li><span>.</span>THREE<span>.</span></li>
</ul>

You can achieve the alignment you want by adding display: flex to the container, in this case the ul element.
fiddle
body {
margin: 0;
}
ul {
padding: 0;
margin: 0;
display: flex;
}
li {
display: inline-flex;
border: 1px solid #000;
justify-content: center;
align-items: center;
width: 64px;
height: 64px;
font-size: 64px;
line-height: 64px;
}
li:last-child {
font-size: 16px;
}
<ul>
<li>1</li>
<li>2</li>
<li>THREE</li>
</ul>

Related

CSS border bottom only on one side

I am trying to use the css border-bottom property with a circle in between. Something like this :
what I want
But, for the first and last circles I only want it to line to be inclusive within the borders but its extending to the ends like this.
result of what I tried with normal css
This is the css I used:
.horizontalLineComplete{
width: 100%;
border-bottom: 4px solid #26890D;
height:20px;
}
.horizontalLineCurrent{
width: 70%;
border-bottom: 4px solid #63666A;
height:20px;
}
I tried using the li:: before and ::after selector classes as well but that also hasn't worked it just shows up the lines between the circles but the colors I assign aren't working accurately. It takes black color by default like this: result for what I tried with selector classes
This is the css I gave:
li.circleComplete::before
{
content: "";
flex: 1 1;
border-bottom: 2px solid #26890D;
margin: auto;
}
li.circleComplete::after {
content: "";
flex: 1 1;
border-bottom: 2px solid #26890D;
margin: auto;
}
li.circleNext::before
{
content: "";
flex: 1 1;
border-bottom: 2px solid #63666A;
margin: auto;
}
li.circleNext::after {
content: "";
flex: 1 1;
border-bottom: 2px solid #63666A;
margin: auto;
}
Can someone help me out on how I can adjust this or let me know if I am making any mistakes in the code? I am using react and typescript for my front end with scss.
This is one of solution how to fix your code.
$('.active').html("&#10003");
$('#goNext').on('click', function() {
$('ul>li.active').removeClass('active').next('li').addClass('active');
$("ul>li.active").html("&#10003")
});
li {
width: 2em;
height: 2em;
text-align: center;
line-height: 2em;
border-radius: 1em;
background: #45ad66;
margin: 0 1em;
display: inline-block;
color: white;
position: relative;
}
li::before{
content: '';
position: absolute;
top: .9em;
left: -4em;
width: 4em;
height: .2em;
background: #45ad66;
z-index: -1;
transition: all 1s;
}
li:first-child::before {
display: none;
}
.active {
background: #3f995b;
transition: all 1s;
}
.active ~ li {
background: gray;
}
.active ~ li::before {
background: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li class="active">1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
</ul>
<button id="goNext">
Go to next
</button>
UPDATED code
I find it easier to put the central line as a background image (via linear-gradient) on the ul element itself.
This snippet sets the ul to display inline-flex and gets the circles (the li elements) spaced out evenly with the first at the left side and the last at the right side by using the space-between justification property.
This way you don't have to do lots of positioning.
The tick is put on via a content in a pseudo element for each li as I assume it is just for decoration rather than as actual content.
ul {
margin: 0;
padding: 0;
list-style-type: none;
display: inline-flex;
width: 100%;
background-image: linear-gradient(#45ad66, #45ad66);
background-size: 100% 2px;
background-position: center center;
background-repeat: no-repeat;
justify-content: space-between;
}
li::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
content: '✔';
color: white;
font-size: 3vmin;
}
li {
display: inline-block;
position: relative;
background-color: #45ad66;
width: 6vmin;
height: 6vmin;
border-radius: 50%;
transition: all 1s;
}
li.active,
li:hover {
background-color: #3f995b;
}
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
Note: dimensions are in terms of vmin so that the whole thing is responsive but of course you are free to change those if required.
Just so you can see the 'active' effect transitioning I've put the active color as the color for a hover on an li element as well. Also moved the transition to the element itself so the color transitions both in and out.

Trying to use CSS to display elements in a row pattern next to each other

I am trying to create a movie app and I am getting stuck with styling some elements. I am new to CSS and styling. Here is my code. I am trying to display these element one after the other.
Here is my code
<ul class="actions">
<li><div class="vote_average">85</div></li>
<li><div class="favorites-heart"><i class="far fa-heart"></i></div></li>
<li><div class="list"><i class="fas fa-list"></i></div></li>
</ul>
.vote_average, .favorites-heart, .list {
font-weight: 300;
font-size: 18px;
/* font-weight: 700; */
line-height: 50px;
position: absolute;
width: 50px;
text-align: center;
}
.favorites-heart::after,
.list::after,
.vote_average::after {
border-radius: 100%;
border: 2px solid #C4AF3D;
/* #ee927b */
content: '';
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
.actions {
list-style-type: none;
padding-left: 0px;
display: flex;
align-items: center;
}
.actions li {
/* display: inline-flex; */
flex-direction: column;
padding-right: 40px;
}
here is what it currently looks like
Here is an example of what i want it to look like
Basically I am trying to get each element to display next to each other is a row pattern. I would like to have a background that contrasts the icon glyphs.
Here is a code which corresponds a little more to the example that you gave us in image.
* {
box-sizing: border-box;
}
ul {
position: relative;
padding: 0;
margin: 0;
display: flex;
width: max-content;
list-style: none;
heigth: 50px;
}
li {
display: flex;
color: #ffffff;
justify-content: center;
align-items: center;
margin: 10px;
min-width: 50px;
height: 50px;
border-radius: 100%;
background: #C4AF3D;
}
.vote_average {
background: #081C22;
font-weight: bold;
}
<ul class="actions">
<li class="vote_average"><div >85</div></li>
<li><div class="favorites-heart"><i class="far fa-heart"></i></div></li>
<li><div class="list"><i class="fas fa-list"></i></div></li>
</ul>
If you have any questions regarding this code, don't hesitate!
Flex-box to the rescue.
ul {
list-style: none;
margin: 0;
padding: 0; /* get rid of the defaults */
}
.actions {
display: flex; /* use flex! */
flex-direction: row;
align-items: center;
border: 1px solid red; /* just to see */
}
li {
border: 1px solid blue; /* just to see */
padding: 10px;
}
<ul class="actions">
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
(you could also make them 'inline-block' and use 'vertical-align: middle', but 'inline-block' elements have some quirks and there are uncontrollable spaces between those items / and I suggest you stick with flex-box! : )
more notes: https://jsfiddle.net/sheriffderek/npdL3vbr/ - on keeping the components separate from the layout

Dynamic horizontal list with variable width lines and content between nodes

I have a system that generates a list of nodes which may or may not have additional elements between the nodes. I want to visualize this list horizontally with lines connecting the nodes as shown in the example below.
The content between two nodes is variable and can even contain more complex elements like images and tables. The nodes themselves are labeled with only text (Point A, Point B, etc.).
Code & What I have tried so far
In my first attempt at trying to visualize the nodes I simply used a vertical list with the :before and :after CSS pseudo-elements to draw the lines between the nodes. I am however having difficulty translating this approach to a horizontal list.
Horizontal approach: (updated thanks to Mayank Gupta's answer below)
h1, h2 {
margin: 0;
padding: 0;
}
ul {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
flex-direction: row;
text-align: center;
list-style-type: none;
font-size: 1.5em;
}
ul li {
display: flex;
justify-content: flex-start;
flex: 2;
position: relative;
border-bottom: 2px solid #000;
padding-bottom: 10px;
}
ul li:after {
position: absolute;
bottom: -5px;
left: 0;
font-family: "Font Awesome 5 Free";
font-weight: 300;
content: "\f111";
height: 17px;
width: 23px;
background: #fff;
}
ul h2 {
margin: 0 0 0 -1em;
padding: 0;
}
ul li:last-of-type {
border: none;
}
ul li > div {
flex: 0 0 auto;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
<link href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" rel="stylesheet"/>
<ul>
<li>
<h2>Node A</h2>
<div>Content</div>
</li>
<li>
<h2>Node B</h2>
<div>Content</div>
</li>
<li>
<h2>Node C</h2>
</li>
</ul>
Problems with this method:
All nodes are equal width causing the last node to take up space needlessly (last node never has any content to show), in effect this prevents the list from using all available horizontal space (or at least seemingly);
The line is sensitive to font-size changes causing it to misalign;
The node labels are centered using a negative offset, this probably isn't the best method;
The content div is taken out of the document flow in order to center it between the nodes, this potentially allows it to overlap the node labels as is the case in this example
How could I resolve these issues?
Constraints
I only support the latest version of modern browsers, so no compatibility is required for ancient browsers;
I prefer an HTML(5) and CSS only approach
Here is my answer - essentially you add display:flex to the ul list and make sure that the nodes have fixed size by using flex:0 0 auto and a fixed width. This would allow for the context between to be variable in size.
Concerning the connection of the nodes, although it could be done by using the :before and :after pseudo-elements, there's an easier way to do it; just use a pseudo-element for the whole list (the ul element) that goes side-by-side along the width of the element (in the example actually left and right have a value of 30px to compensate the arbitrary side padding I set).
HTML:
<ul>
<li class="node"><label>Point A</label></li>
<li class="context"><span>Content here Content here Content here</span></li>
<li class="node"><label>Point B</label></li>
<li class="context"><span>Content here</span></li>
<li class="node"><label>Point C</label></li>
</ul>
CSS:
ul {
list-style-type: none;
display: flex;
width: 100%;
margin: 50px 0 0 0;
padding: 0 30px;
position: relative;
}
ul:before {
content: "";
display: block;
position: absolute;
left: 30px;
right: 30px;
top: 50%;
margin-top: -1px;
height: 2px;
background: steelblue;
}
.node {
flex: 0 0 auto;
display: block;
width: 12px;
height: 12px;
border-radius: 12px;
border: 2px solid steelblue;
position: relative;
background: #fff;
}
.node label {
position: absolute;
top: -30px;
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
}
.context {
flex: 1 1 auto;
}
.context span {
display: block;
margin-top: -15px;
text-align: center;
}
Here is a working fiddle:
https://jsfiddle.net/8ksxtz60/
Try this code
h1, h2 {
margin: 0;
padding: 0;
}
ul {
display: flex;
align-items: center;
}
ul li {
position: relative;
width: 33%;
display: flex;
justify-content: center;
align-items: flex-start;
flex-direction: column;
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
}
ul li:after {
position: absolute;
bottom: -10px;
left: 0;
font-family: "Font Awesome 5 Free";
font-weight: 300;
content: "\f111";
height: 17px;
width: 17px;
background: #fff;
}
<link href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" rel="stylesheet"/>
<ul>
<li>
<h2>Node A</h2>
<div>Content</div>
</li>
<li>
<h2>Node B</h2>
<div>Content</div>
</li>
<li>
<h2>Node C</h2>
<div>Content</div>
</li>
</ul>
This somewhat answers your question[*] [* = however I wrote most of this code before I read on seeing your first image, without seeing the h2/div content].
The bulk of the answer is HTML/CSS, but there is a small bit of javascript to set the li class [of the nodes up to the point selected] to active.
Run the snippet and click on the node items in turn to see the lines being drawn.
EDIT: Re. your questions:
With regard to the last node, you could choose to give it a different width, remove border-width/margin/padding or display:none. If you use the :last-child selector, you can give the last item different property values as required, using CSS.
Regarding different fonts/misalignment, I'd say the same is true of most code using different fonts. Try to stick use one font consistently (I'd regard it as better practice in this instance). As for misalignment, if it's varying image sizes that are mostly causing it to misalign, try using a fixed width with
object-fit (contain/scale-down should display best for you) You can set an overflow to divs, and don't forget wrap
Label-centering: have you tried simply adding text-align: center; ? Not sure what suggest regarding the negative offset, as this is dependent/relative to other css. Would have to see full css/html layout.
Re. potentially overlapping divs: set a max-width for your content and overflow/overflow-y to auto if necessary.
Hope this helps!
var nodelist1 = document.getElementById("connect");
var mynodes = nodelist1.getElementsByTagName("li");
for (var i = 0; i < mynodes.length; i++) {
mynodes[i].addEventListener("click", function() {
var currNode = document.getElementsByClassName("active");
currNode[0].className = currNode[0].className.replace("active", "");
this.className += "active";
});
}
body {
font-family: "Font Awesome 5 Free";
/* padding: 2em;*/
}
#connect {
font-weight: 300;
content: "\f111";
}
h1,
h2 {
margin: 0;
padding: 0;
}
li {
vertical-align: middle;
display: inline-block;
position: relative;
color: white;
width: 4em;
height: 4em;
text-align: center;
margin: 0em .8em;
line-height: 4em;
border-radius: 1em;
background: navy;
}
li::before {
content: '';
position: absolute;
top: 2em;
left: -3em;
min-width: 4em;
/*width of connecting line*/
height: .2em;
background: maroon;
z-index: -1;
}
li:first-child::before {
display: none;
/*at pos 1, other nodes are 'disabled' line is light blue*/
}
.active {
background: navy;
}
.active~li {
background: lightgray;
}
.active~li::before {
background: lightblue;
}
.content {
display: none;
width: 5em;
max-height: 6em;
overflow-y: scroll;
overflow-x: none;
line-height: 1em;
color: red;
padding: 0;
margin: 0;
}
.active .content {
display: block;
}
<link href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" rel="stylesheet" />
<ul id="connect">
<li>1</li>
<li class="active">2</li>
<li>
<h2>Node A</h2>
<div class="content">Manuel: hello how are you I speak English I learn it from a book</div>
</li>
<li>4</li>
<li>5</li>
</ul>

CSS Padding is overflowing parent

I'm trying to make a horizontal navbar, and I've got it sitting at the top of the page with each of the three list items filling up a third of the space, all good on that part.
The issue is when I try to make the anchor tag (which is within the list item) fill up all of the list item's space. I set it to display: block;, so it takes up the full width, however I can't get it to fill up vertical space how I want it to. I can use height: 100% and it will fill up the space, however if I do it that way the anchor tag's text rests at the top of the list item's space, however I want it to be vertically centred. Top and bottom padding came to mind for this, so I tried setting padding: 100%;. When this happened, I could no longer see the text of the anchor tag. By dragging the space with my mouse it looks like the tag is taking up more space than is in the list item.
This is an image of the nav bar with height: 100%;:
Note: the red lines surrounding each section are the borders which I set visible to view boundaries
The relevant HTML code is as follows:
<ul class="main_nav">
<li>Home</li>
<li>Test page</li>
<li>Test page 2</li>
</ul>
The relevant CSS code is as follows:
body * {
font-family: Roboto sans-serif;
box-sizing: border-box;
border: solid 2px red;
}
header ul.main_nav {
background-color: gray;
color: #EBEBEB;
margin: 0px;
padding: 0px;
width: 100%;
height: 50px;
list-style-type: none;
list-style-position: inside;
overflow: hidden;
}
header ul.main_nav li {
text-align: center;
float: left;
width: calc(100% / 3);
height: 100%;
}
header ul.main_nav li a {
display: block;
height: 100%;
text-decoration: none;
}
EDIT: I would rather not use fixed sizes to solve this, as I'd largely prefer being able to edit a single value (50px for the ul) to adjust the whole navbar
you can do this using flexbox,
*,
*:before,
*::after {
box-sizing: border-box;
}
body * {
font-family: Roboto sans-serif;
border: solid 2px red;
}
ul.main_nav {
background-color: gray;
color: #EBEBEB;
margin: 0;
padding: 0;
width: 100%;
height: 50px;
list-style-type: none;
list-style-position: inside;
display: flex
}
ul.main_nav li {
flex: 1;
display: flex;
height:100%;
}
ul.main_nav li a {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
background: yellow
}
<ul class="main_nav">
<li>Home
</li>
<li>Test page
</li>
<li>Test page 2
</li>
</ul>
if you are going to have one line only in your menu, yo can use line-height, see below
*,
*:before,
*::after {
box-sizing: border-box;
}
body * {
font-family: Roboto sans-serif;
border: solid 2px red;
}
ul.main_nav {
background-color: gray;
color: #EBEBEB;
margin: 0;
padding: 0;
width: 100%;
height: 50px;
list-style-type: none;
list-style-position: inside;
overflow: hidden;
font-size: 0;
}
ul.main_nav li {
text-align: center;
width: calc(100% / 3);
height: 100%;
display: inline-block;
font-size: 16px
}
ul.main_nav li a {
display: block;
height: inherit;
background: yellow;
text-decoration: none;
line-height: 32px /*this should be the same value has the parent height*/
}
<ul class="main_nav">
<li>Home
</li>
<li>Test page
</li>
<li>Test page 2
</li>
</ul>

inline-block list items mess up when one is empty

I wanted to put some image in first list item but it seems to mess up when it's no content. i tried various methods in jsfiddle (various options of display and position) but none of them works to align inline al "li" when first is empty. it seems to be problem with inline property. Do enyone had such hilarious problem, and have clear solution.
PS: ul li {display:block; float:left;} doesn't work
ul {
font-size: 0px;
display: inline-block;
height: 40px;
width: 100%;
background-color: black;
}
ul li {
font-size: 14px;
display: inline-block;
width: 50px;
background-color: red;
border: solid black 1px;
height: 38px;
margin: 0px;
padding: 0px;
text-align: center;
}
div {
heght: 1.5em;
margin: 0px;
padding: 0px;
}
<ul>
<li>
<!--no content list item, why it mess up align to top others-->
</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
you need to reset vertical-align propertie to vertical-align:top; (defaut is baseline and depends on content wich sets the line-height)
https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align
examples to play with
ul {
font-size: 0px;
display: inline-block;
height: 40px;
width: 100%;
background-color: black;
}
ul li {
font-size: 14px;
display: inline-block;
vertical-align:top;
width: 50px;
background-color: red;
border: solid black 1px;
height: 38px;
margin: 0px;
padding: 0px;
text-align: center;
}
div {
heght: 1.5em;
margin: 0px;
padding: 0px;
}
<ul>
<li>
<!--no content list item, why it mess up align to top others-->
</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
Add vertical-align: top; to your li elements.
overflow: hidden; is missing into "ul li { }"
Use this:
ul li {
font-size: 14px;
display: inline-block;
width: 50px;
background-color: red;
border: solid black 1px;
height: 38px;
margin: 0px;
padding: 0px;
text-align: center;
overflow: hidden;
}
ul li {display:block; float:left;}
works for me.
JSFiddle