矢印がスライドするボタン
概要
ボタンの横にある矢印がホバーすることでスライドするボタンです。
矢印がボタン方向にスライドすることでボタンに視線を誘導することが狙いのアニメーションです。
実装は極力無駄なマークアップをしないよう工夫しました。
ボタンのラッパー要素とボタンの擬似要素を使って矢印の頭、矢印の線を描いています。
ホバー時のスライドは矢印の頭のみをスライドさせ矢印が動いているように表現しています。
背景はボタンのラッパー要素の親要素と同じになるようにしています。背景色がないと「button」の文字の背面に矢印の頭が見えてしまうので、背景色は設定することがポイントです。
コードサンプル
HTML
<div class="btn-wrap">
<a class="btn" tabindex="0">Button</a>
</div>
CSS
.btn-wrap {
--color-gray: #7d8797;
--color-dark: #333333;
--font-color: var(--color-dark);
--arrow-w: 2em;
--arrow-head: 0.6em;
--arrow-head-thick: 0.1em;
--arrow-head-pos: calc( (var(--arrow-head) - var(--arrow-head-thick) ) * sqrt(2) * -1 );
--anim-duration: 0.3s;
position: relative;
display: inline-block;
padding-inline-start: var(--arrow-w);
background-color: inherit;
box-sizing: border-box;
overflow: hidden;
}
.btn-wrap::before,
.btn-wrap::after {
position: absolute;
inset-inline-start: 0;
inset-block: 0;
display: block;
margin-block: auto;
width: var(--arrow-head);
height: var(--arrow-head);
content: "";
border: var(--arrow-head-thick) solid;
border-color: var(--font-color) var(--font-color) transparent transparent;
box-sizing: inherit;
rotate: 45deg;
transition: border-color var(--anim-duration), translate var(--anim-duration);
will-change: background-color, transform;
}
.btn-wrap::before {
inset-inline-start: var(--arrow-head-pos);
}
.btn-wrap::after {
inset-inline-start: calc(var(--arrow-w) + var(--arrow-head-pos));
}
.btn {
position: relative;
display: inline-block;
padding-block: 0.4em;
padding-inline: 0.5em;
font-size: inherit;
color: var(--font-color);
background-color: inherit;
text-decoration: underline;
text-underline-offset: 0.3em;
text-decoration-color: var(--font-color);
transition: color var(--anim-duration), text-decoration-color var(--anim-duration);
will-change: color, text-decoration-color;
z-index: 1;
}
.btn::before {
position: absolute;
inset-inline-end: 100%;
inset-block: 0;
display: block;
margin-block: auto;
width: var(--arrow-w);
height: 0.08em;
content: "";
background-color: var(--font-color);
border-radius: 1em;
transition: background-color var(--anim-duration);
will-change: background-color;
}
.btn:focus-visible {
outline: none;
}
@media (any-hover) {
.btn-wrap:has(.btn:hover) {
--font-color: var(--color-gray);
}
.btn-wrap:has(.btn:hover)::before,
.btn-wrap:has(.btn:hover)::after {
translate: var(--arrow-w);
}
}