Повторите первый цвет в конце — тогда сдвиг будет бесшовным.
02
background-size: 200% 100%
Сделайте фон вдвое шире кнопки — видна только половина.
03
:hover { background-position: 100% 50% }
При наведении сдвиньте фон; transition сделает переход плавным.
04
:focus-visible { outline: … }
Оставьте видимый фокус для клавиатуры — градиент его не заменяет.
04
Частые ошибки
transition на background не работает
Браузер не умеет плавно менять один градиент на другой. Анимируйте background-position или opacity псевдоэлемента.
Подпись не читается на светлом конце
Белый текст проходит на тёмной половине и проваливается на светлой. Проверяйте контраст по всей длине — справа это делает инструмент.
Убран outline
outline: none без замены лишает клавиатурных пользователей фокуса — это нарушение WCAG 2.4.7.
05
Частые вопросы
Свойство transition не умеет плавно превращать один градиент в другой — фон сменится скачком. Есть два рабочих приёма. Первый — сделать фон вдвое шире кнопки и сдвигать background-position, как в коде рецепта. Второй — положить второй градиент на псевдоэлемент ::before и плавно менять его opacity от нуля до единицы. Первый проще, второй позволяет переходить к совсем другим цветам.
Тот, что проходит контраст 4,5:1 с каждой точкой фона, а не только с серединой. Белая подпись подходит, если оба края градиента достаточно тёмные; на светлом конце она проваливается. Инструмент на странице проверяет белый текст по всей длине и сообщает худшую точку. Если она ниже нормы, затемните светлый цвет или возьмите для подписи тёмный цвет и проверьте его в разделе читаемости на главной странице градиента.
В Tailwind третьей версии фон задают классами bg-gradient-to-r from-… via-… to-…, повторив первый цвет в конце. Ширину фона даёт bg-[length:200%_100%], исходную позицию — bg-left, сдвиг при наведении — hover:bg-right, а плавность — transition-all duration-300. Видимый фокус оставьте классами focus-visible:outline и outline-offset-2. В четвёртой версии направление пишется как bg-linear-to-r, остальное то же.
Да, и он стоит одну строку: перед градиентом задайте background сплошным цветом, обычно первым цветом перехода. Браузер, который не поймёт строку с градиентом, например из-за in oklch, возьмёт сплошной цвет, и кнопка останется видимой и читаемой. Без фолбэка такая кнопка превращается в прозрачный прямоугольник с белой подписью на белом фоне, то есть исчезает вместе с подписью.
Двух-трёх соседних по кругу оттенков достаточно: короткая поверхность кнопки не вмещает длинный переход, и пять цветов на ней превращаются в пёструю полосу. Для эффекта сдвига первый цвет повторяется в конце, так что фактически в коде их на один больше. Противоположные цвета, вроде синего и оранжевого, лучше смешивать в oklch — иначе на середине кнопки появится грязный участок.
Для :active хватит лёгкого сдвига вниз через transform: translateY(1px) и чуть меньшей тени — градиент при этом трогать не нужно. Можно затемнить фон, наложив поверх полупрозрачный слой: background: linear-gradient(rgba(0, 0, 0, .12), rgba(0, 0, 0, .12)), и следом исходный градиент. Отключённую кнопку делайте не прозрачной, а серой: прозрачный градиент на пёстром фоне читается как активная кнопка.