import { cn } from "@/packages/components/react-shadcn/src/lib/utils";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = "Button";
export { Button, buttonVariants };
Native Liquid Button Wave
Try the interaction, copy the React source files, or download the complete example.
About Native Liquid Button Wave
Native Liquid Button Wave is an animated React and Tailwind CSS button by Moumen Soliman. 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
packages/components/react-shadcn/src/components/native/demo/native-liquid-button-demo.tsx
"use client";
import { NativeLiquidButton } from "../native-liquid-button-shadcnui";
import { useState } from "react";
export function NativeLiquidButtonDefault() {
return (
<div className="flex items-center justify-center p-8 min-h-[200px]">
<NativeLiquidButton autoSimulate>Submit</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonGradient() {
return (
<div className="flex items-center justify-center p-8 min-h-[200px]">
<NativeLiquidButton liquidVariant="gradient" autoSimulate>
Download
</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonGlow() {
return (
<div className="flex items-center justify-center p-8 min-h-[200px]">
<NativeLiquidButton liquidVariant="glow" autoSimulate>
Complete
</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonWave() {
return (
<div className="flex items-center justify-center p-8 min-h-[200px]">
<NativeLiquidButton liquidVariant="wave" autoSimulate>
Process
</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonWithProgress() {
const [progress, setProgress] = useState(0);
const handleClick = async () => {
// Simulate progress
for (let i = 0; i <= 100; i += 10) {
await new Promise((resolve) => setTimeout(resolve, 200));
setProgress(i);
}
};
return (
<div className="flex items-center justify-center p-8 min-h-[200px]">
<NativeLiquidButton
progress={progress}
onClick={handleClick}
showPercentage
>
Upload File
</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonLoading() {
return (
<div className="flex items-center justify-center p-8 min-h-[200px]">
<NativeLiquidButton loading progress={50}>
Processing...
</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonSuccess() {
return (
<div className="flex items-center justify-center p-8 min-h-[200px]">
<NativeLiquidButton success progress={100}>
Success!
</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonError() {
return (
<div className="flex items-center justify-center p-8 min-h-[200px]">
<NativeLiquidButton error progress={100}>
Failed
</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonSizes() {
return (
<div className="flex items-center justify-center gap-4 p-8 min-h-[200px] flex-wrap">
<NativeLiquidButton size="sm" autoSimulate>
Small
</NativeLiquidButton>
<NativeLiquidButton size="default" autoSimulate>
Medium
</NativeLiquidButton>
<NativeLiquidButton size="lg" autoSimulate>
Large
</NativeLiquidButton>
<NativeLiquidButton size="lg" autoSimulate>
Extra Large
</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonVariants() {
return (
<div className="flex items-center justify-center gap-4 p-8 min-h-[200px] flex-wrap">
<NativeLiquidButton variant="default" autoSimulate>
Default
</NativeLiquidButton>
<NativeLiquidButton variant="outline" autoSimulate>
Outline
</NativeLiquidButton>
<NativeLiquidButton variant="secondary" autoSimulate>
Secondary
</NativeLiquidButton>
<NativeLiquidButton variant="ghost" autoSimulate>
Ghost
</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonCustomColor() {
return (
<div className="flex items-center justify-center p-8 min-h-[200px]">
<NativeLiquidButton liquidColor="bg-purple-500" autoSimulate>
Custom Color
</NativeLiquidButton>
</div>
);
}
export function NativeLiquidButtonDemo() {
return <NativeLiquidButtonDefault />;
}
packages/components/react-shadcn/src/components/native/native-liquid-button-shadcnui.tsx
"use client";
import {
NativeButton,
type NativeButtonProps,
} from "./native-button-shadcnui";
import { cn } from "@/packages/components/react-shadcn/src/lib/utils";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { Loader2 } from "lucide-react";
import { useEffect, useState } from "react";
export interface NativeLiquidButtonProps
extends Omit<NativeButtonProps, "onClick" | "loading"> {
/**
* Progress value (0-100)
*/
progress?: number;
/**
* Loading state
*/
loading?: boolean;
/**
* Callback when button is clicked
*/
onClick?: () => void | Promise<void>;
/**
* Visual variant
* Default: "default"
*/
liquidVariant?: "default" | "gradient" | "glow" | "wave";
/**
* Liquid color
*/
liquidColor?: string;
/**
* Show percentage text
* Default: false
*/
showPercentage?: boolean;
/**
* Auto-simulate loading (for demo purposes)
*/
autoSimulate?: boolean;
/**
* Success state
*/
success?: boolean;
/**
* Error state
*/
error?: boolean;
}
const sizeVariants = {
sm: "h-9 px-4 text-sm min-w-[100px]",
default: "h-11 px-6 text-base min-w-[140px]",
lg: "h-14 px-8 text-lg min-w-[180px]",
icon: "h-11 px-6 text-base min-w-[140px]",
};
export function NativeLiquidButton({
children,
className,
variant = "default",
size = "default",
progress = 0,
loading = false,
onClick,
disabled,
liquidVariant = "default",
liquidColor,
showPercentage = false,
autoSimulate = false,
success = false,
error = false,
...props
}: NativeLiquidButtonProps) {
const [internalProgress, setInternalProgress] = useState(progress);
const [isSimulating, setIsSimulating] = useState(false);
const shouldReduceMotion = useReducedMotion() ?? false;
useEffect(() => {
setInternalProgress(progress);
}, [progress]);
useEffect(() => {
if (autoSimulate && isSimulating) {
const interval = setInterval(() => {
setInternalProgress((prev) => {
if (prev >= 100) {
clearInterval(interval);
setIsSimulating(false);
return 100;
}
return prev + Math.random() * 10;
});
}, 200);
return () => clearInterval(interval);
}
}, [autoSimulate, isSimulating]);
const handleClick = async () => {
if (disabled || loading) return;
if (autoSimulate) {
setIsSimulating(true);
setInternalProgress(0);
}
if (onClick) {
await onClick();
}
};
const getLiquidColor = () => {
if (success) return "bg-green-500";
if (error) return "bg-red-500";
if (liquidColor) return liquidColor;
switch (liquidVariant) {
case "gradient":
return "bg-gradient-to-r from-primary via-primary/80 to-primary";
case "glow":
return "bg-primary";
default:
return "bg-primary";
}
};
const clampedProgress = Math.min(Math.max(internalProgress, 0), 100);
const isFilling = clampedProgress > 0 && clampedProgress < 100;
const showAmbient = !shouldReduceMotion && clampedProgress > 0;
return (
<div className="relative inline-block">
<NativeButton
variant={variant}
size={size}
loading={false}
disabled={disabled || loading}
onClick={handleClick}
aria-busy={loading || isSimulating}
className={cn(
sizeVariants[size || "default"],
"relative overflow-hidden font-semibold transition-[box-shadow,background-color,border-color,color] duration-300",
"before:absolute before:inset-0 before:bg-background/20 before:pointer-events-none before:rounded-md",
liquidVariant === "glow" &&
!disabled &&
"shadow-lg shadow-primary/30",
className
)}
{...props}
>
{/* Liquid fill — width-based so the liquid inside never distorts */}
<motion.div
aria-hidden="true"
className={cn(
"pointer-events-none absolute inset-y-0 left-0 overflow-hidden transition-colors duration-300",
getLiquidColor()
)}
initial={false}
animate={{ width: `${clampedProgress}%` }}
transition={
shouldReduceMotion
? { duration: 0.2, ease: "easeOut" }
: { type: "spring", stiffness: 100, damping: 20 }
}
>
{/* Meniscus — bright leading edge where the liquid meets air */}
{isFilling && (
<div className="absolute inset-y-0 right-0 w-3 bg-gradient-to-l from-white/30 to-transparent" />
)}
{/* Rising bubbles */}
{liquidVariant === "default" && showAmbient && (
<>
{[...Array(4)].map((_, i) => (
<motion.div
key={i}
className="absolute bottom-0 h-1.5 w-1.5 rounded-full bg-white/40"
style={{
left: `${15 + i * 22}%`,
}}
animate={{
y: [2, -36],
opacity: [0, 0.8, 0],
scale: [0.6, 1, 0.7],
}}
transition={{
duration: 1.8,
repeat: Number.POSITIVE_INFINITY,
delay: i * 0.45,
ease: "easeOut",
}}
/>
))}
</>
)}
{/* Shimmer effect for gradient */}
{liquidVariant === "gradient" && showAmbient && (
<motion.div
className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent"
animate={{
x: ["-100%", "200%"],
}}
transition={{
duration: 1.5,
repeat: Number.POSITIVE_INFINITY,
ease: "linear",
}}
/>
)}
</motion.div>
{/* Wave — a translucent surface layer sloshing around the fill edge */}
{liquidVariant === "wave" && showAmbient && isFilling && (
<motion.div
aria-hidden="true"
className="pointer-events-none absolute inset-y-0 left-0 bg-white/15"
animate={{
width: [
`${Math.max(clampedProgress - 6, 0)}%`,
`${Math.min(clampedProgress + 4, 100)}%`,
`${Math.max(clampedProgress - 6, 0)}%`,
],
}}
transition={{
duration: 2.2,
repeat: Number.POSITIVE_INFINITY,
ease: "easeInOut",
}}
/>
)}
{/* Button content */}
<span className="relative z-10 flex items-center justify-center gap-2">
<AnimatePresence mode="wait">
{loading && (
<motion.div
key="loading"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<Loader2 aria-hidden="true" className="w-4 h-4 animate-spin" />
</motion.div>
)}
</AnimatePresence>
<motion.span
animate={
loading && !shouldReduceMotion
? { opacity: [1, 0.5, 1] }
: { opacity: 1 }
}
transition={
loading && !shouldReduceMotion
? {
duration: 1.5,
repeat: Number.POSITIVE_INFINITY,
ease: "easeInOut",
}
: { duration: 0.2 }
}
>
{children}
</motion.span>
{showPercentage && (
<span className="text-xs tabular-nums">
{Math.round(clampedProgress)}%
</span>
)}
</span>
{/* Glow effect */}
{liquidVariant === "glow" && !disabled && showAmbient && (
<motion.div
aria-hidden="true"
className="pointer-events-none absolute inset-0 bg-primary/20 blur-xl"
animate={{
opacity: [0.3, 0.6, 0.3],
}}
transition={{
duration: 2,
repeat: Number.POSITIVE_INFINITY,
ease: "easeInOut",
}}
/>
)}
</NativeButton>
{clampedProgress > 0 && (
<span
role="progressbar"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={Math.round(clampedProgress)}
className="sr-only"
>
{Math.round(clampedProgress)}%
</span>
)}
</div>
);
}
packages/components/react-shadcn/src/lib/utils.ts
import { cn as baseCn } from "@/packages/utils/src/cn";
export function cn(...inputs: any[]) {
return baseCn(...inputs);
}
packages/components/react-shadcn/src/components/native/native-button-shadcnui.tsx
import { Button, ButtonProps } from "@/packages/components/react-shadcn/src/ui/button";
import { cn } from "@/packages/components/react-shadcn/src/lib/utils";
import { Loader2 } from "lucide-react";
import { ReactNode } from "react";
export interface NativeButtonProps extends ButtonProps {
children: ReactNode;
loading?: boolean;
glow?: boolean;
}
const NativeButton = ({
className,
variant = "default",
size = "lg",
children,
loading = false,
glow = false,
disabled,
...props
}: NativeButtonProps) => {
const buttonContent = (
<>
{loading && (
<Loader2 aria-hidden="true" className="w-4 h-4 mr-2 animate-spin" />
)}
<span
className={cn(
"flex items-center gap-2",
loading && "motion-safe:animate-[pulse_1s_ease-in-out_infinite]"
)}
>
{children}
</span>
</>
);
const glassmorphismClassName = cn(
"cursor-pointer h-12 rounded-md px-7 text-sm relative overflow-hidden",
"transition-[scale,box-shadow,background-color,border-color,color] duration-200",
!disabled &&
!loading &&
"motion-safe:hover:scale-[1.02] motion-safe:active:scale-[0.98]",
!glow && "shadow-md hover:shadow-lg",
glow && "shadow-lg shadow-primary/20 hover:shadow-primary/40",
variant === "outline" && "text-foreground/80 hover:bg-foreground/5",
(disabled || loading) && "opacity-50 cursor-not-allowed grayscale",
className
);
return (
<div className="group/native relative inline-block w-fit">
{glow && !disabled && !loading && (
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 rounded-md bg-primary/20 blur-xl opacity-0 group-hover/native:opacity-100 transition-opacity duration-500"
/>
)}
<Button
variant={variant}
size={size}
className={glassmorphismClassName}
disabled={disabled || loading}
aria-busy={loading}
{...props}
>
{buttonContent}
</Button>
</div>
);
};
NativeButton.displayName = "NativeButton";
export { NativeButton };
packages/utils/src/cn.ts
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
packages/components/react-shadcn/src/ui/button.tsx
import { cn } from "@/packages/components/react-shadcn/src/lib/utils";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = "Button";
export { Button, buttonVariants };
demo.tsx
export { NativeLiquidButtonWave as default } from "@/packages/components/react-shadcn/src/components/native/demo/native-liquid-button-demo";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-red-500: oklch(63.7% 0.237 25.331);
--color-green-500: oklch(72.3% 0.219 149.579);
--color-purple-500: oklch(62.7% 0.265 303.9);
--color-white: #fff;
--spacing: 0.25rem;
--text-xs: 0.75rem;
--text-xs--line-height: calc(1 / 0.75);
--text-sm: 0.875rem;
--text-sm--line-height: calc(1.25 / 0.875);
--text-base: 1rem;
--text-base--line-height: calc(1.5 / 1);
--text-lg: 1.125rem;
--text-lg--line-height: calc(1.75 / 1.125);
--font-weight-medium: 500;
--font-weight-semibold: 600;
--animate-spin: spin 1s linear infinite;
--blur-xl: 24px;
--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 {
.pointer-events-none {
pointer-events: none;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border-width: 0;
}
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.inset-0 {
inset: 0px;
}
.inset-y-0 {
inset-block: 0px;
}
.right-0 {
right: 0px;
}
.bottom-0 {
bottom: 0px;
}
.left-0 {
left: 0px;
}
.z-10 {
z-index: 10;
}
.mr-2 {
margin-right: calc(var(--spacing) * 2);
}
.flex {
display: flex;
}
.inline-block {
display: inline-block;
}
.inline-flex {
display: inline-flex;
}
.h-1\.5 {
height: calc(var(--spacing) * 1.5);
}
.h-4 {
height: calc(var(--spacing) * 4);
}
.h-9 {
height: calc(var(--spacing) * 9);
}
.h-10 {
height: calc(var(--spacing) * 10);
}
.h-11 {
height: calc(var(--spacing) * 11);
}
.h-12 {
height: calc(var(--spacing) * 12);
}
.h-14 {
height: calc(var(--spacing) * 14);
}
.min-h-\[200px\] {
min-height: 200px;
}
.w-1\.5 {
width: calc(var(--spacing) * 1.5);
}
.w-3 {
width: calc(var(--spacing) * 3);
}
.w-4 {
width: calc(var(--spacing) * 4);
}
.w-10 {
width: calc(var(--spacing) * 10);
}
.w-fit {
width: fit-content;
}
.min-w-\[100px\] {
min-width: 100px;
}
.min-w-\[140px\] {
min-width: 140px;
}
.min-w-\[180px\] {
min-width: 180px;
}
.animate-spin {
animation: var(--animate-spin);
}
.cursor-not-allowed {
cursor: not-allowed;
}
.cursor-pointer {
cursor: pointer;
}
.flex-wrap {
flex-wrap: wrap;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.gap-2 {
gap: calc(var(--spacing) * 2);
}
.gap-4 {
gap: calc(var(--spacing) * 4);
}
.overflow-hidden {
overflow: hidden;
}
.rounded-full {
border-radius: calc(infinity * 1px);
}
.rounded-md {
border-radius: calc(var(--radius) - 2px);
}
.border {
border-style: var(--tw-border-style);
border-width: 1px;
}
.border-input {
border-color: var(--input);
}
.bg-background {
background-color: var(--background);
}
.bg-destructive {
background-color: var(--destructive);
}
.bg-green-500 {
background-color: var(--color-green-500);
}
.bg-primary {
background-color: var(--primary);
}
.bg-primary\/20 {
background-color: var(--primary);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--primary) 20%, transparent);
}
}
.bg-purple-500 {
background-color: var(--color-purple-500);
}
.bg-red-500 {
background-color: var(--color-red-500);
}
.bg-secondary {
background-color: var(--secondary);
}
.bg-white\/15 {
background-color: color-mix(in srgb, #fff 15%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-white) 15%, transparent);
}
}
.bg-white\/40 {
background-color: color-mix(in srgb, #fff 40%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-white) 40%, transparent);
}
}
.bg-gradient-to-l {
--tw-gradient-position: to left in oklab;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-gradient-to-r {
--tw-gradient-position: to right in oklab;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.from-primary {
--tw-gradient-from: var(--primary);
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
.from-transparent {
--tw-gradient-from: transparent;
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
.from-white\/30 {
--tw-gradient-from: color-mix(in srgb, #fff 30%, transparent);
@supports (color: color-mix(in lab, red, red)) {
--tw-gradient-from: color-mix(in oklab, var(--color-white) 30%, transparent);
}
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
.via-primary\/80 {
--tw-gradient-via: var(--primary);
@supports (color: color-mix(in lab, red, red)) {
--tw-gradient-via: color-mix(in oklab, var(--primary) 80%, transparent);
}
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-via-stops);
}
.via-white\/20 {
--tw-gradient-via: color-mix(in srgb, #fff 20%, transparent);
@supports (color: color-mix(in lab, red, red)) {
--tw-gradient-via: color-mix(in oklab, var(--color-white) 20%, transparent);
}
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-via-stops);
}
.to-primary {
--tw-gradient-to: var(--primary);
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
.to-transparent {
--tw-gradient-to: transparent;
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
.p-8 {
padding: calc(var(--spacing) * 8);
}
.px-3 {
padding-inline: calc(var(--spacing) * 3);
}
.px-4 {
padding-inline: calc(var(--spacing) * 4);
}
.px-6 {
padding-inline: calc(var(--spacing) * 6);
}
.px-7 {
padding-inline: calc(var(--spacing) * 7);
}
.px-8 {
padding-inline: calc(var(--spacing) * 8);
}
.py-2 {
padding-block: calc(var(--spacing) * 2);
}
.text-base {
font-size: var(--text-base);
line-height: var(--tw-leading, var(--text-base--line-height));
}
.text-lg {
font-size: var(--text-lg);
line-height: var(--tw-leading, var(--text-lg--line-height));
}
.text-sm {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
}
.text-xs {
font-size: var(--text-xs);
line-height: var(--tw-leading, var(--text-xs--line-height));
}
.font-medium {
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
}
.font-semibold {
--tw-font-weight: var(--font-weight-semibold);
font-weight: var(--font-weight-semibold);
}
.whitespace-nowrap {
white-space: nowrap;
}
.text-destructive-foreground {
color: var(--destructive-foreground);
}
.text-foreground\/80 {
color: var(--foreground);
@supports (color: color-mix(in lab, red, red)) {
color: color-mix(in oklab, var(--foreground) 80%, transparent);
}
}
.text-primary {
color: var(--primary);
}
.text-primary-foreground {
color: var(--primary-foreground);
}
.text-secondary-foreground {
color: var(--secondary-foreground);
}
.tabular-nums {
--tw-numeric-spacing: tabular-nums;
font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
}
.underline-offset-4 {
text-underline-offset: 4px;
}
.opacity-0 {
opacity: 0%;
}
.opacity-50 {
opacity: 50%;
}
.shadow-lg {
--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.shadow-md {
--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.shadow-primary\/20 {
--tw-shadow-color: var(--primary);
@supports (color: color-mix(in lab, red, red)) {
--tw-shadow-color: color-mix(in oklab, color-mix(in oklab, var(--primary) 20%, transparent) var(--tw-shadow-alpha), transparent);
}
}
.shadow-primary\/30 {
--tw-shadow-color: var(--primary);
@supports (color: color-mix(in lab, red, red)) {
--tw-shadow-color: color-mix(in oklab, color-mix(in oklab, var(--primary) 30%, transparent) var(--tw-shadow-alpha), transparent);
}
}
.ring-offset-background {
--tw-ring-offset-color: var(--background);
}
.outline {
outline-style: var(--tw-outline-style);
outline-width: 1px;
}
.blur-xl {
--tw-blur: blur(var(--blur-xl));
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
}
.grayscale {
--tw-grayscale: grayscale(100%);
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
}
.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-\[box-shadow\,background-color\,border-color\,color\] {
transition-property: box-shadow,background-color,border-color,color;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.transition-\[scale\,box-shadow\,background-color\,border-color\,color\] {
transition-property: scale,box-shadow,background-color,border-color,color;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.transition-colors {
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
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));
}
.duration-200 {
--tw-duration: 200ms;
transition-duration: 200ms;
}
.duration-300 {
--tw-duration: 300ms;
transition-duration: 300ms;
}
.duration-500 {
--tw-duration: 500ms;
transition-duration: 500ms;
}
@media (hover: hover) {
.group-hover\/native\:opacity-100:is(:where(.group\/native):hover *) {
opacity: 100%;
}
}
.before\:pointer-events-none::before {
content: var(--tw-content);
pointer-events: none;
}
.before\:absolute::before {
content: var(--tw-content);
position: absolute;
}
.before\:inset-0::before {
content: var(--tw-content);
inset: 0px;
}
.before\:rounded-md::before {
content: var(--tw-content);
border-radius: calc(var(--radius) - 2px);
}
.before\:bg-background\/20::before {
content: var(--tw-content);
background-color: var(--background);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--background) 20%, transparent);
}
}
@media (hover: hover) {
.hover\:bg-accent:hover {
background-color: var(--accent);
}
.hover\:bg-destructive\/90:hover {
background-color: var(--destructive);
}
@supports (color: color-mix(in lab, red, red)) {
.hover\:bg-destructive\/90:hover {
background-color: color-mix(in oklab, var(--destructive) 90%, transparent);
}
}
.hover\:bg-foreground\/5:hover {
background-color: var(--foreground);
}
@supports (color: color-mix(in lab, red, red)) {
.hover\:bg-foreground\/5:hover {
background-color: color-mix(in oklab, var(--foreground) 5%, transparent);
}
}
.hover\:bg-primary\/90:hover {
background-color: var(--primary);
}
@supports (color: color-mix(in lab, red, red)) {
.hover\:bg-primary\/90:hover {
background-color: color-mix(in oklab, var(--primary) 90%, transparent);
}
}
.hover\:bg-secondary\/80:hover {
background-color: var(--secondary);
}
@supports (color: color-mix(in lab, red, red)) {
.hover\:bg-secondary\/80:hover {
background-color: color-mix(in oklab, var(--secondary) 80%, transparent);
}
}
.hover\:text-accent-foreground:hover {
color: var(--accent-foreground);
}
.hover\:underline:hover {
text-decoration-line: underline;
}
.hover\:shadow-lg:hover {
--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.hover\:shadow-primary\/40:hover {
--tw-shadow-color: var(--primary);
}
@supports (color: color-mix(in lab, red, red)) {
.hover\:shadow-primary\/40:hover {
--tw-shadow-color: color-mix(in oklab, color-mix(in oklab, var(--primary) 40%, transparent) var(--tw-shadow-alpha), transparent);
}
}
}
.focus-visible\:ring-2:focus-visible {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.focus-visible\:ring-ring:focus-visible {
--tw-ring-color: var(--ring);
}
.focus-visible\:ring-offset-2:focus-visible {
--tw-ring-offset-width: 2px;
--tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
}
.focus-visible\:outline-none:focus-visible {
--tw-outline-style: none;
outline-style: none;
}
.disabled\:pointer-events-none:disabled {
pointer-events: none;
}
.disabled\:opacity-50:disabled {
opacity: 50%;
}
@media (prefers-reduced-motion: no-preference) {
.motion-safe\:animate-\[pulse_1s_ease-in-out_infinite\] {
animation: pulse 1s ease-in-out infinite;
}
@media (hover: hover) {
.motion-safe\:hover\:scale-\[1\.02\]:hover {
scale: 1.02;
}
}
.motion-safe\:active\:scale-\[0\.98\]:active {
scale: 0.98;
}
}
}
: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;
}
@property --tw-border-style {
syntax: "*";
inherits: false;
initial-value: solid;
}
@property --tw-gradient-position {
syntax: "*";
inherits: false;
}
@property --tw-gradient-from {
syntax: "<color>";
inherits: false;
initial-value: #0000;
}
@property --tw-gradient-via {
syntax: "<color>";
inherits: false;
initial-value: #0000;
}
@property --tw-gradient-to {
syntax: "<color>";
inherits: false;
initial-value: #0000;
}
@property --tw-gradient-stops {
syntax: "*";
inherits: false;
}
@property --tw-gradient-via-stops {
syntax: "*";
inherits: false;
}
@property --tw-gradient-from-position {
syntax: "<length-percentage>";
inherits: false;
initial-value: 0%;
}
@property --tw-gradient-via-position {
syntax: "<length-percentage>";
inherits: false;
initial-value: 50%;
}
@property --tw-gradient-to-position {
syntax: "<length-percentage>";
inherits: false;
initial-value: 100%;
}
@property --tw-font-weight {
syntax: "*";
inherits: false;
}
@property --tw-ordinal {
syntax: "*";
inherits: false;
}
@property --tw-slashed-zero {
syntax: "*";
inherits: false;
}
@property --tw-numeric-figure {
syntax: "*";
inherits: false;
}
@property --tw-numeric-spacing {
syntax: "*";
inherits: false;
}
@property --tw-numeric-fraction {
syntax: "*";
inherits: false;
}
@property --tw-shadow {
syntax: "*";
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-shadow-color {
syntax: "*";
inherits: false;
}
@property --tw-shadow-alpha {
syntax: "<percentage>";
inherits: false;
initial-value: 100%;
}
@property --tw-inset-shadow {
syntax: "*";
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-inset-shadow-color {
syntax: "*";
inherits: false;
}
@property --tw-inset-shadow-alpha {
syntax: "<percentage>";
inherits: false;
initial-value: 100%;
}
@property --tw-ring-color {
syntax: "*";
inherits: false;
}
@property --tw-ring-shadow {
syntax: "*";
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-inset-ring-color {
syntax: "*";
inherits: false;
}
@property --tw-inset-ring-shadow {
syntax: "*";
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-ring-inset {
syntax: "*";
inherits: false;
}
@property --tw-ring-offset-width {
syntax: "<length>";
inherits: false;
initial-value: 0px;
}
@property --tw-ring-offset-color {
syntax: "*";
inherits: false;
initial-value: #fff;
}
@property --tw-ring-offset-shadow {
syntax: "*";
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-outline-style {
syntax: "*";
inherits: false;
initial-value: solid;
}
@property --tw-blur {
syntax: "*";
inherits: false;
}
@property --tw-brightness {
syntax: "*";
inherits: false;
}
@property --tw-contrast {
syntax: "*";
inherits: false;
}
@property --tw-grayscale {
syntax: "*";
inherits: false;
}
@property --tw-hue-rotate {
syntax: "*";
inherits: false;
}
@property --tw-invert {
syntax: "*";
inherits: false;
}
@property --tw-opacity {
syntax: "*";
inherits: false;
}
@property --tw-saturate {
syntax: "*";
inherits: false;
}
@property --tw-sepia {
syntax: "*";
inherits: false;
}
@property --tw-drop-shadow {
syntax: "*";
inherits: false;
}
@property --tw-drop-shadow-color {
syntax: "*";
inherits: false;
}
@property --tw-drop-shadow-alpha {
syntax: "<percentage>";
inherits: false;
initial-value: 100%;
}
@property --tw-drop-shadow-size {
syntax: "*";
inherits: false;
}
@property --tw-duration {
syntax: "*";
inherits: false;
}
@property --tw-content {
syntax: "*";
initial-value: "";
inherits: false;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@keyframes pulse {
50% {
opacity: 0.5;
}
}
@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-border-style: solid;
--tw-gradient-position: initial;
--tw-gradient-from: #0000;
--tw-gradient-via: #0000;
--tw-gradient-to: #0000;
--tw-gradient-stops: initial;
--tw-gradient-via-stops: initial;
--tw-gradient-from-position: 0%;
--tw-gradient-via-position: 50%;
--tw-gradient-to-position: 100%;
--tw-font-weight: initial;
--tw-ordinal: initial;
--tw-slashed-zero: initial;
--tw-numeric-figure: initial;
--tw-numeric-spacing: initial;
--tw-numeric-fraction: initial;
--tw-shadow: 0 0 #0000;
--tw-shadow-color: initial;
--tw-shadow-alpha: 100%;
--tw-inset-shadow: 0 0 #0000;
--tw-inset-shadow-color: initial;
--tw-inset-shadow-alpha: 100%;
--tw-ring-color: initial;
--tw-ring-shadow: 0 0 #0000;
--tw-inset-ring-color: initial;
--tw-inset-ring-shadow: 0 0 #0000;
--tw-ring-inset: initial;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-offset-shadow: 0 0 #0000;
--tw-outline-style: solid;
--tw-blur: initial;
--tw-brightness: initial;
--tw-contrast: initial;
--tw-grayscale: initial;
--tw-hue-rotate: initial;
--tw-invert: initial;
--tw-opacity: initial;
--tw-saturate: initial;
--tw-sepia: initial;
--tw-drop-shadow: initial;
--tw-drop-shadow-color: initial;
--tw-drop-shadow-alpha: 100%;
--tw-drop-shadow-size: initial;
--tw-duration: initial;
--tw-content: "";
}
}
}