import SlideTextButton from "@/components/kokonutui/slide-text-button";
export default function ImportedDemo(){return <>
<SlideTextButton hoverText="Click to see more" />
</>}Slide Text Button
By Kokonut UI
Try the interaction, copy the React source files, or download the complete example.
About Slide Text Button
Slide Text Button is an animated React and Tailwind CSS button by Kokonut UI. Try the interaction, copy the React source, or download the example with creator credit.
- Effects and categories
- Hover effects, Animated
- Using this example
- Copy a TSX file or download the original source and standalone demo. Test keyboard focus, motion and contrast in your own project.
Usage, Tailwind and licensing questions
Read the source files
import-examples/slide-text-button.tsx
import SlideTextButton from "@/components/kokonutui/slide-text-button";
export default function ImportedDemo(){return <>
<SlideTextButton hoverText="Click to see more" />
</>}components/kokonutui/slide-text-button.tsx
"use client";
/**
* @author: @kokonut-labs
* @description: Slide Text Button with animated vertical text transition
* @version: 1.0.0
* @date: 2025-11-02
* @license: MIT
* @website: https://kokonutui.com
* @github: https://github.com/kokonut-labs/kokonutui
*/
import Link from "next/link";
import { cn } from "@/lib/utils";
interface SlideTextButtonProps
extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
text?: string;
hoverText?: string;
href?: string;
className?: string;
variant?: "default" | "ghost";
/** Fades and slides the button in when it mounts. */
animateIn?: boolean;
}
export default function SlideTextButton({
text = "Browse Components",
hoverText,
href = "/docs",
className,
variant = "default",
animateIn = true,
...props
}: SlideTextButtonProps) {
const slideText = hoverText ?? text;
const variantStyles =
variant === "ghost"
? "border border-black/10 text-black hover:bg-black/5 dark:border-white/10 dark:text-white dark:hover:bg-white/5"
: "bg-black text-white hover:bg-black/90 dark:bg-white dark:text-black dark:hover:bg-white/90";
return (
<Link
className={cn(
"group relative inline-flex h-10 items-center justify-center overflow-hidden rounded-lg px-8 font-medium text-base tracking-tighter transition-[background-color,scale,opacity,translate] duration-[150ms,150ms,200ms,200ms] ease-[cubic-bezier(0.23,1,0.32,1)] focus-visible:outline-2 focus-visible:outline-black focus-visible:outline-offset-2 active:scale-[0.97] motion-reduce:active:scale-100 md:min-w-56 dark:focus-visible:outline-white",
// CSS entrance from @starting-style: no JS and no wrapper element.
// Reduced motion keeps the fade and drops the slide.
animateIn &&
"starting:translate-x-2 starting:opacity-0 motion-reduce:starting:translate-x-0",
variantStyles,
className
)}
href={href}
{...props}
>
{/* Both labels share one grid cell, so the wider one sets the width
and both stay centred; the hover label waits one row below. */}
<span className="grid justify-items-center transition-transform duration-200 ease-[cubic-bezier(0.77,0,0.175,1)] group-hover:-translate-y-full group-focus-visible:-translate-y-full motion-reduce:transition-none">
<span className="col-start-1 row-start-1 flex items-center gap-2 opacity-100 transition-opacity duration-200 group-hover:opacity-0 group-focus-visible:opacity-0">
<span className="font-medium">{text}</span>
</span>
<span
aria-hidden="true"
className="col-start-1 row-start-1 flex translate-y-full items-center gap-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100 group-focus-visible:opacity-100"
>
<span className="font-medium">{slideText}</span>
</span>
</span>
</Link>
);
}
lib/utils.ts
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
demo.tsx
export { default as default } from "@/import-examples/slide-text-button";CSS
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties;
@layer theme, base, components, utilities;
@layer theme {
:root, :host {
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
"Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol", "Noto Color Emoji";
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
--color-black: #000;
--color-white: #fff;
--spacing: 0.25rem;
--text-base: 1rem;
--text-base--line-height: calc(1.5 / 1);
--font-weight-medium: 500;
--tracking-tighter: -0.05em;
--radius-lg: var(--radius);
--default-transition-duration: 150ms;
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
--default-font-family: var(--font-sans);
--default-mono-font-family: var(--font-mono);
}
}
@layer base {
*, ::after, ::before, ::backdrop, ::file-selector-button {
box-sizing: border-box;
margin: 0;
padding: 0;
border: 0 solid;
}
html, :host {
line-height: 1.5;
-webkit-text-size-adjust: 100%;
tab-size: 4;
font-family: var(--default-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
font-feature-settings: var(--default-font-feature-settings, normal);
font-variation-settings: var(--default-font-variation-settings, normal);
-webkit-tap-highlight-color: transparent;
}
hr {
height: 0;
color: inherit;
border-top-width: 1px;
}
abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}
h1, h2, h3, h4, h5, h6 {
font-size: inherit;
font-weight: inherit;
}
a {
color: inherit;
-webkit-text-decoration: inherit;
text-decoration: inherit;
}
b, strong {
font-weight: bolder;
}
code, kbd, samp, pre {
font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
font-feature-settings: var(--default-mono-font-feature-settings, normal);
font-variation-settings: var(--default-mono-font-variation-settings, normal);
font-size: 1em;
}
small {
font-size: 80%;
}
sub, sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
table {
text-indent: 0;
border-color: inherit;
border-collapse: collapse;
}
:-moz-focusring:where(:not(iframe)) {
outline: auto;
}
progress {
vertical-align: baseline;
}
summary {
display: list-item;
}
ol, ul, menu {
list-style: none;
}
img, svg, video, canvas, audio, iframe, embed, object {
display: block;
vertical-align: middle;
}
img, video {
max-width: 100%;
height: auto;
}
button, input, select, optgroup, textarea, ::file-selector-button {
font: inherit;
font-feature-settings: inherit;
font-variation-settings: inherit;
letter-spacing: inherit;
color: inherit;
border-radius: 0;
background-color: transparent;
opacity: 1;
}
:where(select:is([multiple], [size])) optgroup {
font-weight: bolder;
}
:where(select:is([multiple], [size])) optgroup option {
padding-inline-start: 20px;
}
::file-selector-button {
margin-inline-end: 4px;
}
::placeholder {
opacity: 1;
}
@supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
::placeholder {
color: currentcolor;
@supports (color: color-mix(in lab, red, red)) {
color: color-mix(in oklab, currentcolor 50%, transparent);
}
}
}
textarea {
resize: vertical;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-date-and-time-value {
min-height: 1lh;
text-align: inherit;
}
::-webkit-datetime-edit {
display: inline-flex;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
padding-block: 0;
}
::-webkit-calendar-picker-indicator {
line-height: 1;
}
:-moz-ui-invalid {
box-shadow: none;
}
button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {
appearance: button;
}
::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
height: auto;
}
[hidden]:where(:not([hidden="until-found"])) {
display: none !important;
}
}
@layer utilities {
.relative {
position: relative;
}
.col-start-1 {
grid-column-start: 1;
}
.row-start-1 {
grid-row-start: 1;
}
.flex {
display: flex;
}
.grid {
display: grid;
}
.inline-flex {
display: inline-flex;
}
.h-10 {
height: calc(var(--spacing) * 10);
}
.translate-y-full {
--tw-translate-y: 100%;
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.justify-items-center {
justify-items: center;
}
.gap-2 {
gap: calc(var(--spacing) * 2);
}
.overflow-hidden {
overflow: hidden;
}
.rounded-lg {
border-radius: var(--radius-lg);
}
.border {
border-style: var(--tw-border-style);
border-width: 1px;
}
.border-black\/10 {
border-color: color-mix(in srgb, #000 10%, transparent);
@supports (color: color-mix(in lab, red, red)) {
border-color: color-mix(in oklab, var(--color-black) 10%, transparent);
}
}
.bg-black {
background-color: var(--color-black);
}
.px-8 {
padding-inline: calc(var(--spacing) * 8);
}
.text-base {
font-size: var(--text-base);
line-height: var(--tw-leading, var(--text-base--line-height));
}
.font-medium {
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
}
.tracking-tighter {
--tw-tracking: var(--tracking-tighter);
letter-spacing: var(--tracking-tighter);
}
.text-black {
color: var(--color-black);
}
.text-white {
color: var(--color-white);
}
.opacity-0 {
opacity: 0%;
}
.opacity-100 {
opacity: 100%;
}
.transition {
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.transition-\[background-color\,scale\,opacity\,translate\] {
transition-property: background-color,scale,opacity,translate;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.transition-opacity {
transition-property: opacity;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.transition-transform {
transition-property: transform, translate, scale, rotate;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.duration-200 {
--tw-duration: 200ms;
transition-duration: 200ms;
}
.duration-\[150ms\,150ms\,200ms\,200ms\] {
--tw-duration: 150ms,150ms,200ms,200ms;
transition-duration: 150ms,150ms,200ms,200ms;
}
.ease-\[cubic-bezier\(0\.23\,1\,0\.32\,1\)\] {
--tw-ease: cubic-bezier(0.23,1,0.32,1);
transition-timing-function: cubic-bezier(0.23,1,0.32,1);
}
.ease-\[cubic-bezier\(0\.77\,0\,0\.175\,1\)\] {
--tw-ease: cubic-bezier(0.77,0,0.175,1);
transition-timing-function: cubic-bezier(0.77,0,0.175,1);
}
@media (hover: hover) {
.group-hover\:-translate-y-full:is(:where(.group):hover *) {
--tw-translate-y: -100%;
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.group-hover\:opacity-0:is(:where(.group):hover *) {
opacity: 0%;
}
.group-hover\:opacity-100:is(:where(.group):hover *) {
opacity: 100%;
}
}
.group-focus-visible\:-translate-y-full:is(:where(.group):focus-visible *) {
--tw-translate-y: -100%;
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.group-focus-visible\:opacity-0:is(:where(.group):focus-visible *) {
opacity: 0%;
}
.group-focus-visible\:opacity-100:is(:where(.group):focus-visible *) {
opacity: 100%;
}
@media (hover: hover) {
.hover\:bg-black\/5:hover {
background-color: color-mix(in srgb, #000 5%, transparent);
}
@supports (color: color-mix(in lab, red, red)) {
.hover\:bg-black\/5:hover {
background-color: color-mix(in oklab, var(--color-black) 5%, transparent);
}
}
.hover\:bg-black\/90:hover {
background-color: color-mix(in srgb, #000 90%, transparent);
}
@supports (color: color-mix(in lab, red, red)) {
.hover\:bg-black\/90:hover {
background-color: color-mix(in oklab, var(--color-black) 90%, transparent);
}
}
}
.focus-visible\:outline-2:focus-visible {
outline-style: var(--tw-outline-style);
outline-width: 2px;
}
.focus-visible\:outline-offset-2:focus-visible {
outline-offset: 2px;
}
.focus-visible\:outline-black:focus-visible {
outline-color: var(--color-black);
}
.active\:scale-\[0\.97\]:active {
scale: 0.97;
}
@media (prefers-reduced-motion: reduce) {
.motion-reduce\:transition-none {
transition-property: none;
}
.motion-reduce\:active\:scale-100:active {
--tw-scale-x: 100%;
--tw-scale-y: 100%;
--tw-scale-z: 100%;
scale: var(--tw-scale-x) var(--tw-scale-y);
}
}
@media (width >= 48rem) {
.md\:min-w-56 {
min-width: calc(var(--spacing) * 56);
}
}
.dark\:border-white\/10:where(.dark, .dark *) {
border-color: color-mix(in srgb, #fff 10%, transparent);
@supports (color: color-mix(in lab, red, red)) {
border-color: color-mix(in oklab, var(--color-white) 10%, transparent);
}
}
.dark\:bg-white:where(.dark, .dark *) {
background-color: var(--color-white);
}
.dark\:text-black:where(.dark, .dark *) {
color: var(--color-black);
}
.dark\:text-white:where(.dark, .dark *) {
color: var(--color-white);
}
@media (hover: hover) {
.dark\:hover\:bg-white\/5:where(.dark, .dark *):hover {
background-color: color-mix(in srgb, #fff 5%, transparent);
}
@supports (color: color-mix(in lab, red, red)) {
.dark\:hover\:bg-white\/5:where(.dark, .dark *):hover {
background-color: color-mix(in oklab, var(--color-white) 5%, transparent);
}
}
.dark\:hover\:bg-white\/90:where(.dark, .dark *):hover {
background-color: color-mix(in srgb, #fff 90%, transparent);
}
@supports (color: color-mix(in lab, red, red)) {
.dark\:hover\:bg-white\/90:where(.dark, .dark *):hover {
background-color: color-mix(in oklab, var(--color-white) 90%, transparent);
}
}
}
.dark\:focus-visible\:outline-white:where(.dark, .dark *):focus-visible {
outline-color: var(--color-white);
}
@starting-style {
.starting\:translate-x-2 {
--tw-translate-x: calc(var(--spacing) * 2);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.starting\:opacity-0 {
opacity: 0%;
}
}
@media (prefers-reduced-motion: reduce) {
@starting-style {
.motion-reduce\:starting\:translate-x-0 {
--tw-translate-x: 0px;
translate: var(--tw-translate-x) var(--tw-translate-y);
}
}
}
}
:root {
--background: #fff;
--foreground: #111;
--primary: #171717;
--primary-foreground: #fff;
--secondary: #eee;
--secondary-foreground: #171717;
--muted: #eee;
--muted-foreground: #666;
--accent: #eee;
--accent-foreground: #171717;
--border: #ddd;
--input: #ddd;
--ring: #777;
--destructive: #dc2626;
--destructive-foreground: #fff;
--popover: #fff;
--popover-foreground: #111;
--success: #15803d;
--success-foreground: #fff;
--radius: .625rem;
--hairline: 1px;
}
.dark {
--background: #090909;
--foreground: #fafafa;
--primary: #fafafa;
--primary-foreground: #171717;
--secondary: #262626;
--secondary-foreground: #fafafa;
--muted: #262626;
--muted-foreground: #a3a3a3;
--accent: #262626;
--accent-foreground: #fafafa;
--border: #333;
--input: #333;
--popover: #171717;
--popover-foreground: #fafafa;
--color-fd-background: #000;
--color-fd-card: #0a0a0a;
--color-fd-popover: #0a0a0a;
--color-fd-border: rgb(255 255 255 / 0.14);
--color-fd-accent: rgb(255 255 255 / 0.07);
}
:root {
--sidebar: hsl(0 0% 98%);
--sidebar-foreground: hsl(240 5.3% 26.1%);
--sidebar-primary: hsl(240 5.9% 10%);
--sidebar-primary-foreground: hsl(0 0% 98%);
--sidebar-accent: hsl(240 4.8% 95.9%);
--sidebar-accent-foreground: hsl(240 5.9% 10%);
--sidebar-border: hsl(220 13% 91%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
}
.dark {
--sidebar: hsl(240 5.9% 10%);
--sidebar-foreground: hsl(240 4.8% 95.9%);
--sidebar-primary: hsl(224.3 76.3% 48%);
--sidebar-primary-foreground: hsl(0 0% 100%);
--sidebar-accent: hsl(240 3.7% 15.9%);
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
--sidebar-border: hsl(240 3.7% 15.9%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
}
:root {
--leaf: #1fa834;
}
.dark {
--leaf: #3bb44b;
}
@property --tw-translate-x {
syntax: "*";
inherits: false;
initial-value: 0;
}
@property --tw-translate-y {
syntax: "*";
inherits: false;
initial-value: 0;
}
@property --tw-translate-z {
syntax: "*";
inherits: false;
initial-value: 0;
}
@property --tw-border-style {
syntax: "*";
inherits: false;
initial-value: solid;
}
@property --tw-font-weight {
syntax: "*";
inherits: false;
}
@property --tw-tracking {
syntax: "*";
inherits: false;
}
@property --tw-duration {
syntax: "*";
inherits: false;
}
@property --tw-ease {
syntax: "*";
inherits: false;
}
@property --tw-outline-style {
syntax: "*";
inherits: false;
initial-value: solid;
}
@property --tw-scale-x {
syntax: "*";
inherits: false;
initial-value: 1;
}
@property --tw-scale-y {
syntax: "*";
inherits: false;
initial-value: 1;
}
@property --tw-scale-z {
syntax: "*";
inherits: false;
initial-value: 1;
}
@layer properties {
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
*, ::before, ::after, ::backdrop {
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-translate-z: 0;
--tw-border-style: solid;
--tw-font-weight: initial;
--tw-tracking: initial;
--tw-duration: initial;
--tw-ease: initial;
--tw-outline-style: solid;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-scale-z: 1;
}
}
}