skip to Main Content

I made 2 buttons and a indicator to show which one is selected, but I don’t know how to center the indicator to the selected button vertically. The 2 buttons use flex: 1; for positioning. I’m sorry if my code is trash. I’m still learning how to code. I also want the indicator to slide to the other button smoothly.

let selectedHeaderButton = 1;

document.querySelector('.for-you-button').addEventListener('click', () => {
  selectedHeaderButton = 1;
})

document.querySelector('.following-button').addEventListener('click', () => {
  selectedHeaderButton = 2;
})
body {
  height: 3000px;
  margin-top: 100px;
  font-family: Noto Sans, Arial;
}

.header {
  position: relative;
  text-align: center;
  top: 0;
  left: 0;
  right: 0;
  border-bottom: 1px solid rgb(239, 243, 244);
  height: 106.5px;
}

.header-background {
  background-color: rgb(255, 255, 255, 0.5);
  top: 0;
  left: 0;
  right: 0;
  height: 106.5px;
  backdrop-filter: blur(4px);
  position: fixed;
}

.nav-selected {
  display: block;
  font-weight: bold;
  text-align: left;
  margin-left: 15px;
  font-size: 19px;
  filter: none;
  margin-top: 15px;
  margin-left: 10px;
}

.header-buttons {
  position: absolute;
  height: 106.5px;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: flex-end;
}

.for-you-button,
.following-button {
  flex: 1;
  border: none;
  background-color: rgba(255, 255, 255, 0);
  font-weight: 600;
  font-size: 15px;
  height: 53px;
  transition: background-color 150ms;
  text-align: center;
  color: rgb(83, 100, 113);
}

.for-you-button:hover,
.following-button:hover {
  background-color: rgba(200, 200, 200, 0.5);
}

.selected-button {
  height: 4px;
  width: 53px;
  background-color: rgb(29, 155, 240);
  position: absolute;
  bottom: 0;
  border-radius: 15px;
  left: var(--x);
}
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">

<header class="header-background">
  <header class="header">
    <div class="nav-selected">Home</div>
    <div class="header-buttons">
      <button class="for-you-button">For You</button>
      <button class="following-button">Following</button>
    </div>
    <div class="selected-button"></div>
  </header>
</header>
<main>
  <div class="tweet-container">
    <img>
    <div>Minecraft <span>@Minecraft &centerdot; Jul 12</span></div>
    <div>
      It's time for a new type of challenge! Show us your mob-making skills and build your pet in Blockbench. Don't forget to use the hashtag #FeatureMeMinecraft! Get started in our Creator Portal: https://minecraft.net/en-us/creator
    </div>
  </div>
</main>

2

Answers


  1. We can exploit the fact that the buttons are equal width and take up the same width as the .selected-button‘s container. This means we can position the selected indicator relative to its position container and it will line up with the buttons.

    First let’s establish some bearings. We can apply:

    .selected-button {
      …
      left: 50%;
      transform: translateX(-50%);
    }
    

    To center .selected-button exactly in the horizontal center. left: 50% places .selected-button‘s left edge to the .header‘s mid point. transform: translateX(-50%) then moves the .selected-button‘s left edge to the left by half its width, thereby aligning .selected-button‘s mid point with .header‘s mid point.

    With left: 50% being the middle between the two buttons, then we can deduce that left: 25% and left: 75% will be the middle of the two buttons. We can thus apply the default selection of the left button in CSS:

    .selected-button {
      …
      left: 25%;
    }
    

    And then manipulate the left property when one clicks on either button:

    const selected = document.querySelector('.selected-button');
    
    document.querySelector('.for-you-button').addEventListener('click', () => {
      selected.style.left = '25%';
    })
    
    document.querySelector('.following-button').addEventListener('click', () => {
      selected.style.left = '75%';
    })
    

    Lastly, to slide to the other button smoothly, we’ll add transition:

    .selected-button {
      …
      transition: left 150ms cubic-bezier(0, 0, 0.2, 1);
    }
    

    All together:

    const selected = document.querySelector('.selected-button');
    
    document.querySelector('.for-you-button').addEventListener('click', () => {
      selected.style.left = '25%';
    })
    
    document.querySelector('.following-button').addEventListener('click', () => {
      selected.style.left = '75%';
    })
    body {
      height: 3000px;
      margin-top: 100px;
      font-family: Noto Sans, Arial;
    }
    
    .header {
      position: relative;
      text-align: center;
      top: 0;
      left: 0;
      right: 0;
      border-bottom: 1px solid rgb(239, 243, 244);
      height: 106.5px;
    }
    
    .header-background {
      background-color: rgb(255, 255, 255, 0.5);
      top: 0;
      left: 0;
      right: 0;
      height: 106.5px;
      backdrop-filter: blur(4px);
      position: fixed;
    }
    
    .nav-selected {
      display: block;
      font-weight: bold;
      text-align: left;
      margin-left: 15px;
      font-size: 19px;
      filter: none;
      margin-top: 15px;
      margin-left: 10px;
    }
    
    .header-buttons {
      position: absolute;
      height: 106.5px;
      top: 0;
      left: 0;
      right: 0;
      display: flex;
      align-items: flex-end;
    }
    
    .for-you-button,
    .following-button {
      flex: 1;
      border: none;
      background-color: rgba(255, 255, 255, 0);
      font-weight: 600;
      font-size: 15px;
      height: 53px;
      transition: background-color 150ms;
      text-align: center;
      color: rgb(83, 100, 113);
    }
    
    .for-you-button:hover,
    .following-button:hover {
      background-color: rgba(200, 200, 200, 0.5);
    }
    
    .selected-button {
      height: 4px;
      width: 53px;
      background-color: rgb(29, 155, 240);
      position: absolute;
      bottom: 0;
      border-radius: 15px;
      left: 25%;
      transform: translateX(-50%);
      transition: left 150ms cubic-bezier(0, 0, 0.2, 1);
    }
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
    
    <header class="header-background">
      <header class="header">
        <div class="nav-selected">Home</div>
        <div class="header-buttons">
          <button class="for-you-button">For You</button>
          <button class="following-button">Following</button>
        </div>
        <div class="selected-button"></div>
      </header>
    </header>
    <main>
      <div class="tweet-container">
        <img>
        <div>Minecraft <span>@Minecraft &centerdot; Jul 12</span></div>
        <div>
          It's time for a new type of challenge! Show us your mob-making skills and build your pet in Blockbench. Don't forget to use the hashtag #FeatureMeMinecraft! Get started in our Creator Portal: https://minecraft.net/en-us/creator
        </div>
      </div>
    </main>
    Login or Signup to reply.
  2. Here is a bit more of an elegant solution in my opinion.

    First, I updated and reduced your code a bit to be a bit more modern.
    I.E. Get rid of a lot of the hardcoded pixels and let the padding and flex framework do the work and consolidate CSS to show you better practices. Generally speaking, there are not many reasons these days to be hardcoding pixel sizes, especially when working with flexbox or grid.

    This approach simply listens for a click, finds the the currently selected button and removes the "selected" class, then adds the "selected" class to the button you clicked. It then adds a pseudo element to the selected button that will always be centered. This eliminates any need for math/margin fun and is also scalable.

    Hope this helps!

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <link
            href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
            rel="stylesheet">
        <style>
            body {
                height: 3000px;
                margin-top: 100px;
                font-family: Noto Sans, Arial;
            }
    
            .header {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                border-bottom: 1px solid rgb(239, 243, 244);
                backdrop-filter: blur(4px);
                background-color: rgb(255, 255, 255, 0.5);
            }
    
            .nav-selected {
                display: block;
                font-weight: bold;
                font-size: 1.25rem;
                padding: 1rem;
            }
    
            .header-buttons {
                top: 0;
                left: 0;
                right: 0;
                display: flex;
                align-items: flex-end;
                cursor: pointer;
            }
    
            .header-button {
                position: relative;
            }
    
            .header-button.selected:before {
                content: '';
                position: absolute;
                bottom: 0;
                right: 50%;
                transform: translate(50%, 100%);
                width: 4rem;
                height: .25rem;
                border-radius: 1rem;
                background-color: rgb(29, 155, 240);
            }
    
            .header-button {
                flex: 1;
                flex-basis: 50%;
                border: none;
                background-color: rgba(255, 255, 255, 0);
                font-weight: 600;
                font-size: 1rem;
                padding: 1rem 0;
                transition: all .25s ease;
                text-align: center;
                color: rgb(83, 100, 113);
                cursor: pointer;
            }
    
            .for-you-button:hover,
            .following-button:hover {
                background-color: rgba(200, 200, 200, 0.5);
            }
        </style>
    </head>
    
    <body>
        <header class="header-background">
            <header class="header">
                <div class="nav-selected">Home</div>
                <div class="header-buttons">
                    <button class="for-you-button header-button selected">For You</button>
                    <button class="following-button header-button">Following</button>
                </div>
                <div class="selected-button"></div>
            </header>
        </header>
        <main>
            <div class="tweet-container">
                <img>
                <div>Minecraft <span>@Minecraft &centerdot; Jul 12</span></div>
                <div>
                    It's time for a new type of challenge! Show us your mob-making skills and build your pet in Blockbench.
                    Don't forget to use the hashtag #FeatureMeMinecraft! Get started in our Creator Portal:
                    https://minecraft.net/en-us/creator
                </div>
            </div>
        </main>
        <script>
            const headerButtons = document.querySelectorAll('.header-button');
            headerButtons.forEach(btn => {
                btn.addEventListener('click', () => {
                    document.querySelector('.selected').classList.remove('selected')
                    btn.classList.add('selected')
                });
            });
        </script>
    </body>
    </html>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search