"use client";
import * as React from "react";
import confetti from "canvas-confetti";
import { cn } from "@/lib/utils";
export interface ConfettiButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
/** Label used when no children are provided. */
label?: React.ReactNode;
/** Confetti particles per burst. */
particleCount?: number;
/** Confetti spread in degrees. */
spread?: number;
/** Launch velocity of the burst. */
startVelocity?: number;
/** Custom confetti colors. */
colors?: string[];
/** Show the leading sparkle icon. */
icon?: boolean;
/** Fired after each confetti burst. */
onCelebrate?: () => void;
}
/** Soft neutrals with a few cool accents, so the burst reads as polish, not a party. */
const CONFETTI_COLORS = ["#fafafa", "#d4d4d4", "#a3a3a3", "#93c5fd", "#c4b5fd", "#fcd34d"];
/** How long the sparkle stays lit after a burst. */
const FIRE_MS = 420;
function burstFromElement(
element: HTMLElement,
options: { particleCount: number; spread: number; startVelocity: number; colors: string[] },
) {
const rect = element.getBoundingClientRect();
const origin = {
x: (rect.left + rect.width / 2) / window.innerWidth,
y: (rect.top + rect.height / 3) / window.innerHeight,
};
// One tight burst of small flat pieces that fall and fade quickly.
void confetti({
...options,
origin,
angle: 90,
gravity: 1.1,
decay: 0.9,
ticks: 160,
scalar: 0.75,
shapes: ["square", "circle"],
disableForReducedMotion: true,
});
}
export const ConfettiButton = React.forwardRef<HTMLButtonElement, ConfettiButtonProps>(
(
{
children,
label = "Continue",
particleCount = 80,
spread = 64,
startVelocity = 32,
colors = CONFETTI_COLORS,
icon = true,
onCelebrate,
onClick,
className,
disabled,
type = "button",
...props
},
ref,
) => {
const [firing, setFiring] = React.useState(false);
const fireTimerRef = React.useRef<number | undefined>(undefined);
React.useEffect(() => () => window.clearTimeout(fireTimerRef.current), []);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(event);
if (disabled || event.defaultPrevented) return;
burstFromElement(event.currentTarget, { particleCount, spread, startVelocity, colors });
setFiring(true);
window.clearTimeout(fireTimerRef.current);
fireTimerRef.current = window.setTimeout(() => setFiring(false), FIRE_MS);
onCelebrate?.();
};
return (
<button
ref={ref}
type={type}
disabled={disabled}
data-firing={firing || undefined}
onClick={handleClick}
className={cn(
"group/confetti inline-flex h-9 cursor-pointer items-center justify-center gap-2 rounded-[10px] px-4 text-sm font-medium text-neutral-50 outline-none select-none",
// Graded dark surface: dark outer hairline, faint inner ring, top highlight, soft drop.
"bg-linear-to-b from-[#353535] to-[#272727] shadow-[0_0_0_1px_rgb(0_0_0/0.9),inset_0_0_0_1px_rgb(255_255_255/0.06),inset_0_1px_0_rgb(255_255_255/0.14),0_1px_2px_rgb(0_0_0/0.25),0_4px_12px_-4px_rgb(0_0_0/0.4)]",
"transition-[scale,filter,box-shadow] duration-300 ease-[cubic-bezier(0.34,1.35,0.64,1)] hover:brightness-110",
// Press eases in fast and settles the shadow; release springs back on the slower base curve.
"active:scale-[0.97] active:brightness-95 active:duration-100 active:ease-out active:shadow-[0_0_0_1px_rgb(0_0_0/0.9),inset_0_0_0_1px_rgb(255_255_255/0.05),inset_0_1px_0_rgb(255_255_255/0.08),0_0_1px_rgb(0_0_0/0.2),0_1px_3px_-2px_rgb(0_0_0/0.3)] motion-reduce:active:scale-100",
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50",
icon && "pl-3",
className,
)}
{...props}
>
{icon ? (
<svg
aria-hidden
viewBox="0 0 16 16"
fill="currentColor"
className={cn(
"size-3.5 text-neutral-50/45 transition-[rotate,scale,color] duration-300 ease-[cubic-bezier(0.34,1.35,0.64,1)]",
"group-hover/confetti:rotate-12 group-hover/confetti:text-neutral-50/75",
"group-data-firing/confetti:scale-125 group-data-firing/confetti:rotate-45 group-data-firing/confetti:text-amber-200 motion-reduce:transform-none",
)}
>
<path d="M8 1.5c.3 2.9 1.6 4.2 4.5 4.5-2.9.3-4.2 1.6-4.5 4.5-.3-2.9-1.6-4.2-4.5-4.5 2.9-.3 4.2-1.6 4.5-4.5Z" />
<path d="M12.5 10c.15 1.3.7 1.85 2 2-1.3.15-1.85.7-2 2-.15-1.3-.7-1.85-2-2 1.3-.15 1.85-.7 2-2Z" />
</svg>
) : null}
<span>{children ?? label}</span>
</button>
);
},
);
ConfettiButton.displayName = "ConfettiButton";
export default ConfettiButton;
ConfettiButton
By jay sharma
Try the interaction, copy the React source files, or download the complete example.
About ConfettiButton
ConfettiButton is an animated React and Tailwind CSS button by jay sharma. 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/confetti-button.tsx
import { ConfettiButton } from "@/components/evil-buttons/confetti-button";
export function ButtonDemo() {
return (
<ConfettiButton
colors={["#fafafa", "#a3a3a3", "#93c5fd"]}
onCelebrate={() => console.log("Celebrated!")}
>
Celebrate
</ConfettiButton>
);
}
components/evil-buttons/confetti-button.tsx
"use client";
import * as React from "react";
import confetti from "canvas-confetti";
import { cn } from "@/lib/utils";
export interface ConfettiButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
/** Label used when no children are provided. */
label?: React.ReactNode;
/** Confetti particles per burst. */
particleCount?: number;
/** Confetti spread in degrees. */
spread?: number;
/** Launch velocity of the burst. */
startVelocity?: number;
/** Custom confetti colors. */
colors?: string[];
/** Show the leading sparkle icon. */
icon?: boolean;
/** Fired after each confetti burst. */
onCelebrate?: () => void;
}
/** Soft neutrals with a few cool accents, so the burst reads as polish, not a party. */
const CONFETTI_COLORS = ["#fafafa", "#d4d4d4", "#a3a3a3", "#93c5fd", "#c4b5fd", "#fcd34d"];
/** How long the sparkle stays lit after a burst. */
const FIRE_MS = 420;
function burstFromElement(
element: HTMLElement,
options: { particleCount: number; spread: number; startVelocity: number; colors: string[] },
) {
const rect = element.getBoundingClientRect();
const origin = {
x: (rect.left + rect.width / 2) / window.innerWidth,
y: (rect.top + rect.height / 3) / window.innerHeight,
};
// One tight burst of small flat pieces that fall and fade quickly.
void confetti({
...options,
origin,
angle: 90,
gravity: 1.1,
decay: 0.9,
ticks: 160,
scalar: 0.75,
shapes: ["square", "circle"],
disableForReducedMotion: true,
});
}
export const ConfettiButton = React.forwardRef<HTMLButtonElement, ConfettiButtonProps>(
(
{
children,
label = "Continue",
particleCount = 80,
spread = 64,
startVelocity = 32,
colors = CONFETTI_COLORS,
icon = true,
onCelebrate,
onClick,
className,
disabled,
type = "button",
...props
},
ref,
) => {
const [firing, setFiring] = React.useState(false);
const fireTimerRef = React.useRef<number | undefined>(undefined);
React.useEffect(() => () => window.clearTimeout(fireTimerRef.current), []);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(event);
if (disabled || event.defaultPrevented) return;
burstFromElement(event.currentTarget, { particleCount, spread, startVelocity, colors });
setFiring(true);
window.clearTimeout(fireTimerRef.current);
fireTimerRef.current = window.setTimeout(() => setFiring(false), FIRE_MS);
onCelebrate?.();
};
return (
<button
ref={ref}
type={type}
disabled={disabled}
data-firing={firing || undefined}
onClick={handleClick}
className={cn(
"group/confetti inline-flex h-9 cursor-pointer items-center justify-center gap-2 rounded-[10px] px-4 text-sm font-medium text-neutral-50 outline-none select-none",
// Graded dark surface: dark outer hairline, faint inner ring, top highlight, soft drop.
"bg-linear-to-b from-[#353535] to-[#272727] shadow-[0_0_0_1px_rgb(0_0_0/0.9),inset_0_0_0_1px_rgb(255_255_255/0.06),inset_0_1px_0_rgb(255_255_255/0.14),0_1px_2px_rgb(0_0_0/0.25),0_4px_12px_-4px_rgb(0_0_0/0.4)]",
"transition-[scale,filter,box-shadow] duration-300 ease-[cubic-bezier(0.34,1.35,0.64,1)] hover:brightness-110",
// Press eases in fast and settles the shadow; release springs back on the slower base curve.
"active:scale-[0.97] active:brightness-95 active:duration-100 active:ease-out active:shadow-[0_0_0_1px_rgb(0_0_0/0.9),inset_0_0_0_1px_rgb(255_255_255/0.05),inset_0_1px_0_rgb(255_255_255/0.08),0_0_1px_rgb(0_0_0/0.2),0_1px_3px_-2px_rgb(0_0_0/0.3)] motion-reduce:active:scale-100",
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50",
icon && "pl-3",
className,
)}
{...props}
>
{icon ? (
<svg
aria-hidden
viewBox="0 0 16 16"
fill="currentColor"
className={cn(
"size-3.5 text-neutral-50/45 transition-[rotate,scale,color] duration-300 ease-[cubic-bezier(0.34,1.35,0.64,1)]",
"group-hover/confetti:rotate-12 group-hover/confetti:text-neutral-50/75",
"group-data-firing/confetti:scale-125 group-data-firing/confetti:rotate-45 group-data-firing/confetti:text-amber-200 motion-reduce:transform-none",
)}
>
<path d="M8 1.5c.3 2.9 1.6 4.2 4.5 4.5-2.9.3-4.2 1.6-4.5 4.5-.3-2.9-1.6-4.2-4.5-4.5 2.9-.3 4.2-1.6 4.5-4.5Z" />
<path d="M12.5 10c.15 1.3.7 1.85 2 2-1.3.15-1.85.7-2 2-.15-1.3-.7-1.85-2-2 1.3-.15 1.85-.7 2-2Z" />
</svg>
) : null}
<span>{children ?? label}</span>
</button>
);
},
);
ConfettiButton.displayName = "ConfettiButton";
export default ConfettiButton;
lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
demo.tsx
export { ButtonDemo as default } from "@/import-examples/confetti-button";CSS
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties;
@layer theme, base, components, utilities;
@layer theme {
:root, :host {
--color-amber-200: oklch(92.4% 0.12 95.746);
--color-neutral-50: oklch(98.5% 0 none);
--spacing: 0.25rem;
--text-sm: 0.875rem;
--text-sm--line-height: calc(1.25 / 0.875);
--font-weight-medium: 500;
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--default-transition-duration: 150ms;
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
--default-font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
--default-mono-font-family: var(--font-geist-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 {
.inline-flex {
display: inline-flex;
}
.size-3\.5 {
width: calc(var(--spacing) * 3.5);
height: calc(var(--spacing) * 3.5);
}
.h-9 {
height: calc(var(--spacing) * 9);
}
.cursor-pointer {
cursor: pointer;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.gap-2 {
gap: calc(var(--spacing) * 2);
}
.rounded-\[10px\] {
border-radius: 10px;
}
.bg-linear-to-b {
--tw-gradient-position: to bottom;
@supports (background-image: linear-gradient(in lab, red, red)) {
--tw-gradient-position: to bottom in oklab;
}
background-image: linear-gradient(var(--tw-gradient-stops));
}
.from-\[\#353535\] {
--tw-gradient-from: #353535;
--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-\[\#272727\] {
--tw-gradient-to: #272727;
--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));
}
.px-4 {
padding-inline: calc(var(--spacing) * 4);
}
.pl-3 {
padding-left: calc(var(--spacing) * 3);
}
.text-sm {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
}
.font-medium {
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
}
.text-neutral-50 {
color: var(--color-neutral-50);
}
.text-neutral-50\/45 {
color: color-mix(in srgb, oklch(98.5% 0 none) 45%, transparent);
@supports (color: color-mix(in lab, red, red)) {
color: color-mix(in oklab, var(--color-neutral-50) 45%, transparent);
}
}
.shadow-\[0_0_0_1px_rgb\(0_0_0\/0\.9\)\,inset_0_0_0_1px_rgb\(255_255_255\/0\.06\)\,inset_0_1px_0_rgb\(255_255_255\/0\.14\)\,0_1px_2px_rgb\(0_0_0\/0\.25\)\,0_4px_12px_-4px_rgb\(0_0_0\/0\.4\)\] {
--tw-shadow: 0 0 0 1px var(--tw-shadow-color, rgb(0 0 0/0.9)), inset 0 0 0 1px var(--tw-shadow-color, rgb(255 255 255/0.06)), inset 0 1px 0 var(--tw-shadow-color, rgb(255 255 255/0.14)), 0 1px 2px var(--tw-shadow-color, rgb(0 0 0/0.25)), 0 4px 12px -4px var(--tw-shadow-color, rgb(0 0 0/0.4));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.transition-\[rotate\,scale\,color\] {
transition-property: rotate,scale,color;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.transition-\[scale\,filter\,box-shadow\] {
transition-property: scale,filter,box-shadow;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.duration-300 {
--tw-duration: 300ms;
transition-duration: 300ms;
}
.ease-\[cubic-bezier\(0\.34\,1\.35\,0\.64\,1\)\] {
--tw-ease: cubic-bezier(0.34,1.35,0.64,1);
transition-timing-function: cubic-bezier(0.34,1.35,0.64,1);
}
.outline-none {
--tw-outline-style: none;
outline-style: none;
}
.select-none {
-webkit-user-select: none;
user-select: none;
}
@media (hover: hover) {
.group-hover\/confetti\:rotate-12:is(:where(.group\/confetti):hover *) {
rotate: 12deg;
}
.group-hover\/confetti\:text-neutral-50\/75:is(:where(.group\/confetti):hover *) {
color: color-mix(in srgb, oklch(98.5% 0 none) 75%, transparent);
}
@supports (color: color-mix(in lab, red, red)) {
.group-hover\/confetti\:text-neutral-50\/75:is(:where(.group\/confetti):hover *) {
color: color-mix(in oklab, var(--color-neutral-50) 75%, transparent);
}
}
}
.group-data-firing\/confetti\:scale-125:is(:where(.group\/confetti)[data-firing] *) {
--tw-scale-x: 125%;
--tw-scale-y: 125%;
--tw-scale-z: 125%;
scale: var(--tw-scale-x) var(--tw-scale-y);
}
.group-data-firing\/confetti\:rotate-45:is(:where(.group\/confetti)[data-firing] *) {
rotate: 45deg;
}
.group-data-firing\/confetti\:text-amber-200:is(:where(.group\/confetti)[data-firing] *) {
color: var(--color-amber-200);
}
@media (hover: hover) {
.hover\:brightness-110:hover {
--tw-brightness: brightness(110%);
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,);
}
}
.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\:ring-offset-background:focus-visible {
--tw-ring-offset-color: var(--background);
}
.active\:scale-\[0\.97\]:active {
scale: 0.97;
}
.active\:shadow-\[0_0_0_1px_rgb\(0_0_0\/0\.9\)\,inset_0_0_0_1px_rgb\(255_255_255\/0\.05\)\,inset_0_1px_0_rgb\(255_255_255\/0\.08\)\,0_0_1px_rgb\(0_0_0\/0\.2\)\,0_1px_3px_-2px_rgb\(0_0_0\/0\.3\)\]:active {
--tw-shadow: 0 0 0 1px var(--tw-shadow-color, rgb(0 0 0/0.9)), inset 0 0 0 1px var(--tw-shadow-color, rgb(255 255 255/0.05)), inset 0 1px 0 var(--tw-shadow-color, rgb(255 255 255/0.08)), 0 0 1px var(--tw-shadow-color, rgb(0 0 0/0.2)), 0 1px 3px -2px var(--tw-shadow-color, rgb(0 0 0/0.3));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.active\:brightness-95:active {
--tw-brightness: brightness(95%);
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,);
}
.active\:duration-100:active {
--tw-duration: 100ms;
transition-duration: 100ms;
}
.active\:ease-out:active {
--tw-ease: var(--ease-out);
transition-timing-function: var(--ease-out);
}
.disabled\:cursor-not-allowed:disabled {
cursor: not-allowed;
}
.disabled\:opacity-50:disabled {
opacity: 50%;
}
@media (prefers-reduced-motion: reduce) {
.motion-reduce\:transform-none {
transform: 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);
}
}
}
: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;
}
:root {
--card: oklch(1 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--border: oklch(0.922 0 0);
--primary: oklch(0.205 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--chrome: oklch(0.967 0 0);
--brand: oklch(0.6 0.23 29);
--chart-background: oklch(1 0 0);
--chart-foreground: oklch(0.145 0.004 285);
--chart-foreground-muted: oklch(0.55 0.014 260);
--chart-line-primary: var(--chart-1);
--chart-line-secondary: var(--chart-2);
--chart-crosshair: oklch(0.4 0.1828 274.34);
--chart-grid: oklch(0.9 0 0);
--chart-brush-border: var(--chart-grid);
--chart-tooltip-background: oklch(0.21 0.006 285 / 0.8);
--chart-tooltip-foreground: oklch(0.985 0 0);
--chart-tooltip-muted: oklch(0.65 0.01 260);
--chart-marker-background: oklch(0.97 0.005 260);
--chart-marker-border: oklch(0.85 0.01 260);
--chart-marker-foreground: oklch(0.3 0.01 260);
--chart-label: oklch(0.45 0.01 260);
--chart-scale-01: oklch(0.98 0.003 106);
--chart-scale-02: oklch(0.92 0.008 106);
--chart-scale-03: oklch(0.82 0.015 106);
--chart-scale-04: oklch(0.68 0.02 106);
--chart-scale-05: oklch(0.55 0.025 106);
--chart-scale-pattern-color: oklch(0.96 0.005 106);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--chrome: oklch(0.18 0 0);
--card: oklch(0.205 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--border: oklch(1 0 0 / 10%);
--primary: oklch(0.922 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.922 0 0);
--sidebar-primary-foreground: oklch(0.205 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
--brand: oklch(0.66 0.23 32);
--chart-background: oklch(0.145 0 0);
--chart-foreground: oklch(0.45 0 0);
--chart-foreground-muted: oklch(0.65 0.01 260);
--chart-crosshair: oklch(0.45 0 0);
--chart-grid: oklch(0.25 0 0);
--chart-brush-border: var(--chart-grid);
--chart-marker-background: oklch(0.25 0.01 260);
--chart-marker-border: oklch(0.4 0.01 260);
--chart-marker-foreground: oklch(0.9 0 0);
--chart-label: oklch(0.75 0.01 260);
--chart-scale-01: oklch(0.28 0.03 270);
--chart-scale-02: oklch(0.38 0.04 270);
--chart-scale-03: oklch(0.48 0.05 270);
--chart-scale-04: oklch(0.58 0.06 270);
--chart-scale-05: oklch(0.68 0.07 270);
--chart-scale-pattern-color: oklch(0.145 0 0);
}
@keyframes docs-dot-swing {
0%, 100% {
transform: translateX(0) scale(1);
}
45% {
transform: translateX(-6px) scale(0.7);
}
}
@keyframes hell-flicker {
0%, 100% {
opacity: 1;
}
41% {
opacity: 0.7;
}
43% {
opacity: 1;
}
77% {
opacity: 0.85;
}
78% {
opacity: 1;
}
}
@keyframes lights-off {
0% {
opacity: 0;
}
15% {
opacity: 1;
}
30% {
opacity: 0.2;
}
45% {
opacity: 1;
}
60% {
opacity: 0.5;
}
70%, 100% {
opacity: 1;
}
}
@keyframes lights-on {
0% {
opacity: 1;
}
20% {
opacity: 0;
}
32% {
opacity: 1;
}
50% {
opacity: 0.1;
}
60% {
opacity: 0.9;
}
75%, 100% {
opacity: 0;
}
}
@keyframes lights-fade {
to {
opacity: 0;
}
}
@keyframes eyes-dim {
from {
opacity: 0;
}
}
@keyframes eyes-open {
from {
opacity: 0;
transform: scaleY(0.05);
}
}
@keyframes eyes-blink {
0%, 92%, 100% {
transform: none;
}
95% {
transform: scaleY(0.08);
}
}
@keyframes crack-life {
0% {
opacity: 0;
transform: scale(0.92);
}
4% {
opacity: 1;
transform: none;
}
80% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes egg-wobble {
0%, 84%, 100% {
transform: none;
}
87% {
transform: rotate(-14deg);
}
90% {
transform: rotate(10deg);
}
93% {
transform: rotate(-6deg);
}
96% {
transform: rotate(3deg);
}
}
@keyframes page-glitch {
0% {
transform: none;
filter: none;
clip-path: none;
}
12% {
transform: translate(-6px, 2px) skewX(-2deg);
filter: hue-rotate(90deg) saturate(3);
}
24% {
transform: translate(5px, -1px);
filter: invert(1) hue-rotate(180deg);
clip-path: inset(18% 0 52% 0);
}
36% {
transform: none;
filter: contrast(2.2) saturate(0);
clip-path: none;
}
48% {
transform: translate(9px, 0) skewX(4deg);
filter: hue-rotate(-60deg);
clip-path: inset(0 0 70% 0);
}
60% {
transform: translate(-3px, 3px);
filter: none;
clip-path: inset(40% 0 20% 0);
}
72% {
transform: translate(2px, -4px) scaleY(1.02);
filter: saturate(4) contrast(1.4);
clip-path: none;
}
86% {
transform: translate(-8px, 0);
filter: invert(1);
clip-path: inset(62% 0 8% 0);
}
}
@keyframes glitch-label {
0% {
transform: translate(0, 0);
opacity: 1;
}
33% {
transform: translate(-4px, 2px);
opacity: 0.6;
}
66% {
transform: translate(5px, -2px);
opacity: 1;
}
}
@keyframes deleted-progress {
from {
width: 0;
}
to {
width: 100%;
}
}
@keyframes deleted-restore {
0%, 70% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes creeper-swell {
0% {
transform: scale(0.3) translateY(40px);
opacity: 0;
filter: none;
}
20% {
transform: scale(1);
opacity: 1;
}
45%, 65%, 85% {
filter: brightness(3) saturate(0);
}
55%, 75% {
filter: none;
}
100% {
transform: scale(1.35);
filter: brightness(4) saturate(0);
}
}
@keyframes creeper-flash {
from {
opacity: 0.85;
}
to {
opacity: 0;
}
}
@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-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-duration {
syntax: "*";
inherits: false;
}
@property --tw-ease {
syntax: "*";
inherits: false;
}
@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;
}
@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;
}
@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-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-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-duration: initial;
--tw-ease: initial;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-scale-z: 1;
--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;
}
}
}