Issue
I've got a list of menu items, and to indicate the "active" item, it gets a certain background colour. When the active item changes to another, this background slides to the new item with an animation.
Inactive menu items are coloured black, active menu items are coloured white, and the background is coloured blue (for simplicity - the exact colours are obviously irrelevant). The active colour should only be applied to the part of the text that has the indicatory background behind it.
I know I can use mix-blend-mode: difference
in case the text starts off being the same colour as the moving background. In my case, the text has a different colour than the background when the item is inactive.
How can I achieve this effect using CSS?
For reference, the implementation with mix-blend-mode: difference
is as follows:
const slider = document.querySelector('.tab-slider');
document.querySelectorAll('.tabs a').forEach(a => {
a.onclick = () => {
document.querySelector('.active').classList.remove('active');
const li = a.parentElement;
li.classList.add('active');
slider.style.left = li.offsetLeft + "px";
slider.style.width = li.offsetWidth + "px";
};
});
// init
const li = document.querySelector(".active");
slider.style.left = li.offsetLeft + "px";
slider.style.width = li.offsetWidth + "px";
slider.style.height = li.offsetHeight + "px";
html {
font-family: sans-serif;
}
.tabs ul {
display: flex;
list-style-type: none;
}
.tabs li {
padding-block: 2px;
padding-inline: 5px;
}
.tabs a {
text-decoration: none;
color: blueviolet;
mix-blend-mode: difference;
}
.tab-slider {
position: absolute;
background: blueviolet;
z-index: -1;
transition: all 1000ms;
}
<nav class="tabs">
<div class="tab-slider"></div>
<ul>
<li class="active"><a href="#">about</a></li>
<li><a href="#">summary</a></li>
<li><a href="#">refs</a></li>
<li><a href="#">log</a></li>
<li><a href="#">tree</a></li>
<li><a href="#">commit</a></li>
<li><a href="#">diff</a></li>
<li><a href="#">stats</a></li>
</ul>
</nav>
Solution
One way to have complete control of the three colors is to make the overlay a copy of the whole list, with pointer events off and a clip-path to show only that part that is active, with a transition (as now) between the start and end clip-paths.
<style>
html {
font-family: sans-serif;
}
* {
margin: 0;
}
.tabs {
position: relative;
}
.tabs ul {
display: flex;
list-style-type: none;
}
.tabs li {
padding-block: 2px;
padding-inline: 5px;
}
.tabs a {
text-decoration: none;
color: blueviolet;
}
.tabs .tab-slider {
position: absolute;
background-color: red;
top: 0;
left: 0;
transition: all 1000ms;
clip-path: polygon(var(--left) 0, var(--right) 0, var(--right) 100%, var(--left) 100%);
}
.tabs .tab-slider a {
color: yellow;
pointer-events: none;
}
</style>
<nav class="tabs">
<ul class="tab-slider">
<li><a href="#">about</a></li>
<li><a href="#">summary</a></li>
<li><a href="#">refs</a></li>
<li><a href="#">log</a></li>
<li><a href="#">tree</a></li>
<li><a href="#">commit</a></li>
<li><a href="#">diff</a></li>
<li><a href="#">stats</a></li>
</ul>
<ul class="main">
<li><a href="#" class="active">about</a></li>
<li><a href="#">summary</a></li>
<li><a href="#">refs</a></li>
<li><a href="#">log</a></li>
<li><a href="#">tree</a></li>
<li><a href="#">commit</a></li>
<li><a href="#">diff</a></li>
<li><a href="#">stats</a></li>
</ul>
</nav>
<script>
const slider = document.querySelector('.tab-slider');
document.querySelectorAll('.tabs ul.main a').forEach(a => {
a.onclick = () => {
document.querySelector('.active').classList.remove('active');
const li = a.parentElement;
li.classList.add('active');
//slider.style.left = li.offsetLeft + "px";
//slider.style.width = li.offsetWidth + "px";
slider.style.setProperty('--left', li.offsetLeft + "px");
slider.style.setProperty('--right', li.offsetLeft + li.offsetWidth + "px");
};
});
// init
const li = document.querySelector(".active");
slider.style.setProperty('--left', li.offsetLeft + "px");
slider.style.setProperty('--right', li.offsetLeft + li.offsetWidth + "px");
</script>
Answered By - A Haworth
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.