Search code examples
htmlcsspseudo-elementfixed

Fixed placement of element, but considering pseudo before element


I have an annoying issue with the html layout of a form. I cannot really change the general setup, since it is part of a huge framework. But I have to "move" a button to a more suitable location. I am close, but not happy with the solution so far. Maybe you can give me some idea in this. Here is a dramatically simplified version to demonstrate my approach:

I have two container divs, top and bottom. The top container shows a button on the left side. That button is fixed, but can have a different width due to the translation of its label. The bottom container holds lots of stuff. Amongst that a second button at its top which works fine, but looks wrong. I want to optically move it into the top container, since there is a logical connection to the button in there. Sure, really placing it in there would be the correct solution, but I currently cannot do that. Instead I use a fixed position which works fine, except for the horizontal placement. I have to decide how far pushed from the left to place the button, so that it certainly does not overlap the first button in the container. I obviously have to consider all translations, the result works, but depending on the first buttons label I have an annoying horizontal gap between the two buttons.

I tried to use a pseudo element (::before) on the second button to help with the layout. Since when rendering the view I obviously have the translated label of the first button I can copy that into some property of the second button and use that property in my css to fill a before pseudo element of the second button which has exactly the same length as the first button. That is what is shown in the code example posted below.

What I completely fail to do is to place that pseudo element such that is it left in the top container (so exactly below the first button). The idea is to indirectly place the second button that way. Looks like this is not possible, obviously. But since I am a bloody beginner in markup and styling I thought it might be worth asking here...


Below is some drastically stripped down code to demonstrate my approach.

I create a jsfiddle for you to play around with. Here is the code:

HTML:

<div id="top-container">
    <button>multilingual button text</button>
</div>

<div id="bottom-container">
    <h2>
        Some title opening the bottom container
        <span class="into-top-container">
            <button id="place-me" reference-text="multilingual button text">button to be placed</button>
        </span>
    </h2>
    <p>Some content</p>
    <p>Some content</p>
    <p>Some content</p>
</div>

CSS:

body {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
}
div {
    margin: 0;
    padding: 5px;
}
button {
    margin: 0;
    padding: 5px;
    white-space: nowrap;
}


div#top-container {
    width: 100%;
    border: 1px solid green;
}

div#bottom-container {
    width: 100%;
    border: 1px solid blue;
}

#place-me {
    position: fixed;
    top: 0;
    left: 400px;
    margin: 5px;
    background: yellow;
}

#place-me::before {
    z-index: 0;
  /*visibility: hidden;*/
    position: absolute;
    content: attr(reference-text);
    margin: 0 5px;
    padding: 0;
    background: gold;
    right: 100%;
}

Notes:

  • that in the above code the second button is placed with left: 400px;. That is more or less what I want to change. But obviously left: 0 is not correct...

  • the visibility css rule for the pseudo element is currently commented out for demonstration purpose

  • keep in mind that the second button is *not* contained inside the top container, but actually logically below the title of the bottom container. The goal is to move it optically up into the top container which already is where close to what I want. Except for the horizontal alignment...


Upon request here is a screenshot:

enter image description here

It is taken from the fiddle I posted above. I added the red ellipse which shows what element pair I want to move and the left pointing arrow indicating where I want to move that too. I want to move it exactly that far, that the two tests "multilingual button text" are exactly placed on top of each other, but without specifying an explicit left placement obviously. That is why the pseudo element exists: as a dummy placeholder. I would then hide that pseudo element and have the second button placed exactly right of the first button, regardless of how long the translated text in there is.

So the final result should like like that:

enter image description here


Solution

  • OK, I invested some more time, since this issue popped up again after a regression in our code and I found, as often after allowing some time to pass, a logical and relatively clean solution:

    I use the same stripped down code to for demonstration purposes.

    The jsfiddle is based on the one provided in the question itself.

    HTML: no real change, except for the reference-text having moved from button to container, for the why see below:

    CSS:

    * {
        font-size: 12px;
        font-weight: normal;
        font-family: Arial;
    }
    
    body {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        font-size: 12px;
        font-weight: normal;
    }
    span,
    div {
        margin: 0;
        padding: 5px;
    }
    button {
        margin: 0;
        padding: 5px;
        white-space: nowrap;
    }
    
    
    div#top-container {
        width: 100%;
        border: 1px solid green;
    }
    
    div#bottom-container {
        width: 100%;
        border: 1px solid blue;
    }
    
    span.into-top-container {
        position: fixed;
        top: 0;
        left: 0;
        pointer-events: none;
        border: 1px solid transparent;
    }
    
    span.into-top-container::before {
        visibility: hidden;
        content: attr(reference-text);
        position: relative;
        margin-right: 5px;
        padding: 5px;
        border: 2px solid;
        background: gold;
    }
    
    #place-me {
        background: yellow;
        pointer-events: all;
    }
    

    The basic change in strategy: it is the container holding the button to be placed that has to be positioned in a fixed manner, not that button itself (so the <span class="into-top-container">)! That allows to use the pseudo before element, now also anchored to that container, not the button, to take the space as required without actually getting part of the button itself.

    Since that container is now place over the original multilingual button that one is not clickable any more. That issue is fixed by a css pointer-events set to none for the container and set to all for the placed button again. That makes the container itself simply ignore all events (clicks) and have them passed to the original button beneath.

    I had to make sure that the font used inside the pseudo element is style exactly like the original multilingual button. That actually makes sense, since the font styling defines the actual width used by that button, so the actual width used by the pseudo element should be defined in exactly the same manner. In the example above I forced that by simply setting all elements font style rules to some fixed values (the initial * {...} in the CSS code). That can obviously also be done right inside the css rules for the pseudo element itself. I chose the more simple and brute variant here to keep the code clean.