Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Positioning content in :after pseudo-element absolutely relative to an inline elements top edge

I'm looking for a CSS-only way to position content in an :after pseudo element absolutely relative to another - possibly multiline - element's top edge. The result should resemble this:

Result demonstration

Usually there's no rocket science involved to archieve this, but I'd like to find a solution that also meets the following criteria:

  • Do not use the :before pseudoelement (this is already needed to achieve some other independent thing),
  • do not use display: block/inline-block; on the inline element (because it creates ugly holes in the text flow),
  • allow to define the width of the elements as a percentage of the containing element (at least after applying the rule of three),
  • do not hardcode the top position/margin/padding of the :after-pseudo-element in any way that makes it dependent of the parent element's position inside the grandparent element, the parent element's height or the actual element's height (or to make it short: don't make anything dependent of the content),
  • do not insert additional HTML elements, and
  • do not add JS.

I created a Codepen that hopefully makes it easier to grasp what I'm going for here.

I know that it is easily possible to get to the result by violating one of the restrictions listed above (as demonstrated in the Codepen), but I'm looking for an elegant solution to this problem where this is not needed.

After playing around with this for quite a while I'd say that it is not possible, but it would be great if someone could convince me of the contrary - or at least formally proves that it actually is impossible. Thanks a lot for any help in advance.

like image 788
bfncs Avatar asked Oct 14 '14 12:10

bfncs


Video Answer


1 Answers

Well, the only possible solution with the wanted method could be this.

- First to mention, for this to work you'd need to define ::after content directly in CSS, not through data- attribute. (I don't know why, but seems like a parsing problem.)

- Set position:relative to the .wrapper. as you're going to move ::after left-right based on that parent (not direct one).

- Set width: 100% to ::after (100% of the parent)

- Set white-space: pre to ::after - this is important

- Write the content directly with using \A (escape character) to break the word in particular places.

- Set top: 0 and give it a negative right position (how far you want)

Note: You might need to set word-wrap: break-word on certain elemenets if the additional text is overflowing out of the wrapper.

Basically, this is the addition/modification for your CSS:

.wrapper {
  position: relative;
}

.accent::before{
    position: absolute;
    width: 100%;
    content: "Unfortunately \A it is aligned with \A the last line of said span.";
    white-space: pre;
    right: -70%;
}

.content p {
    word-wrap: break-word; /* in my example <p> was problematic */
}

Note: I don't know how good this method is for responsive designs, never tried it that way.

Tested on FF and Chrome.
Live example

EDIT:
Hm, yeah, I started with ::after then by accident switched to ::before =)

Alright, minor modifications to the CSS:

- Instead of width: 100% set width: auto

- Instead of top: 0 you need to manually (sorry) reduce margin-top: -(number)px - don't use top as it's gonna set top position based on the wrapper itself.

- Switch right: -(number)% to right: -(number)px

CSS modification:

.accent::after{
    width: auto;
    right: -50px;
    margin-top: -40px;
    /* and remove "top" property completely */
}

Live example

ANOTHER EDIT
Cleaner working solution where you can use content: attr(data-meta) (removed white-space: pre and set width to wanted percentage size:

.accent::after{
    position: absolute;
    width: 30%;
    content: attr(data-meta);
    right: -50px;
    margin-top: -40px;
}

Live example

like image 119
Davion Avatar answered Nov 16 '22 04:11

Davion