krasi
krasi

Reputation: 38

How can I prevent the ::before pseudo element from affecting my flex layout?

I have a header on my website, which uses display: flex. Inside it, there are three elements, a heading, an image (nested inside a div so that I can make use of a certain type of CSS image filtering) and a vertical nav.

The heading and nav have flex: 0, whereas the image has flex: 1, so it gets bigger as my display gets wider, as intended.

The nav items have a ::before pseudo element that appears on hover. When it appears, it takes up space and so my image shrinks varying amounts when I hover over the nav items and it makes my page look glitchy. How can I make it so that the ::before pseudo element does not make the image shrink when it appears? Is this even possible? Or do I need to switch to using an actual element that gets made visible when I hover?

I have tried using absolute positioning on the ::before element but then when I try to move it using the left property, it disappears.

https://codepen.io/pen/?template=BamyEYW

header {
  margin-block-start: 3em;
  padding-bottom: 0.67rem;
  padding-left: 5%;
  padding-right: 5%;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

nav {
  margin-left: auto;
}

nav ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  padding-inline-start: 0;
  padding-left: 1em;
}

nav ul li {
  text-align: right;
  font-size: x-large;
}

nav ul li a:hover {
  color: red;
}

nav ul li.internal-nav.selected::before {
  content: '> ';
  color: rgba(0, 0, 0, 0.6);
}

nav ul li.internal-nav:hover::before {
  content: '> ';
  color: red;
}

h1 {
  color: rgba(0, 0, 0, 0.6);
  font-size: 5em;
}

.profile-photo3 {
  position: relative;
  flex: 1;
  min-width: 20rem;
  background-color: blue;
  clip-path: polygon(70% 0%, 100% 50%, 70% 100%, 0% 70%, 20% 15%);
}

.profile-photo3 img {
  width: 100%;
  mix-blend-mode: screen;
}
<header>
  <h1>Page Title</h1>
  <div class="profile-photo3"><img src="MyPhoto.jpeg" /></div>
  <nav>
    <ul>
      <li class="internal-nav"><a href="#about-me">About Me</a></li>
      <li class="internal-nav"><a href="#experience">Experience</a></li>
      <li class="internal-nav"><a href="#lab">Lab</a></li>
    </ul>
  </nav>
</header>

Upvotes: 1

Views: 2333

Answers (4)

Yaroslav Trach
Yaroslav Trach

Reputation: 2011

You can update your pseudo element, like this:

nav ul li.internal-nav.selected a:before,
nav ul li.internal-nav a:hover:before {
  content: '> ';
  position: absolute;
  left: -1rem;
  color: red;
}

nav ul li.internal-nav.selected a:before {
  opacity: .6;
}

Run code snippet and open it on fullpage to see changes. Also you can check it here (Codepen.io)

header {
  margin-block-start: 3em;
  padding-bottom: 0.67rem;
  padding-left: 5%;
  padding-right: 5%;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

nav {
  margin-left: auto;
}

nav ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  padding-inline-start: 0;
  padding-left: 1em;
}

nav ul li {
  text-align: right;
  font-size: x-large;
}

nav ul li a:hover {
  color: red;
}

nav ul li.internal-nav a {
  position: relative;
}

nav ul li.internal-nav.selected a:before,
nav ul li.internal-nav a:hover:before {
  content: '> ';
  position: absolute;
  left: -1rem;
  color: red;
}

nav ul li.internal-nav.selected a:before {
  opacity: .6;
}

h1 {
  color: rgba(0, 0, 0, 0.6);
  font-size: 5em;
}

.profile-photo3 {
  position: relative;
  flex: 1;
  min-width: 20rem;
  background-color: blue;
  clip-path: polygon(70% 0%, 100% 50%, 70% 100%, 0% 70%, 20% 15%);
}

.profile-photo3 img {
  width: 100%;
  mix-blend-mode: screen;
}
<head>
  <script src="https://cpwebassets.codepen.io/assets/editor/iframe/iframeConsoleRunner-d0f3648046d2aaca07bd0037b9e061a26c74a8a999b75672ad6a638cca641472.js"></script>
  <script src="https://cpwebassets.codepen.io/assets/editor/iframe/iframeRefreshCSS-4793b73c6332f7f14a9b6bba5d5e62748e9d1bd0b5c52d7af6376f3d1c625d7e.js"></script>
  <script src="https://cpwebassets.codepen.io/assets/editor/iframe/iframeRuntimeErrors-4f205f2c14e769b448bcf477de2938c681660d5038bc464e3700256713ebe261.js"></script>
</head>

<body>
  <header>
    <h1>Dogs :)</h1>
    
    <div class="profile-photo3"><img src="https://images.dog.ceo/breeds/collie-border/n02106166_2345.jpg"></div>
    
    <nav>
      <ul>
        <li class="internal-nav"><a href="#">About Dogs</a></li>
        <li class="internal-nav selected"><a href="#">My Fav Dogs</a></li>
        <li class="internal-nav"><a href="#">Dog Facts</a></li>
      </ul>
    </nav>
  </header>

  <script src="https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-1b93190375e9ccc259df3a57c1abc0e64599724ae30d7ea4c6877eb615f89387.js"></script>
  <script src="https://cdpn.io/cp/internal/boomboom/pen.js?key=pen.js-f2136278-4370-4b42-10b9-cde5fe228f8b" crossorigin=""></script>
</body>

Upvotes: 4

Joachim Lous
Joachim Lous

Reputation: 1541

The core issue here is:

  • The size of the image depends on the width of the nav
  • The width of the nav changes

One of those has to go.
I'm assuming you want to keep the first and remove the second.

You can't make a ::before-element take up space when the style that creates it is not enganged, so the remaining (simple) strategies I can think of are:

  1. Reserve space for the marker all the time (as you suggest in your answer)
  2. Make the marker take up space all the time (as you suggest in your question)
  3. Make the marker take up no space all the time (like @yaroslav-trach suggests)

#1 lets you continue to base it on ::before, but is a bit brittle since it involves guesswork/knowledge of font witdth etc.
#2 is very robust, and can still be done in pure css.
#3 is in between: you still need to get the width right, but if you miss it only affects the look of the marker, not the rest of the layout

Upvotes: 1

Jamie Garcia
Jamie Garcia

Reputation: 294

add a max-width: (suggesting 65rem) to profile-photo3 to stop the photo shrinking the max width will never get larger than the appointed size though.

Upvotes: 0

krasi
krasi

Reputation: 38

Seeing as the nav items are unlikely to change, or if they change they will change to set values, it is fine to set the min-width of the nav to leave enough space for the ::before pseudo element, e.g. min-width: 12em.

Upvotes: 0

Related Questions