I'm having an animated underline effect when user points the links on my website. The underline is a bit wider than the text itself, as there's a bit of horizontal padding.
Here's the effect I wanted to achieve and I did:
I was thinking if it was possible to simplify my code. After some trial and error, I used negative margin-left on the underline element and calc() to calculate its width as 100% + 2 * padding. It looks to me like an overcomplicated solution. Can the same effect be achieved without calc() and, perhaps, without negative margin?
Of note, adding a wrapper element is not an option. It needs to be a plain <a> element.
:root {
--link-color: #f80;
--link-underline-padding: .5em;
}
a {
color: var(--link-color);
display: inline-block;
padding: 0 var(--link-underline-padding);
text-decoration: none;
}
a:after {
background-color: var(--link-color);
content: '';
display: block;
height: .1em;
margin-left: calc(var(--link-underline-padding) * -1);
margin-top: .2em;
transition: width .5s;
width: 0;
}
a:hover:after {
width: calc(100% + var(--link-underline-padding) * 2);
}
I find dogs pretty cool.
A simple background animation can do this:
a {
background: linear-gradient(currentColor 0 0)
bottom left/
var(--underline-width, 0%) 0.1em
no-repeat;
color: #f80;
display: inline-block;
padding: 0 .5em 0.2em;
text-decoration: none;
transition: background-size 0.5s;
}
a:hover {
--underline-width: 100%;
}
I find dogs pretty cool.
Related:
How to animate underline from left to right?
How to hover underline start from center instead of left?
If you set a to position: relative; you can then use position: absolute; and left: 0px; to push it past the padding and then just use width: 100% to have it extend the entire length.
:root {
--link-color: #f80;
--link-underline-padding: .5em;
}
a {
position: relative;
color: var(--link-color);
display: inline-block;
padding: 0px var(--link-underline-padding);
text-decoration: none;
}
a:after {
position: absolute;
left: 0px;
background-color: var(--link-color);
content: '';
display: block;
height: .1em;
margin-top: .2em;
transition: width .5s;
width: 0;
}
a:hover:after {
width: 100%;
}
I find dogs pretty cool.
I'm trying to make a odd-shaped dialogue box shaped liked one in the link below (without the wiggly tail). I've thought about doing it through polyfill, but that ended up making the element too large. I thought I could maybe do it with pseudo elements, but I didn't think I could put a shadow behind effect and have the odd-shaped top/bottom with just two pseudo elements.
The best method I could think of was to nest multiple divs inside the main one and position them with absolute and set the top manually, but I was wondering if there was an easier way of doing so.
If I want to make a div like this and write things inside without them being cut off, how should I go about doing it?
A single element with pseudo classes absolutely positioned using perspective and transforms can look like that.
div {
position: relative;
display: inline-block;
color: white;
margin: 3em;
perspective: 250px;
}
div::before, div::after {
content: '';
position: absolute;
z-index: -1;
top: -1em;
left: -1.5em;
right: -2em;
bottom: -1em;
background: black;
padding: 2em 3em .5em .5em;
transform: rotateX(180deg) rotateY(15deg) rotate(1.5deg) skewX(25deg);
}
div::before {
background: white;
top: -1.5em;
left: -2.25em;
right: -2.75em;
bottom: -1.75em;
transform: rotateX(180deg) rotateY(15deg) skewX(35deg);
}
body {
background: red;
}
<div>i know kung foo</div>
I am working on a layout for a webshop and am experiencing a problem which seems to be very specific.
There is a dropdown navigation which is design to look kind of a tab with a box under it. The point is, that there is a 1px border line between the tab (first level Menu Item) and the box (second level items) which I can't hide.
I thought about giving the second level box a lower z-index than the first level element, but that didn't changed anything. I read a lot about z-index, how it works and how it NOT works, but nothing was about z-index within one list.
This is how it should looks like and how it really looks like: http://i.stack.imgur.com/xbQ6x.png
I created a codepen, which shows the problem very good, when hovering the first level items: http://codepen.io/anon/pen/bNqJxN
li .dropdown{
overflow: hidden;
position: relative;
display: inline;
visibility: hidden;
position: absolute;
padding:0;
margin: 0 0 0 -1px; /*Putting a negativ margin-top here puts the box OVER the parent element :-( */
background: #fff;
border: 1px solid $light-grey;
width: 280px;
height: 200px;
&.right {
right: -1px;
left: auto;
}
.dropdown-1-2 {
float: left;
width: 50%;
}
}
I usually solve this issue with z-index to have the bottom of the li to overlap the top of the dropdown.
In your case, I had to remove the * selector for the z-index which came after the li and dropdown which was resetting the z-index to 2 on everything in that navigation. Instead I created just the one stacking context (here's an article on it) for the first nav to appear above the second, and then I gave the ul position relative and the dropdown a z-index of -1 and -1px top margin to move it just behind the unpositioned li.
#mainnav {
...
ul {
#include reduced-list;
...
position: relative;
li .dropdown{
...
margin: -1px 0 0 -1px;
z-index: -1;
...
&#nav1 {
z-index: 2;
}
&#nav2 {
z-index: 1;
}
Sorry, the codepen didn't save.
You can solve it adding a pseudo element to cover the border
li:hover:after {
content: "";
position: absolute;
width: 100%;
height: 2px;
background-color: white;
bottom: -1px;
left: 0px;
z-index: 999;
}
codepen
Thanks so much!
Both answers solved my problem like a charme!
I created a codepen with the solution here: http://codepen.io/anon/pen/NPpQOq
ul {
#include reduced-list;
position: relative; /* YEAH */
float:right;
li .dropdown{
overflow: hidden;
position: relative;
display: inline;
visibility: hidden;
position: absolute;
padding:0;
margin: -1px 0 0 -1px; /* YEAH */
z-index: -1; /* YEAH */
background: #fff;
border: 1px solid $light-grey;
width: 280px;
height: 200px;
&.right {
right: -1px;
left: auto;
}
}
Placing a nested child under a parent element seems to be possible :-)
First time really using the pseudo :after selector. Not sure if the problem I'm running into is a limitation of it or I'm just missing something obvious.
Here's my live code.
li.current:after {
border-width: 1px 1px 0 0;
content: ' ';
background: #256f9e;
display: block;
height: 13px;
position: absolute;
width: 10px;
top: 6;
margin:0px auto;
z-index: 99;
transform: rotate(-224deg);
-webkit-transform: rotate(-224deg);
-moz-transform: rotate(-224deg);
-ms-transform: rotate(-224deg);
-o-transform: rotate(-224deg);
transform-origin: 50% 50%;
-webkit-transform-origin: 50% 50%;
-moz-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
-o-transform-origin: 50% 50%;
text-align: center;
float: center;
}
I've created a little triangle (Or rather a box that has been rotated to look like a triangle). I want it centered within the <li></li> but can't figure it out using my normal methods.
The things that have failed (in no particular order):
text-align: center;
float: center;
margin: 0 auto;
margin-right: 0;
margin-left: 0;
What am I missing? I doubt it matters, but I'm using AngularJS. Thought I'd mention it in case there is a known conflict between Angular & Pseudo selectors (which I doubt).
Thanks.
The issue is your use of absolute positioning & the method you're using to try and center it. If you position an element absolutely, the ol' margin: 0 auto; method won't work to center the thing. I point you to an explanation as to why this is at the end of the question, but in case you just want this to work let's get to the solution first.
Here's some working code. View it on JSFiddle
#tool-menu li {
...
position: relative;
}
li.current:after {
...
position: absolute;
width: 10px;
top: 6;
left: 50%;
margin-left: -5px;
}
Let's break down what's going on here.
Setting up a new Containing Block
In your original Fiddle, the pseudoelement is positioned absolutely relative to the viewport. An example might be the best way to show what this means, and why we don't want this. Consider setting it to top: 0. This would keep it latched to the top of the browser window (or, in this case, the JSFiddle frame), rather than the parent (the li). So, if our menu happened to be at the bottom of the page, or even moving around, the pseudoelement would be floating independent from it, stuck to the top of the page.
This is the default behavior of an absolutely positioned element when you don't explicitly set the position on any parent elements. What we want is to have its position defined relative to the parent. If we do this then the pseudoelement sticks with the parent, no matter where it happens to be.
To make this happen, you need to set the parent, #tool-menu li, to be explicitly positioned (which means setting it to be anything other than position: static). If you choose to use position: relative;, it won't change the computed location of the parent on the page, and does the thing we want. So that's why I used that one.
In technical terms, what we're doing here is creating a new containing block for the child.
Positioning the Pseudoelement
Now that our absolute positioning will be determined in relation to the parent, we can take advantage of the fact that we can use percentages to define where to place the child. In this case, you want it centered, so I set it be left: 50%.
If you do just this, though, you'll see that this lines up the left edge of the pseudoelement at 50%. This isn't what we want – we want the center of the pseudoelement to be at the middle. And that's why I added the negative margin-left. This scoots it over a bit to line the middle up with the center of the parent.
And once we do that, it's centered! Brilliance!
Why didn't my margin: auto; work?
The auto value of a margin is calculated from a fairly complex algorithm. At times, it computes to 0. I know from experience that this is one such instance of that happening, though I haven't yet traced my way through the algorithm to see exactly why. If you'd like to run through it, take a look at the spec most browsers have most likely implemented.
Using calc to center
You can also use the calc function in css to center the pseudo element.
Note: This isn't supported in IE8 and below (caniuse) but you can provide a fallback for older browsers.
View it on this code pen. I'm also using MarkP's css border method to draw the triangle.
li.current:after {
content: '';
display: block;
height: 0;
position: absolute;
width: 0;
overflow: hidden;
bottom: -5px;
left: calc(50% - 5px);
z-index: 2;
border-top: 5px #256f9e solid;
border-left: 5px transparent solid;
border-right: 5px transparent solid;
}
Wouldn't be better to just define the width as a percentage, make it a block element and text-align it in the center?
"float: center;" is invalid, and won't work. Mixing floated and absolute positioned elements are a sure way to get trouble with your layout as they don't really work that well togheter.
Try something like this:
li.current:after {
content: 'YOUR CONTENT';
display: block;
width: 100%;
text-align: center; }
Using margin:auto to center
As long as the element has a width declared you can use the absolute centering method.
To use this method the right and left properties must be set to 0 for margin: auto to be effective.
This method can be expanded to implement horizontal centering as well.
see link for full info:
http://www.smashingmagazine.com/2013/08/09/absolute-horizontal-vertical-centering-css/
li.current:after {
content: "";
position: absolute;
display: block;
right: 0;
bottom: -5px;
left: 0;
margin: auto;
border-width: 5px;
border-style: solid;
border-color: transparent;
border-top-color: #256f9e;
width: 200px;
height: 200px;
}
ul {
list-style-type: none;
padding: 0;
margin: 0;
}
<div>
<ul>
<li class="current"></li>
</ul>
</div>
Not directly related (have already voted up jmeas) but you may also find it easier to use the CSS border trick to make the triangle. e.g.
li.current:after {
content: '';
display: block;
height: 0;
position: absolute;
width: 0;
overflow:hidden;
bottom: 0;
left: 50%;
margin: 0 0 -5px -5px;
z-index: 99;
border-top: 5px #256f9e solid;
border-left: 5px transparent solid;
border-right: 5px transparent solid;
}
Similar tactics as to what jmeas has suggested with regards to the vertical positioning. We align to the bottom and then use a negative margin-bottom to push this out to the desired location.
With transform: translate() centering can be accomplished without a fixed size. This is because translate(<x>%) will use the (psuedo-)element's own size, while left and margin-left will use the container's size. By using these together we can therefore find the exact center-point.
tl;dr
To center vertically:
.container {
position: relative;
}
.container:after {
top: 50%;
transform: translateY(-50%);
}
To center horizontally:
.container {
position: relative;
}
.container:after {
left: 50%;
transform: translateX(-50%);
}
Full example
.container {
position: relative;
display: inline-block;
background: #4aa;
color: white;
padding: .5ex 1ex;
}
.container:after {
content: ":after";
position: absolute;
background: #a4a;
color: white;
padding: .5ex 1ex;
/* position below container */
top: 100%;
/* move right by 50% of containers width */
left: 50%;
/* move left by 50% of own width */
transform: translateX(-50%);
}
<p class="container">
Container with content
</p>
This may be the simplest way to do it:
.child_class::after{
position: absolute;
content: 'YourContentHere';
width: 100%;
text-align: center;
top: 50%;
}
the simplest way to do this -
With pesudo element :after or :before use display: inline-block;
Try something like this:
content: url(../images/no-result.png);
display: inline-block;
width: 100%;
text-align: center;
Using display: grid to center ::before
...worked nice for me. I'm using fa-Icons on this page and centered them within an element with 50% border-radius:
i {
font-size: 0.9rem;
border: 1px solid white;
border-radius: 50%;
width: 30px;
aspect-ratio: 1;
margin-inline: 5px;
}
i::before {
width: 100%;
height: 100%;
display: grid;
place-items: center;
}
This tutorial can help you to make center CSS pseudo-elements.
https://techidem.com/centering-pseudo-before-after-elements-content/
h2 {
text-align: center;
color: #181818;
padding-bottom: 20px;
margin-bottom: 35px;
border-bottom: 1px solid #eaeaea;
position: relative;
}
h2::after {
content: "";
width: 70px;
height: 4px;
background-color: #ff0000;
left: calc( 100% - ( 50% + 35px ) );
position: absolute;
display: block;
bottom: 0;
}
<h2>Most Recent Posts</h2>
You may use container queries with grid layout
element {
container-type: size;
&::after {
content: '';
position: absolute;
width: 100cqw;
height: 100cqh;
display: grid;
place-items: center;
}
}
Container queries: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries
I personally don't really like the idea to change position attribute or do some margin manipulation. I think the easiest way is two lines of CSS:
.element::after {
/* your css */
line-height: initial;
vertical-align: initial;
}
And there is no need to touch the parent.
I am having some problems with my DIV, it wont display over a a DIV that has a web user control in it. Below you can find my css. I believe I have done everything right and am hoping that someone can maybe see an error that I have made and help me out. If you need any other code let me know. I also wonder if its just IE rendering it wrong? Thanks for looking.
The Popup CSS:
{
background: #ececec;
position:absolute;
top: 236px;
left: 201px;
height: auto;
width: 280px;
border: solid 1px gray;
z-index: 50;
text-align:left;
padding-left: 5px;
padding-top: 5px;
padding-bottom: 15px;
font-size: 8pt;
}
The Activity DIV (same the div above just changed position)
{
border: solid 2px #A9C5EB;
position: absolute;
top: 353px;
left: 290px;
width: 710px;
height: 227px;
font-size: small;
overflow: scroll;
overflow-x: hidden;
background-color: #F8FBFE;
z-index: 2;
}
To know the HTML is essential to fix your problem.
What is the html that contains your popup? Is it relative to the body tag or some other element? Is the containing element position: relative;?
Try setting the containing element's z-index and position:
#my-container {
position: relative;
z-index: 1;
}
See this SO post about absolute positioning.
On a side note, check out IE-7.js which fixes many IE browser issues, including - AFAIK - this bug.