feat(site): add bouncy pop animation to inbox notification badge (#26057)

> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

Replaces the basic `animate-in fade-in zoom-in duration-200` on the
inbox unread badge with a bouncier pop using an overshoot easing.

The badge now pops in from `scale(0)` with `cubic-bezier(0.34, 1.36,
0.64, 1)` over 500ms, giving it an elastic overshoot that makes it feel
alive. Uses `tailwindcss-animate` utilities (`animate-in zoom-in-0
fade-in-0`) with just one arbitrary property for the easing. Conditional
rendering is preserved as before.

Also adds a `BadgeTransition` story with toggle buttons for easy visual
verification.

<img width="150" height="150" alt="preview-notification-animation"
src="https://github.com/user-attachments/assets/70331cd2-c2e2-4fd7-8f9e-edfe876e2bab"
/>
This commit is contained in:
Jake Howell
2026-06-23 04:58:59 +00:00
committed by GitHub
parent 27ecd17991
commit e88f2a3485
2 changed files with 34 additions and 3 deletions
@@ -1,4 +1,5 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import { useState } from "react";
import { InboxButton } from "./InboxButton";
const meta: Meta<typeof InboxButton> = {
@@ -16,3 +17,32 @@ export const Unread: Story = {
unreadCount: 3,
},
};
export const BadgeTransition: Story = {
render: function BadgeTransitionStory() {
const [count, setCount] = useState(0);
return (
<div className="flex items-center gap-4">
<InboxButton unreadCount={count} />
<div className="flex gap-2">
<button
type="button"
className="rounded border border-solid border-border px-3 py-1 text-sm bg-surface-primary text-content-primary"
onClick={() => setCount((c) => c + 1)}
>
Add notification
</button>
<button
type="button"
className="rounded border border-solid border-border px-3 py-1 text-sm bg-surface-primary text-content-primary"
onClick={() => setCount(0)}
>
Clear all
</button>
</div>
<span className="text-sm text-content-secondary">Count: {count}</span>
</div>
);
},
};
@@ -17,11 +17,12 @@ export const InboxButton: React.FC<InboxButtonProps> = ({
{unreadCount > 0 && (
<UnreadBadge
count={unreadCount}
className={cn([
className={cn(
"[--offset:calc(var(--unread-badge-size)/2)]",
"absolute top-0 right-0 -mr-[--offset] -mt-[--offset]",
"animate-in fade-in zoom-in duration-200",
])}
"origin-center animate-in zoom-in-0 fade-in-0 duration-500",
"[animation-timing-function:cubic-bezier(0.34,1.36,0.64,1)]",
)}
/>
)}
</Button>