Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/deploy-v3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ jobs:
deploy-v3:
runs-on: ubuntu-latest
environment: v3-deployment
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "20"
- name: Install yarn
run: npm install -g yarn
- name: Restore cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
.next/cache
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@ jobs:
if: github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-latest
environment: preview-deployment
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
- name: Install yarn
run: npm install -g yarn
- name: Restore cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
.next/cache
Expand Down
64 changes: 30 additions & 34 deletions animata/card/card-spread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,60 +29,56 @@ function RemodelNotes() {
}

const cards = [
{
component: Notes,
rotationClass: "",
revealClass: "-rotate-[2deg]",
},
{
component: ShoppingList,
rotationClass: "group-hover/spread:rotate-[15deg]",
revealClass: "rotate-[3deg] translate-y-2",
},

{
component: RemodelNotes,
rotationClass: "group-hover/spread:rotate-[30deg]",
revealClass: "-rotate-[2deg] translate-x-1",
},

{
component: Reminders,
rotationClass: "group-hover/spread:rotate-[45deg]",
revealClass: "rotate-[2deg]",
},
{ component: Notes },
{ component: ShoppingList },
{ component: RemodelNotes },
];

export default function CardSpread() {
const [isExpanded, setExpanded] = useState(false);

return (
<div
role="group"
aria-expanded={isExpanded}
className={cn(
Comment on lines 41 to 44
Comment on lines +42 to 44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

aria-expanded is applied to the wrong element/role.

role="group" does not support aria-expanded. Move aria-expanded to the actual interactive toggle element (the clickable card control).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@animata/card/card-spread.tsx` around lines 42 - 44, The element with
role="group" is currently receiving aria-expanded (using isExpanded) which is
invalid; remove aria-expanded from the role="group" wrapper in card-spread.tsx
and instead apply aria-expanded={isExpanded} to the actual interactive toggle
control — i.e., the clickable card control/button element that handles expansion
(the element with the onClick/toggle handler). Keep role="group" on the wrapper
for grouping semantics and ensure the interactive control is keyboard-focusable
(button or role="button") so aria-expanded is correctly exposed.

"group/spread relative flex min-h-80 min-w-52 items-center transition duration-500 ease-in-out",
"group/spread relative flex flex-row items-end justify-center gap-8 py-6 min-h-80",
{
"origin-bottom transition duration-500 ease-in-out hover:-rotate-[15deg]": !isExpanded,
"gap-3": isExpanded,
"origin-bottom hover:-rotate-[5deg]": !isExpanded,
},
)}
>
{cards.map((item, index) => {
const mid = (cards.length - 1) / 2;
const offset = (index - mid) * 120; // px to spread when expanded
const rotate = (index - mid) * 5; // small rotation per card
const delay = Math.abs(index - mid) * 140; // stronger stagger delay in ms
return (
<div
key={index}
onClick={(e) => {
setExpanded(!isExpanded);
e.preventDefault();
}}
className={cn(
"transition duration-500 ease-in-out",
{
absolute: !isExpanded,
"origin-bottom": !isExpanded,
},
!isExpanded && item.rotationClass,
isExpanded && item.revealClass,
)}
tabIndex={0}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
setExpanded(!isExpanded);
e.preventDefault();
}
}}
Comment on lines 57 to +69
Comment on lines 57 to +69
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use a semantic button for card toggles and functional state updates.

Interactive behavior on a div requires manual keyboard/a11y plumbing and is currently flagged by a11y lint. Use <button type="button"> with aria-expanded, and switch toggles to functional updates.

Suggested fix
-          <div
+          <button
+            type="button"
             key={index}
-            onClick={(e) => {
-              setExpanded(!isExpanded);
-              e.preventDefault();
-            }}
-            tabIndex={0}
-            onKeyDown={(e) => {
-              if (e.key === "Enter" || e.key === " ") {
-                setExpanded(!isExpanded);
-                e.preventDefault();
-              }
-            }}
+            aria-expanded={isExpanded}
+            onClick={() => setExpanded((prev) => !prev)}
             className={cn("relative min-w-52 cursor-pointer", {
               "origin-bottom": !isExpanded,
             })}
             style={{
               transform: isExpanded
                 ? `translateX(${offset}px) translateY(-22px) rotate(${rotate}deg)`
                 : `rotate(${rotate / 2}deg)`,
               boxShadow: isExpanded
                 ? "0 22px 56px rgba(2,6,23,0.16)"
                 : "0 8px 22px rgba(2,6,23,0.07)",
               transition: `transform 520ms cubic-bezier(0.22,1,0.36,1) ${delay}ms, box-shadow 520ms ease ${delay}ms`,
             }}
           >
             <item.component />
-          </div>
+          </button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div
key={index}
onClick={(e) => {
setExpanded(!isExpanded);
e.preventDefault();
}}
className={cn(
"transition duration-500 ease-in-out",
{
absolute: !isExpanded,
"origin-bottom": !isExpanded,
},
!isExpanded && item.rotationClass,
isExpanded && item.revealClass,
)}
tabIndex={0}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
setExpanded(!isExpanded);
e.preventDefault();
}
}}
<button
type="button"
key={index}
aria-expanded={isExpanded}
onClick={() => setExpanded((prev) => !prev)}
className={cn("relative min-w-52 cursor-pointer", {
"origin-bottom": !isExpanded,
})}
style={{
transform: isExpanded
? `translateX(${offset}px) translateY(-22px) rotate(${rotate}deg)`
: `rotate(${rotate / 2}deg)`,
boxShadow: isExpanded
? "0 22px 56px rgba(2,6,23,0.16)"
: "0 8px 22px rgba(2,6,23,0.07)",
transition: `transform 520ms cubic-bezier(0.22,1,0.36,1) ${delay}ms, box-shadow 520ms ease ${delay}ms`,
}}
>
<item.component />
</button>
🧰 Tools
🪛 Biome (2.4.15)

[error] 63-63: The HTML element div is non-interactive. Do not use tabIndex.

(lint/a11y/noNoninteractiveTabindex)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@animata/card/card-spread.tsx` around lines 57 - 69, Replace the interactive
div with a semantic <button type="button"> element (instead of the current div
keyed by index) and add aria-expanded={isExpanded} to it; remove manual
tabIndex/onKeyDown keyboard handling and leave click handling to the button, and
change the onClick handler to use a functional state update like
setExpanded(prev => !prev) (referencing setExpanded and isExpanded in the
component) so toggling is safe and accessible.

className={cn("relative min-w-52 cursor-pointer", {
"origin-bottom": !isExpanded,
})}
style={{
transform: isExpanded
? `translateX(${offset}px) translateY(-22px) rotate(${rotate}deg)`
: `rotate(${rotate / 2}deg)`,
boxShadow: isExpanded
? "0 22px 56px rgba(2,6,23,0.16)"
: "0 8px 22px rgba(2,6,23,0.07)",
transition: `transform 520ms cubic-bezier(0.22,1,0.36,1) ${delay}ms, box-shadow 520ms ease ${delay}ms`,
}}
>
<item.component />
</div>
Expand Down
33 changes: 33 additions & 0 deletions animata/card/state-action-card.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Meta, StoryObj } from "@storybook/react";

import StateActionCard from "@/animata/card/state-action-card";

const meta = {
title: "Card/State Action Card",
component: StateActionCard,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
} satisfies Meta<typeof StateActionCard>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Taskmanager: Story = {
args: {
useCase: "task",
},
};

export const Socialcard: Story = {
args: {
useCase: "social",
},
};

export const Ordercard: Story = {
args: {
useCase: "order",
},
};
Loading
Loading