Center a Pseudo Element

CssPseudo Element

Css Problem Overview


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.

Css Solutions


Solution 1 - Css

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.

Solution 2 - Css

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;
}

Solution 3 - Css

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; }

Solution 4 - Css

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>

Solution 5 - Css

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.

Solution 6 - Css

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>

Solution 7 - Css

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;

Solution 8 - Css

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 -

Solution 9 - Css

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;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEnigmaRMView Question on Stackoverflow
Solution 1 - CssjamespleaseView Answer on Stackoverflow
Solution 2 - CssMark ReillyView Answer on Stackoverflow
Solution 3 - CssHexdomView Answer on Stackoverflow
Solution 4 - Cssuser1095118View Answer on Stackoverflow
Solution 5 - CssMarkPView Answer on Stackoverflow
Solution 6 - CssglennslView Answer on Stackoverflow
Solution 7 - Cssocram88View Answer on Stackoverflow
Solution 8 - Cssreckart designsView Answer on Stackoverflow
Solution 9 - CssmseiwaldView Answer on Stackoverflow