Skip to content

Commit 9e400e1

Browse files
bob-obringerclaude
andcommitted
feat(resizable): add duration prop and CSS variable for animation customization
- Add `duration` prop (default 400ms) to control animation speed - Add `--resizable-easing` CSS variable for theme-level easing control - Document Tailwind arbitrary property syntax for inline easing - Bump version to 1.2.1 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 542e7c6 commit 9e400e1

5 files changed

Lines changed: 53 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to `@neynar/ui` will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.2.1] - 2025-01-20
9+
10+
### Added
11+
- **ResizablePanel**: Added `duration` prop (default 400ms) and `--resizable-easing` CSS variable for animation customization.
12+
13+
---
14+
815
## [1.2.0] - 2025-01-20
916

1017
### Added

llm/components/resizable.llm.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ All panels inherit props from react-resizable-panels Panel component.
7272
| collapsedSize | number \| string | 0 | Size when collapsed |
7373
| collapsed | boolean | - | Controlled collapsed state. Panel syncs to this value. |
7474
| animated | boolean | false | Enable smooth CSS transition for collapse/expand. Drag resizing stays instant. |
75+
| duration | number | 400 | Animation duration in milliseconds. Only applies when `animated` is true. |
7576
| onResize | (size: { asPercentage: number; inPixels: number }) => void | - | Called when panel is resized |
7677
| onCollapse | () => void | - | Called when panel collapses |
7778
| onExpand | () => void | - | Called when panel expands |
@@ -230,6 +231,22 @@ function MonitoredPanels() {
230231

231232
Use `collapsed` prop for declarative control and `animated` for smooth transitions. Drag resizing remains instant (no animation lag).
232233

234+
**Customization:**
235+
- `duration` prop controls speed (default 400ms)
236+
- `--resizable-easing` CSS variable controls easing curve (default `cubic-bezier(0.16, 1, 0.3, 1)`)
237+
238+
```tsx
239+
// Custom duration
240+
<ResizablePanel animated duration={200} collapsed={isCollapsed} />
241+
242+
// Custom easing via Tailwind arbitrary property
243+
<ResizablePanel
244+
className="[--resizable-easing:ease-out]"
245+
animated
246+
collapsed={isCollapsed}
247+
/>
248+
```
249+
233250
```tsx
234251
import { useState } from "react"
235252
import {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@neynar/ui",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"license": "MIT",
55
"author": "Neynar Inc.",
66
"description": "AI-first React component library for coding agents. LLM-optimized docs, sensible defaults, zero config. Built on shadcn patterns, Base UI, and Tailwind CSS v4.",

src/components/ui/resizable.tsx

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ import {
1010

1111
import { cn } from "@/lib/utils";
1212

13-
/** Duration of the collapse/expand animation in milliseconds. */
14-
const TRANSITION_DURATION = 300;
13+
/** Default duration of the collapse/expand animation in milliseconds. */
14+
const DEFAULT_DURATION = 400;
1515

16-
/** CSS transition value for animated collapse/expand. */
17-
const TRANSITION_STYLE =
18-
"flex-grow 0.3s cubic-bezier(0.16, 1, 0.3, 1), flex-basis 0.3s cubic-bezier(0.16, 1, 0.3, 1)";
16+
/** Default easing function for collapse/expand animations. */
17+
const DEFAULT_EASING = "cubic-bezier(0.16, 1, 0.3, 1)";
1918

19+
/** Generate CSS transition value for animated collapse/expand. */
20+
function getTransitionStyle(durationMs: number, easing: string): string {
21+
const seconds = durationMs / 1000;
22+
return `flex-grow ${seconds}s ${easing}, flex-basis ${seconds}s ${easing}`;
23+
}
2024

2125
type ResizablePanelGroupProps = React.ComponentProps<typeof PanelGroup>;
2226

@@ -48,6 +52,8 @@ type ResizablePanelProps = Omit<
4852
animated?: boolean;
4953
/** Controlled collapsed state. When provided, the panel syncs to this value. */
5054
collapsed?: boolean;
55+
/** Animation duration in milliseconds. Only applies when `animated` is true. @default 400 */
56+
duration?: number;
5157
};
5258

5359
/**
@@ -64,17 +70,20 @@ function ResizablePanel({
6470
className,
6571
animated,
6672
collapsed,
73+
duration = DEFAULT_DURATION,
6774
...props
6875
}: ResizablePanelProps) {
6976
const panelRef = React.useRef<PanelImperativeHandle>(null);
7077
const elementRef = React.useRef<HTMLDivElement>(null);
7178
const animatedRef = React.useRef(animated);
79+
const durationRef = React.useRef(duration);
7280
const isFirstRender = React.useRef(true);
7381

74-
// Keep the ref in sync with prop changes
82+
// Keep the refs in sync with prop changes
7583
React.useEffect(() => {
7684
animatedRef.current = animated;
77-
}, [animated]);
85+
durationRef.current = duration;
86+
}, [animated, duration]);
7887

7988
/** Apply transition, call action, then remove transition after duration */
8089
const withTransition = React.useCallback(
@@ -84,11 +93,17 @@ function ResizablePanel({
8493
) as HTMLElement | null;
8594

8695
if (animatedRef.current && panelEl && !skipAnimation) {
87-
panelEl.style.transition = TRANSITION_STYLE;
96+
const ms = durationRef.current;
97+
// Read easing from CSS variable, fallback to default
98+
const easing =
99+
getComputedStyle(panelEl)
100+
.getPropertyValue("--resizable-easing")
101+
.trim() || DEFAULT_EASING;
102+
panelEl.style.transition = getTransitionStyle(ms, easing);
88103
action();
89104
setTimeout(() => {
90105
panelEl.style.transition = "";
91-
}, TRANSITION_DURATION);
106+
}, ms);
92107
} else {
93108
action();
94109
}

src/styles/styles.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@
7373
body {
7474
@apply font-sans bg-background text-foreground;
7575
}
76+
:root {
77+
/* Resizable panel animation easing - override in theme or component */
78+
--resizable-easing: cubic-bezier(0.16, 1, 0.3, 1);
79+
}
7680
}
7781

7882
/* Surface blur effect - value comes from theme's --surface-blur variable */

0 commit comments

Comments
 (0)